| /* Program to dump out a Java(TM) .class file. |
| Functionally similar to Sun's javap. |
| |
| Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 |
| Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING. If not, write to |
| the Free Software Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. |
| |
| Java and all Java-based marks are trademarks or registered trademarks |
| of Sun Microsystems, Inc. in the United States and other countries. |
| The Free Software Foundation is independent of Sun Microsystems, Inc. */ |
| |
| /* Written by Per Bothner <bothner@cygnus.com>, February 1996. */ |
| |
| /* |
| jcf-dump is a program to print out the contents of class files. |
| Usage: jcf-dump [FLAGS] CLASS |
| Each CLASS is either: |
| + the name of a class in the CLASSPATH (e.g "java.lang.String"), or |
| + the name of a class *file* (e.g. "/home/me/work/package/Foo.class"). |
| + The name of a .zip or .jar file (which prints all the classes in the |
| archive). |
| |
| OPTIONS: |
| -c |
| Dis-assemble each method. |
| -classpath PATH |
| Overrides $CLASSPATH. |
| --print-main |
| Print nothing if there is no valid "main" method; |
| otherwise, print only the class name. |
| --javap |
| Print output in the style of Sun's javap program. VERY UNFINISHED. |
| */ |
| |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "ggc.h" |
| #include "intl.h" |
| |
| #include "jcf.h" |
| #include "tree.h" |
| #include "java-tree.h" |
| |
| #include "version.h" |
| |
| #include <getopt.h> |
| #include <math.h> |
| |
| /* Outout file. */ |
| FILE *out; |
| /* Name of output file, if NULL if stdout. */ |
| char *output_file = NULL; |
| |
| int verbose = 0; |
| |
| int flag_disassemble_methods = 0; |
| int flag_print_class_info = 1; |
| int flag_print_constant_pool = 0; |
| int flag_print_fields = 1; |
| int flag_print_methods = 1; |
| int flag_print_attributes = 1; |
| |
| /* When nonzero, warn when source file is newer than matching class |
| file. */ |
| int flag_newer = 1; |
| |
| /* Print names of classes that have a "main" method. */ |
| int flag_print_main = 0; |
| |
| /* Index in constant pool of this class. */ |
| int this_class_index = 0; |
| |
| int class_access_flags = 0; |
| |
| /* Print in format similar to javap. VERY INCOMPLETE. */ |
| int flag_javap_compatible = 0; |
| |
| static void print_access_flags (FILE *, uint16, char); |
| static void print_constant_terse (FILE*, JCF*, int, int); |
| static void print_constant (FILE *, JCF *, int, int); |
| static void print_constant_ref (FILE *, JCF *, int); |
| static void disassemble_method (JCF*, const unsigned char *, int); |
| static void print_name (FILE*, JCF*, int); |
| static void print_signature (FILE*, JCF*, int, int); |
| static int utf8_equal_string (struct JCF*, int, const char *); |
| static void usage (void) ATTRIBUTE_NORETURN; |
| static void help (void) ATTRIBUTE_NORETURN; |
| static void version (void) ATTRIBUTE_NORETURN; |
| static void process_class (struct JCF *); |
| static void print_constant_pool (struct JCF *); |
| static void print_exception_table (struct JCF *, const unsigned char *entries, |
| int); |
| |
| #define PRINT_SIGNATURE_RESULT_ONLY 1 |
| #define PRINT_SIGNATURE_ARGS_ONLY 2 |
| |
| static int |
| utf8_equal_string (JCF *jcf, int index, const char * value) |
| { |
| if (CPOOL_INDEX_IN_RANGE (&jcf->cpool, index) |
| && JPOOL_TAG (jcf, index) == CONSTANT_Utf8) |
| { |
| int len = strlen (value); |
| if (JPOOL_UTF_LENGTH (jcf, index) == len |
| && memcmp (JPOOL_UTF_DATA (jcf, index), value, len) == 0) |
| return 1; |
| } |
| return 0; |
| } |
| |
| #define HANDLE_MAGIC(MAGIC, MINOR, MAJOR) \ |
| this_class_index = 0; \ |
| if (flag_print_class_info) \ |
| fprintf (out, \ |
| "Magic number: 0x%0lx, minor_version: %ld, major_version: %ld.\n",\ |
| (long) MAGIC, (long) MINOR, (long) MAJOR) |
| |
| #define HANDLE_START_CONSTANT_POOL(COUNT) \ |
| if (flag_print_constant_pool) \ |
| fprintf (out, "\nConstant pool (count: %d):\n", COUNT) |
| |
| #define HANDLE_SOURCEFILE(INDEX) \ |
| { fprintf (out, "Attribute "); \ |
| print_constant_terse (out, jcf, attribute_name, CONSTANT_Utf8); \ |
| fprintf (out, ", length:%ld, #%d=", (long) attribute_length, INDEX); \ |
| print_constant_terse (out, jcf, INDEX, CONSTANT_Utf8); fputc ('\n', out); } |
| |
| #define HANDLE_CLASS_INFO(ACCESS_FLAGS, THIS, SUPER, INTERFACES_COUNT) \ |
| this_class_index = THIS; \ |
| class_access_flags = ACCESS_FLAGS; \ |
| if (flag_print_class_info) \ |
| { fprintf (out, "\nAccess flags: 0x%x", ACCESS_FLAGS); \ |
| print_access_flags (out, ACCESS_FLAGS, 'c'); \ |
| fputc ('\n', out); \ |
| fprintf (out, "This class: "); \ |
| print_constant_terse_with_index (out, jcf, THIS, CONSTANT_Class); \ |
| if (flag_print_constant_pool || SUPER != 0) \ |
| fprintf (out, ", super: "); \ |
| if (flag_print_constant_pool) \ |
| { \ |
| fprintf (out, "%d", SUPER); \ |
| if (SUPER != 0) \ |
| fputc ('=', out); \ |
| } \ |
| if (SUPER != 0) \ |
| print_constant_terse (out, jcf, SUPER, CONSTANT_Class); \ |
| fprintf (out, "\nInterfaces (count: %d):\n", INTERFACES_COUNT); \ |
| } |
| |
| #define IGNORE_ATTRIBUTE(JCF, NAME, NAME_LENGTH) \ |
| (flag_print_attributes <= 0) |
| |
| #define HANDLE_CLASS_INTERFACE(INDEX) \ |
| if (flag_print_class_info) \ |
| { fprintf (out, "- Implements: "); \ |
| print_constant_terse_with_index (out, jcf, INDEX, CONSTANT_Class); \ |
| fputc ('\n', out); } |
| |
| #define HANDLE_START_FIELDS(FIELDS_COUNT) \ |
| if (flag_print_fields) \ |
| fprintf (out, "\nFields (count: %d):\n", FIELDS_COUNT) |
| |
| #define HANDLE_START_FIELD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \ |
| if (flag_print_fields) \ |
| { fprintf (out, "Field name:"); \ |
| print_constant_terse (out, jcf, NAME, CONSTANT_Utf8); \ |
| print_access_flags (out, ACCESS_FLAGS, 'f'); \ |
| fprintf (out, " Signature: "); \ |
| if (flag_print_constant_pool) \ |
| fprintf (out, "%d=", SIGNATURE); \ |
| print_signature (out, jcf, SIGNATURE, 0); \ |
| fputc ('\n', out); } \ |
| else \ |
| flag_print_attributes--; |
| |
| #define HANDLE_END_FIELD() \ |
| if (! flag_print_fields) \ |
| flag_print_attributes++; |
| |
| #define HANDLE_START_METHODS(METHODS_COUNT) \ |
| if (flag_print_methods) \ |
| fprintf (out, "\nMethods (count: %d):\n", METHODS_COUNT); \ |
| else \ |
| flag_print_attributes--; |
| |
| |
| #define HANDLE_END_METHODS() \ |
| if (! flag_print_methods) \ |
| flag_print_attributes++; |
| |
| #define HANDLE_METHOD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \ |
| { \ |
| if (flag_print_methods) \ |
| { \ |
| if (flag_javap_compatible) \ |
| { \ |
| fprintf (out, " "); \ |
| print_access_flags (out, ACCESS_FLAGS, 'm'); \ |
| fputc (' ', out); \ |
| print_signature (out, jcf, SIGNATURE, PRINT_SIGNATURE_RESULT_ONLY); \ |
| fputc (' ', out); \ |
| print_constant_terse (out, jcf, NAME, CONSTANT_Utf8); \ |
| print_signature (out, jcf, SIGNATURE, PRINT_SIGNATURE_ARGS_ONLY); \ |
| fputc ('\n', out); \ |
| } \ |
| else \ |
| { \ |
| fprintf (out, "\nMethod name:"); \ |
| print_constant_terse (out, jcf, NAME, CONSTANT_Utf8); \ |
| print_access_flags (out, ACCESS_FLAGS, 'm'); \ |
| fprintf (out, " Signature: "); \ |
| if (flag_print_constant_pool) \ |
| fprintf (out, "%d=", SIGNATURE); \ |
| print_signature (out, jcf, SIGNATURE, 0); \ |
| fputc ('\n', out); \ |
| } \ |
| } \ |
| if (flag_print_main && ACCESS_FLAGS == (ACC_STATIC|ACC_PUBLIC) \ |
| && utf8_equal_string (jcf, NAME, "main") \ |
| && utf8_equal_string (jcf, SIGNATURE, "([Ljava/lang/String;)V") \ |
| && this_class_index > 0 \ |
| && (class_access_flags & ACC_PUBLIC)) \ |
| { \ |
| print_constant_terse(out, jcf, this_class_index, CONSTANT_Class); \ |
| fputc ('\n', out); \ |
| } \ |
| } |
| |
| #define COMMON_HANDLE_ATTRIBUTE(JCF, INDEX, LENGTH) \ |
| ( fprintf (out, "Attribute "), \ |
| print_constant_terse (out, jcf, INDEX, CONSTANT_Utf8), \ |
| fprintf (out, ", length:%ld", (long) LENGTH) ) |
| |
| #define HANDLE_CONSTANTVALUE(VALUE_INDEX) \ |
| ( COMMON_HANDLE_ATTRIBUTE(JCF, attribute_name, attribute_length), \ |
| fprintf (out, ", value: "), \ |
| print_constant_ref (out, jcf, VALUE_INDEX), \ |
| fprintf (out, "\n") ) |
| |
| #define HANDLE_CODE_ATTRIBUTE(MAX_STACK, MAX_LOCALS, CODE_LENGTH) \ |
| { COMMON_HANDLE_ATTRIBUTE(JCF, attribute_name, attribute_length); \ |
| fprintf (out, ", max_stack:%ld, max_locals:%ld, code_length:%ld\n", \ |
| (long) MAX_STACK, (long) MAX_LOCALS, (long) CODE_LENGTH); \ |
| disassemble_method (jcf, jcf->read_ptr, CODE_LENGTH); } |
| |
| #define HANDLE_EXCEPTION_TABLE(ENTRIES, COUNT) \ |
| print_exception_table (jcf, ENTRIES, COUNT) |
| |
| #define HANDLE_EXCEPTIONS_ATTRIBUTE(COUNT) \ |
| { int n = (COUNT); int i; \ |
| COMMON_HANDLE_ATTRIBUTE(JCF, attribute_name, attribute_length); \ |
| fprintf (out, ", count: %d\n", n); \ |
| for (i = 0; i < n; i++) {\ |
| int ex_index = JCF_readu2 (jcf); \ |
| fprintf (out, "%3d: ", i); \ |
| print_constant_ref (out, jcf, ex_index); \ |
| fputc ('\n', out); } } |
| |
| #define HANDLE_LOCALVARIABLETABLE_ATTRIBUTE(COUNT) \ |
| { int n = (COUNT); int i; \ |
| COMMON_HANDLE_ATTRIBUTE(JCF, attribute_name, attribute_length); \ |
| fprintf (out, ", count: %d\n", n); \ |
| for (i = 0; i < n; i++) {\ |
| int start_pc = JCF_readu2 (jcf); \ |
| int length = JCF_readu2 (jcf); \ |
| int name_index = JCF_readu2 (jcf); \ |
| int signature_index = JCF_readu2 (jcf); \ |
| int slot = JCF_readu2 (jcf); \ |
| fprintf (out, " slot#%d: name: ", slot); \ |
| if (flag_print_constant_pool) \ |
| fprintf (out, "%d=", name_index); \ |
| print_name (out, jcf, name_index); \ |
| fprintf (out, ", type: "); \ |
| if (flag_print_constant_pool) \ |
| fprintf (out, "%d=", signature_index); \ |
| print_signature (out, jcf, signature_index, 0); \ |
| fprintf (out, " (pc: %d length: %d)\n", start_pc, length); }} |
| |
| #define HANDLE_LINENUMBERTABLE_ATTRIBUTE(COUNT) \ |
| { int n = (COUNT); int i; \ |
| COMMON_HANDLE_ATTRIBUTE(jcf, attribute_name, attribute_length); \ |
| fprintf (out, ", count: %d\n", n); \ |
| if (flag_disassemble_methods) \ |
| for (i = 0; i < n; i++) {\ |
| int start_pc = JCF_readu2 (jcf); \ |
| int line_number = JCF_readu2 (jcf); \ |
| fprintf (out, " line: %d at pc: %d\n", line_number, start_pc); }\ |
| else \ |
| JCF_SKIP (jcf, 4 * n); } |
| |
| #define HANDLE_INNERCLASSES_ATTRIBUTE(COUNT) \ |
| { int n = (COUNT); \ |
| COMMON_HANDLE_ATTRIBUTE(jcf, attribute_name, attribute_length); \ |
| while (n--) \ |
| { \ |
| uint16 inner_class_info_index = JCF_readu2 (jcf); \ |
| uint16 outer_class_info_index = JCF_readu2 (jcf); \ |
| uint16 inner_name_index = JCF_readu2 (jcf); \ |
| uint16 inner_class_access_flags = JCF_readu2 (jcf); \ |
| \ |
| if (flag_print_class_info) \ |
| { \ |
| fprintf (out, "\n inner: "); \ |
| print_constant_terse_with_index (out, jcf, \ |
| inner_class_info_index, CONSTANT_Class); \ |
| if (inner_name_index == 0) \ |
| fprintf (out, " (anonymous)"); \ |
| else if (verbose || flag_print_constant_pool) \ |
| { \ |
| fprintf (out, " ("); \ |
| print_constant_terse_with_index (out, jcf, inner_name_index, \ |
| CONSTANT_Utf8); \ |
| fputc (')', out); \ |
| } \ |
| fprintf (out, ", access flags: 0x%x", inner_class_access_flags); \ |
| print_access_flags (out, inner_class_access_flags, 'c'); \ |
| fprintf (out, ", outer class: "); \ |
| print_constant_terse_with_index (out, jcf, \ |
| outer_class_info_index, CONSTANT_Class); \ |
| } \ |
| } \ |
| if (flag_print_class_info) \ |
| fputc ('\n', out); \ |
| } |
| |
| #define PROCESS_OTHER_ATTRIBUTE(JCF, INDEX, LENGTH) \ |
| { COMMON_HANDLE_ATTRIBUTE(JCF, INDEX, LENGTH); \ |
| fputc ('\n', out); JCF_SKIP (JCF, LENGTH); } |
| |
| #define START_FINAL_ATTRIBUTES(ATTRIBUTES_COUNT) \ |
| if (flag_print_attributes > 0) \ |
| fprintf (out, "\nAttributes (count: %d):\n", attributes_count); |
| |
| #include "javaop.h" |
| |
| static void |
| print_constant_ref (FILE *stream, JCF *jcf, int index) |
| { |
| if (index <= 0 || index >= JPOOL_SIZE(jcf)) |
| fprintf (stream, "<out of range>"); |
| else |
| { |
| if (flag_print_constant_pool) |
| fprintf (stream, "#%d=", index); |
| fputc ('<', stream); |
| print_constant (stream, jcf, index, 1); |
| fputc ('>', stream); |
| } |
| } |
| |
| /* Print the access flags given by FLAGS. |
| The CONTEXT is one of 'c' (class flags), 'f' (field flags), |
| or 'm' (method flags). */ |
| |
| static void |
| print_access_flags (FILE *stream, uint16 flags, char context) |
| { |
| if (flags & ACC_PUBLIC) fprintf (stream, " public"); |
| if (flags & ACC_PRIVATE) fprintf (stream, " private"); |
| if (flags & ACC_PROTECTED) fprintf (stream, " protected"); |
| if (flags & ACC_ABSTRACT) fprintf (stream, " abstract"); |
| if (flags & ACC_STATIC) fprintf (stream, " static"); |
| if (flags & ACC_FINAL) fprintf (stream, " final"); |
| if (flags & ACC_TRANSIENT) fprintf (stream, " transient"); |
| if (flags & ACC_VOLATILE) fprintf (stream, " volatile"); |
| if (flags & ACC_NATIVE) fprintf (stream, " native"); |
| if (flags & ACC_SYNCHRONIZED) |
| { |
| if (context == 'c') |
| fprintf (stream, " super"); |
| else |
| fprintf (stream, " synchronized"); |
| } |
| if (flags & ACC_INTERFACE) fprintf (stream, " interface"); |
| if (flags & ACC_STRICT) fprintf (stream, " strictfp"); |
| } |
| |
| |
| static void |
| print_name (FILE* stream, JCF* jcf, int name_index) |
| { |
| if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8) |
| fprintf (stream, "<not a UTF8 constant>"); |
| else |
| jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf,name_index), |
| JPOOL_UTF_LENGTH (jcf, name_index)); |
| } |
| |
| /* If the type of the constant at INDEX matches EXPECTED, |
| print it tersely, otherwise more verbosely. */ |
| |
| static void |
| print_constant_terse (FILE *out, JCF *jcf, int index, int expected) |
| { |
| if (! CPOOL_INDEX_IN_RANGE (&jcf->cpool, index)) |
| fprintf (out, "<constant pool index %d not in range>", index); |
| else if (JPOOL_TAG (jcf, index) != expected) |
| { |
| fprintf (out, "<Unexpected constant type "); |
| print_constant (out, jcf, index, 1); |
| fprintf (out, ">"); |
| } |
| else |
| print_constant (out, jcf, index, 0); |
| } |
| |
| static void |
| print_constant_terse_with_index (FILE *out, JCF *jcf, int index, int expected) |
| { |
| if (flag_print_constant_pool) |
| fprintf (out, "%d=", index); |
| print_constant_terse (out, jcf, index, expected); |
| } |
| |
| /* Print the constant at INDEX in JCF's constant pool. |
| If verbosity==0, print very tersely (no extraneous text). |
| If verbosity==1, prefix the type of the constant. |
| If verbosity==2, add more descriptive text. */ |
| |
| static void |
| print_constant (FILE *out, JCF *jcf, int index, int verbosity) |
| { |
| int j, n; |
| jlong num; |
| const char *str; |
| int kind = JPOOL_TAG (jcf, index); |
| switch (kind) |
| { |
| case CONSTANT_Class: |
| n = JPOOL_USHORT1 (jcf, index); |
| if (verbosity > 0) |
| { |
| if (verbosity > 1) |
| fprintf (out, "Class name: %d=", n); |
| else |
| fprintf (out, "Class "); |
| } |
| if (! CPOOL_INDEX_IN_RANGE (&jcf->cpool, n)) |
| fprintf (out, "<out of range>"); |
| else if (verbosity < 2 && JPOOL_TAG (jcf, n) == CONSTANT_Utf8) |
| { |
| int len = JPOOL_UTF_LENGTH (jcf, n); |
| jcf_print_utf8_replace (out, JPOOL_UTF_DATA(jcf,n), len, '/', '.'); |
| } |
| else |
| print_constant_terse (out, jcf, n, CONSTANT_Utf8); |
| break; |
| case CONSTANT_Fieldref: |
| str = "Field"; goto field_or_method; |
| case CONSTANT_Methodref: |
| str = "Method"; goto field_or_method; |
| case CONSTANT_InterfaceMethodref: |
| str = "InterfaceMethod"; goto field_or_method; |
| field_or_method: |
| { |
| uint16 tclass = JPOOL_USHORT1 (jcf, index); |
| uint16 name_and_type = JPOOL_USHORT2 (jcf, index); |
| if (verbosity == 2) |
| fprintf (out, "%sref class: %d=", str, tclass); |
| else if (verbosity > 0) |
| fprintf (out, "%s ", str); |
| print_constant_terse (out, jcf, tclass, CONSTANT_Class); |
| if (verbosity < 2) |
| fprintf (out, "."); |
| else |
| fprintf (out, " name_and_type: %d=<", name_and_type); |
| print_constant_terse (out, jcf, name_and_type, CONSTANT_NameAndType); |
| if (verbosity == 2) |
| fputc ('>', out); |
| } |
| break; |
| case CONSTANT_String: |
| j = JPOOL_USHORT1 (jcf, index); |
| if (verbosity > 0) |
| { |
| if (verbosity > 1) |
| fprintf (out, "String %d=", j); |
| else |
| fprintf (out, "String "); |
| } |
| print_constant_terse (out, jcf, j, CONSTANT_Utf8); |
| break; |
| case CONSTANT_Integer: |
| if (verbosity > 0) |
| fprintf (out, "Integer "); |
| num = JPOOL_INT (jcf, index); |
| goto integer; |
| case CONSTANT_Long: |
| if (verbosity > 0) |
| fprintf (out, "Long "); |
| num = JPOOL_LONG (jcf, index); |
| goto integer; |
| integer: |
| { |
| char buffer[25]; |
| format_int (buffer, num, 10); |
| fprintf (out, "%s", buffer); |
| if (verbosity > 1) |
| { |
| format_uint (buffer, (uint64)num, 16); |
| fprintf (out, "=0x%s", buffer); |
| } |
| } |
| break; |
| case CONSTANT_Float: |
| { |
| jfloat fnum = JPOOL_FLOAT (jcf, index); |
| |
| if (verbosity > 0) |
| fputs ("Float ", out); |
| |
| if (fnum.negative) |
| putc ('-', out); |
| |
| if (JFLOAT_FINITE (fnum)) |
| { |
| int dummy; |
| int exponent = fnum.exponent - JFLOAT_EXP_BIAS; |
| double f; |
| uint32 mantissa = fnum.mantissa; |
| if (fnum.exponent == 0) |
| /* Denormal. */ |
| exponent++; |
| else |
| /* Normal; add the implicit bit. */ |
| mantissa |= ((uint32)1 << 23); |
| |
| f = frexp (mantissa, &dummy); |
| f = ldexp (f, exponent + 1); |
| fprintf (out, "%.10g", f); |
| } |
| else |
| { |
| if (fnum.mantissa == 0) |
| fputs ("Inf", out); |
| else if (fnum.mantissa & JFLOAT_QNAN_MASK) |
| fprintf (out, "QNaN(%u)", (fnum.mantissa & ~JFLOAT_QNAN_MASK)); |
| else |
| fprintf (out, "SNaN(%u)", (fnum.mantissa & ~JFLOAT_QNAN_MASK)); |
| } |
| |
| if (verbosity > 1) |
| fprintf (out, ", bits = 0x%08lx", JPOOL_UINT (jcf, index)); |
| |
| break; |
| } |
| case CONSTANT_Double: |
| { |
| jdouble dnum = JPOOL_DOUBLE (jcf, index); |
| |
| if (verbosity > 0) |
| fputs ("Double ", out); |
| |
| if (dnum.negative) |
| putc ('-', out); |
| |
| if (JDOUBLE_FINITE (dnum)) |
| { |
| int dummy; |
| int exponent = dnum.exponent - JDOUBLE_EXP_BIAS; |
| double d; |
| uint64 mantissa = ((((uint64) dnum.mantissa0) << 32) |
| + dnum.mantissa1); |
| if (dnum.exponent == 0) |
| /* Denormal. */ |
| exponent++; |
| else |
| /* Normal; add the implicit bit. */ |
| mantissa |= ((uint64)1 << 52); |
| |
| d = frexp (mantissa, &dummy); |
| d = ldexp (d, exponent + 1); |
| fprintf (out, "%.20g", d); |
| } |
| else |
| { |
| uint64 mantissa = dnum.mantissa0 & ~JDOUBLE_QNAN_MASK; |
| mantissa = (mantissa << 32) + dnum.mantissa1; |
| |
| if (dnum.mantissa0 == 0 && dnum.mantissa1 == 0) |
| fputs ("Inf", out); |
| else if (dnum.mantissa0 & JDOUBLE_QNAN_MASK) |
| fprintf (out, "QNaN(%llu)", (unsigned long long)mantissa); |
| else |
| fprintf (out, "SNaN(%llu)", (unsigned long long)mantissa); |
| } |
| if (verbosity > 1) |
| { |
| int32 hi, lo; |
| hi = JPOOL_UINT (jcf, index); |
| lo = JPOOL_UINT (jcf, index + 1); |
| fprintf (out, ", bits = 0x%08lx%08lx", (long) hi, (long) lo); |
| } |
| break; |
| } |
| case CONSTANT_NameAndType: |
| { |
| uint16 name = JPOOL_USHORT1 (jcf, index); |
| uint16 sig = JPOOL_USHORT2 (jcf, index); |
| if (verbosity > 0) |
| { |
| if (verbosity > 1) |
| fprintf (out, "NameAndType name: %d=", name); |
| else |
| fprintf (out, "NameAndType "); |
| } |
| print_name (out, jcf, name); |
| if (verbosity <= 1) |
| fputc (' ', out); |
| else |
| fprintf (out, ", signature: %d=", sig); |
| print_signature (out, jcf, sig, 0); |
| } |
| break; |
| case CONSTANT_Utf8: |
| { |
| const unsigned char *str = JPOOL_UTF_DATA (jcf, index); |
| int length = JPOOL_UTF_LENGTH (jcf, index); |
| if (verbosity > 0) |
| { /* Print as 8-bit bytes. */ |
| fputs ("Utf8: \"", out); |
| while (--length >= 0) |
| jcf_print_char (out, *str++); |
| } |
| else |
| { /* Print as Unicode. */ |
| fputc ('\"', out); |
| jcf_print_utf8 (out, str, length); |
| } |
| fputc ('\"', out); |
| } |
| break; |
| default: |
| fprintf (out, "(Unknown constant type %d)", kind); |
| } |
| } |
| |
| static void |
| print_constant_pool (JCF *jcf) |
| { |
| int i; |
| for (i = 1; i < JPOOL_SIZE(jcf); i++) |
| { |
| int kind = JPOOL_TAG (jcf, i); |
| fprintf (out, "#%d: ", i); |
| print_constant (out, jcf, i, 2); |
| fprintf (out, "\n"); |
| if (kind == CONSTANT_Double || kind == CONSTANT_Long) |
| i++; /* These take up two slots in the constant table */ |
| } |
| } |
| |
| static void |
| print_signature_type (FILE* stream, const unsigned char **ptr, |
| const unsigned char *limit) |
| { |
| int array_size; |
| if ((*ptr) >= limit) |
| return; |
| switch (*(*ptr)) |
| { |
| case '[': |
| array_size = -1; |
| for ((*ptr)++; (*ptr) < limit && ISDIGIT (**ptr); (*ptr)++) |
| { |
| array_size = (array_size < 0 ? 0 : 10 * array_size) + *(*ptr) - '0'; |
| } |
| print_signature_type (stream, ptr, limit); |
| if (array_size == -1) |
| fprintf (stream, "[]"); |
| else |
| fprintf (stream, "[%d]", array_size); |
| break; |
| case '(': |
| { |
| int nargs = 0; |
| fputc (*(*ptr)++, stream); |
| for (; **ptr != ')' && *ptr < limit; nargs++) |
| { |
| if (nargs > 0) |
| fputc (',', stream); |
| print_signature_type (stream, ptr, limit); |
| } |
| if (*ptr < limit) |
| { |
| fputc (*(*ptr)++, stream); |
| print_signature_type (stream, ptr, limit); |
| } |
| else |
| fprintf (stream, "???"); |
| } |
| break; |
| |
| case 'B': fprintf (stream, "byte"); (*ptr)++; break; |
| case 'C': fprintf (stream, "char"); (*ptr)++; break; |
| case 'D': fprintf (stream, "double"); (*ptr)++; break; |
| case 'F': fprintf (stream, "float"); (*ptr)++; break; |
| case 'S': fprintf (stream, "short"); (*ptr)++; break; |
| case 'I': fprintf (stream, "int"); (*ptr)++; break; |
| case 'J': fprintf (stream, "long"); (*ptr)++; break; |
| case 'Z': fprintf (stream, "boolean"); (*ptr)++; break; |
| case 'V': fprintf (stream, "void"); (*ptr)++; break; |
| |
| case 'L': |
| for ((*ptr)++; (*ptr)<limit && *(*ptr) != ';'; (*ptr)++) |
| jcf_print_char (stream, *(*ptr) == '/' ? '.' : *(*ptr)); |
| if (*(*ptr) == ';') |
| (*ptr)++; |
| break; |
| default: |
| jcf_print_char (stream, *(*ptr)++); |
| } |
| } |
| |
| static void |
| print_signature (FILE* stream, JCF *jcf, int signature_index, int options) |
| { |
| if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8) |
| print_constant_terse (out, jcf, signature_index, CONSTANT_Utf8); |
| else |
| { |
| const unsigned char *str = JPOOL_UTF_DATA (jcf, signature_index); |
| int length = JPOOL_UTF_LENGTH (jcf, signature_index); |
| const unsigned char *limit; |
| limit = str + length; |
| if (str >= limit) |
| fprintf (stream, "<empty signature string>"); |
| else |
| { |
| if (options & PRINT_SIGNATURE_RESULT_ONLY) |
| { |
| while (str < limit && *str++ != ')') ; |
| } |
| if (options & PRINT_SIGNATURE_ARGS_ONLY) |
| { |
| str++; |
| fputc ('(', stream); |
| while (str < limit && *str != ')') |
| { |
| print_signature_type (stream, &str, limit); |
| if (*str != ')') |
| fputs (", ", stream); |
| } |
| fputc (')', stream); |
| } |
| else |
| { |
| print_signature_type (stream, &str, limit); |
| if (str < limit) |
| { |
| fprintf (stream, "<junk:"); |
| jcf_print_utf8 (stream, str, limit - str); |
| fputc ('>', stream); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| static void |
| print_exception_table (JCF *jcf, const unsigned char *entries, int count) |
| { |
| /* Print exception table. */ |
| int i = count; |
| if (i > 0) |
| { |
| const unsigned char *ptr = entries; |
| fprintf (out, "Exceptions (count: %d):\n", i); |
| for (; --i >= 0; ptr+= 8) |
| { |
| int start_pc = GET_u2 (ptr); |
| int end_pc = GET_u2 (ptr+2); |
| int handler_pc = GET_u2 (ptr+4); |
| int catch_type = GET_u2 (ptr+6); |
| fprintf (out, " start: %d, end: %d, handler: %d, type: ", |
| start_pc, end_pc, handler_pc); |
| if (catch_type == 0) |
| fputs ("0 /* finally */", out); |
| else |
| print_constant_terse_with_index (out, jcf, |
| catch_type, CONSTANT_Class); |
| fputc ('\n', out); |
| } |
| } |
| } |
| |
| #include "jcf-reader.c" |
| |
| static void |
| process_class (JCF *jcf) |
| { |
| int code; |
| if (jcf_parse_preamble (jcf) != 0) |
| fprintf (stderr, _("Not a valid Java .class file.\n")); |
| |
| /* Parse and possibly print constant pool */ |
| code = jcf_parse_constant_pool (jcf); |
| if (code != 0) |
| { |
| fprintf (stderr, _("error while parsing constant pool\n")); |
| exit (FATAL_EXIT_CODE); |
| } |
| code = verify_constant_pool (jcf); |
| if (code > 0) |
| { |
| fprintf (stderr, _("error in constant pool entry #%d\n"), code); |
| exit (FATAL_EXIT_CODE); |
| } |
| if (flag_print_constant_pool) |
| print_constant_pool (jcf); |
| |
| jcf_parse_class (jcf); |
| code = jcf_parse_fields (jcf); |
| if (code != 0) |
| { |
| fprintf (stderr, _("error while parsing fields\n")); |
| exit (FATAL_EXIT_CODE); |
| } |
| code = jcf_parse_methods (jcf); |
| if (code != 0) |
| { |
| fprintf (stderr, _("error while parsing methods\n")); |
| exit (FATAL_EXIT_CODE); |
| } |
| code = jcf_parse_final_attributes (jcf); |
| if (code != 0) |
| { |
| fprintf (stderr, _("error while parsing final attributes\n")); |
| exit (FATAL_EXIT_CODE); |
| } |
| jcf->filename = NULL; |
| } |
| |
| |
| |
| /* This is used to mark options with no short value. */ |
| #define LONG_OPT(Num) ((Num) + 128) |
| |
| #define OPT_classpath LONG_OPT (0) |
| #define OPT_CLASSPATH OPT_classpath |
| #define OPT_bootclasspath LONG_OPT (1) |
| #define OPT_extdirs LONG_OPT (2) |
| #define OPT_HELP LONG_OPT (3) |
| #define OPT_VERSION LONG_OPT (4) |
| #define OPT_JAVAP LONG_OPT (5) |
| |
| static const struct option options[] = |
| { |
| { "classpath", required_argument, NULL, OPT_classpath }, |
| { "bootclasspath", required_argument, NULL, OPT_bootclasspath }, |
| { "extdirs", required_argument, NULL, OPT_extdirs }, |
| { "CLASSPATH", required_argument, NULL, OPT_CLASSPATH }, |
| { "help", no_argument, NULL, OPT_HELP }, |
| { "verbose", no_argument, NULL, 'v' }, |
| { "version", no_argument, NULL, OPT_VERSION }, |
| { "javap", no_argument, NULL, OPT_JAVAP }, |
| { "print-main", no_argument, &flag_print_main, 1 }, |
| { "print-constants", no_argument, &flag_print_constant_pool, 1 }, |
| { NULL, no_argument, NULL, 0 } |
| }; |
| |
| static void |
| usage (void) |
| { |
| fprintf (stderr, _("Try 'jcf-dump --help' for more information.\n")); |
| exit (1); |
| } |
| |
| static void |
| help (void) |
| { |
| printf (_("Usage: jcf-dump [OPTION]... CLASS...\n\n")); |
| printf (_("Display contents of a class file in readable form.\n\n")); |
| printf (_(" -c Disassemble method bodies\n")); |
| printf (_(" --javap Generate output in 'javap' format\n")); |
| printf ("\n"); |
| printf (_(" --classpath PATH Set path to find .class files\n")); |
| printf (_(" -IDIR Append directory to class path\n")); |
| printf (_(" --bootclasspath PATH Override built-in class path\n")); |
| printf (_(" --extdirs PATH Set extensions directory path\n")); |
| printf (_(" -o FILE Set output file name\n")); |
| printf ("\n"); |
| printf (_(" --help Print this help, then exit\n")); |
| printf (_(" --version Print version number, then exit\n")); |
| printf (_(" -v, --verbose Print extra information while running\n")); |
| printf ("\n"); |
| printf (_("For bug reporting instructions, please see:\n" |
| "%s.\n"), bug_report_url); |
| exit (0); |
| } |
| |
| static void |
| version (void) |
| { |
| printf ("jcf-dump (GCC) %s\n\n", version_string); |
| printf ("Copyright %s 2004 Free Software Foundation, Inc.\n", _("(C)")); |
| printf (_("This is free software; see the source for copying conditions. There is NO\n" |
| "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n")); |
| exit (0); |
| } |
| |
| int |
| main (int argc, char** argv) |
| { |
| JCF jcf[1]; |
| int argi, opt; |
| |
| /* Unlock the stdio streams. */ |
| unlock_std_streams (); |
| |
| gcc_init_libintl (); |
| |
| if (argc <= 1) |
| { |
| fprintf (stderr, _("jcf-dump: no classes specified\n")); |
| usage (); |
| } |
| |
| jcf_path_init (); |
| |
| /* We use getopt_long_only to allow single `-' long options. For |
| some of our options this is more natural. */ |
| while ((opt = getopt_long_only (argc, argv, "o:I:vc", options, NULL)) != -1) |
| { |
| switch (opt) |
| { |
| case 0: |
| /* Already handled. */ |
| break; |
| |
| case 'o': |
| output_file = optarg; |
| break; |
| |
| case 'I': |
| jcf_path_include_arg (optarg); |
| break; |
| |
| case 'v': |
| verbose++; |
| break; |
| |
| case 'c': |
| flag_disassemble_methods = 1; |
| break; |
| |
| case OPT_classpath: |
| jcf_path_classpath_arg (optarg); |
| break; |
| |
| case OPT_bootclasspath: |
| jcf_path_bootclasspath_arg (optarg); |
| break; |
| |
| case OPT_extdirs: |
| jcf_path_extdirs_arg (optarg); |
| break; |
| |
| case OPT_HELP: |
| help (); |
| break; |
| |
| case OPT_VERSION: |
| version (); |
| break; |
| |
| case OPT_JAVAP: |
| flag_javap_compatible++; |
| flag_print_constant_pool = 0; |
| flag_print_attributes = 0; |
| break; |
| |
| default: |
| usage (); |
| } |
| } |
| |
| if (verbose && ! flag_javap_compatible) |
| flag_print_constant_pool = 1; |
| |
| if (optind == argc) |
| { |
| fprintf (stderr, _("jcf-dump: no classes specified\n")); |
| usage (); |
| } |
| |
| jcf_path_seal (verbose); |
| |
| if (flag_print_main) |
| { |
| flag_print_fields = 0; |
| flag_print_methods = 0; |
| flag_print_constant_pool = 0; |
| flag_print_attributes = 0; |
| flag_print_class_info = 0; |
| } |
| |
| if (output_file) |
| { |
| out = fopen (output_file, "w"); |
| if (! out) |
| { |
| fprintf (stderr, _("Cannot open '%s' for output.\n"), output_file); |
| return FATAL_EXIT_CODE; |
| } |
| } |
| else |
| out = stdout; |
| |
| if (optind >= argc) |
| { |
| fprintf (out, "Reading .class from <standard input>.\n"); |
| open_class ("<stdio>", jcf, 0, NULL); |
| process_class (jcf); |
| } |
| else |
| { |
| for (argi = optind; argi < argc; argi++) |
| { |
| char *arg = argv[argi]; |
| const char *class_filename = find_class (arg, strlen (arg), jcf, 0); |
| if (class_filename == NULL) |
| class_filename = find_classfile (arg, jcf, NULL); |
| if (class_filename == NULL) |
| { |
| perror ("Could not find class"); |
| return FATAL_EXIT_CODE; |
| } |
| JCF_FILL (jcf, 4); |
| if (GET_u4 (jcf->read_ptr) == ZIPMAGIC) |
| { |
| long compressed_size, member_size; |
| int compression_method, filename_length, extra_length; |
| int general_purpose_bits; |
| const char *filename; |
| int total_length; |
| if (flag_print_class_info) |
| fprintf (out, "Reading classes from archive %s.\n", |
| class_filename); |
| for (;;) |
| { |
| int skip = 0; |
| jcf_filbuf_t save_filbuf = jcf->filbuf; |
| long magic = JCF_readu4_le (jcf); |
| if (magic == 0x02014b50 || magic == 0x06054b50) |
| break; /* got to central directory */ |
| if (magic != 0x04034b50) /* ZIPMAGIC (little-endian) */ |
| { |
| fprintf (stderr, _("bad format of .zip/.jar archive\n")); |
| return FATAL_EXIT_CODE; |
| } |
| JCF_FILL (jcf, 26); |
| JCF_SKIP (jcf, 2); |
| general_purpose_bits = JCF_readu2_le (jcf); |
| compression_method = JCF_readu2_le (jcf); |
| JCF_SKIP (jcf, 8); |
| compressed_size = JCF_readu4_le (jcf); |
| member_size = JCF_readu4_le (jcf); |
| filename_length = JCF_readu2_le (jcf); |
| extra_length = JCF_readu2_le (jcf); |
| total_length = filename_length + extra_length |
| + compressed_size; |
| if (jcf->read_end - jcf->read_ptr < total_length) |
| jcf_trim_old_input (jcf); |
| JCF_FILL (jcf, total_length); |
| filename = (const char *) jcf->read_ptr; |
| JCF_SKIP (jcf, filename_length); |
| JCF_SKIP (jcf, extra_length); |
| if (filename_length > 0 |
| && filename[filename_length-1] == '/') |
| { |
| if (flag_print_class_info) |
| fprintf (out, "[Skipping directory %.*s]\n", |
| filename_length, filename); |
| skip = 1; |
| } |
| else if (compression_method != 0) |
| { |
| if (flag_print_class_info) |
| fprintf (out, "[Skipping compressed file %.*s]\n", |
| filename_length, filename); |
| skip = 1; |
| } |
| else if (member_size < 4 |
| || GET_u4 (jcf->read_ptr) != 0xcafebabe) |
| { |
| if (flag_print_class_info) |
| fprintf (out, "[Skipping non-.class member %.*s]\n", |
| filename_length, filename); |
| skip = 1; |
| } |
| else |
| { |
| if (flag_print_class_info) |
| fprintf (out, "Reading class member: %.*s.\n", |
| filename_length, filename); |
| } |
| if (skip) |
| { |
| JCF_SKIP (jcf, compressed_size); |
| } |
| else |
| { |
| unsigned char *save_end; |
| jcf->filbuf = jcf_unexpected_eof; |
| save_end = jcf->read_end; |
| jcf->read_end = jcf->read_ptr + compressed_size; |
| process_class (jcf); |
| jcf->filbuf = save_filbuf; |
| jcf->read_end = save_end; |
| } |
| } |
| } |
| else |
| { |
| if (flag_print_class_info) |
| fprintf (out, "Reading .class from %s.\n", class_filename); |
| process_class (jcf); |
| } |
| JCF_FINISH(jcf); |
| } |
| } |
| |
| return SUCCESS_EXIT_CODE; |
| } |
| |
| |
| |
| static void |
| disassemble_method (JCF* jcf, const unsigned char *byte_ops, int len) |
| { |
| #undef PTR |
| int PC; |
| int i; |
| int saw_wide = 0; |
| if (flag_disassemble_methods == 0) |
| return; |
| #define BCODE byte_ops |
| for (PC = 0; PC < len;) |
| { |
| int oldpc = PC; |
| int saw_index; |
| jint INT_temp; |
| switch (byte_ops[PC++]) |
| { |
| |
| /* This is the actual code emitted for each of opcodes in javaops.def. |
| The actual opcode-specific stuff is handled by the OPKIND macro. |
| I.e. for an opcode whose OPKIND is BINOP, the BINOP will be called. |
| Those macros are defined below. The OPKINDs that do not have any |
| inline parameters (such as BINOP) and therefore do mot need anything |
| else to me printed out just use an empty body. */ |
| |
| #define JAVAOP(OPNAME, OPCODE, OPKIND, OPERAND_TYPE, OPERAND_VALUE) \ |
| case OPCODE: \ |
| fprintf (out, "%3d: %s", oldpc, #OPNAME); \ |
| OPKIND(OPERAND_TYPE, OPERAND_VALUE); \ |
| fputc ('\n', out); \ |
| break; |
| |
| #define CONST_INDEX_1 (saw_index = 1, IMMEDIATE_u1) |
| #define CONST_INDEX_2 (saw_index = 1, IMMEDIATE_u2) |
| #define VAR_INDEX_1 (saw_index = 1, IMMEDIATE_u1) |
| #define VAR_INDEX_2 (saw_index = 1, IMMEDIATE_u2) |
| |
| #define CHECK_PC_IN_RANGE(PC) (PC < 0 || PC > len ? \ |
| (fprintf(stderr, _("Bad byte codes.\n")), exit(-1), 0) : 1) |
| |
| /* Print out operand (if not implied by the opcode) for PUSCH opcodes. |
| These all push a constant onto the opcode stack. */ |
| #define PUSHC(OPERAND_TYPE, OPERAND_VALUE) \ |
| saw_index = 0, i = (OPERAND_VALUE); \ |
| if (oldpc+1 == PC) /* nothing */; \ |
| else if (saw_index) fprintf (out, " "), print_constant_ref (out, jcf, i); \ |
| else fprintf (out, " %d", i); |
| |
| /* Print out operand (a local variable index) for LOAD opcodes. |
| These all push local variable onto the opcode stack. */ |
| #define LOAD(OPERAND_TYPE, OPERAND_VALUE) \ |
| INT_temp = saw_wide ? IMMEDIATE_u2 : (OPERAND_VALUE); goto load_store; |
| |
| /* Handle STORE opcodes same as LOAD opcodes. |
| These all store a value from the opcode stack in a local variable. */ |
| #define STORE LOAD |
| |
| /* Handle more kind of opcodes. */ |
| #define STACK(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ |
| #define UNOP(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ |
| #define BINOP(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ |
| #define CONVERT(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ |
| #define CONVERT2(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ |
| #define RETURN(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ |
| #define UNKNOWN(OPERAND_TYPE, OPERAND_VALUE) /* nothing */ |
| |
| /* Handle putfield and getfield opcodes, with static versions. */ |
| #define FIELD(MAYBE_STATIC, PUT_OR_GET) \ |
| fputc (' ', out); print_constant_ref (out, jcf, IMMEDIATE_u2) |
| |
| /* Print operand for invoke opcodes. */ |
| #define INVOKE(OPERAND_TYPE, OPERAND_VALUE) \ |
| fputc (' ', out); print_constant_ref (out, jcf, IMMEDIATE_u2);\ |
| if (OPERAND_VALUE) /* for invokeinterface */ \ |
| { int nargs = IMMEDIATE_u1; PC++; \ |
| fprintf (out, " nargs:%d", nargs); } |
| |
| #define OBJECT(OPERAND_TYPE, OPERAND_VALUE) \ |
| fputc (' ', out); print_constant_ref (out, jcf, IMMEDIATE_u2); |
| |
| #define ARRAY(OPERAND_TYPE, SUBOP) \ |
| ARRAY_##SUBOP(OPERAND_TYPE) |
| /* Handle sub-categories of ARRAY opcodes. */ |
| #define ARRAY_LOAD(TYPE) /* nothing */ |
| #define ARRAY_STORE(TYPE) /* nothing */ |
| #define ARRAY_LENGTH(TYPE) /* nothing */ |
| #define ARRAY_NEW(TYPE) ARRAY_NEW_##TYPE |
| #define ARRAY_NEW_NUM \ |
| INT_temp = IMMEDIATE_u1; \ |
| { switch ((int) INT_temp) { \ |
| case 4: fputs (" boolean", out); break; \ |
| case 5: fputs (" char", out); break; \ |
| case 6: fputs (" float", out); break; \ |
| case 7: fputs (" double", out); break; \ |
| case 8: fputs (" byte", out); break; \ |
| case 9: fputs (" short", out); break; \ |
| case 10: fputs (" int", out); break; \ |
| case 11: fputs (" long", out); break; \ |
| default: fprintf (out, " <unknown type code %ld>", (long)INT_temp); break;\ |
| } } |
| |
| #define ARRAY_NEW_PTR \ |
| fputc (' ', out); print_constant_ref (out, jcf, IMMEDIATE_u2); |
| |
| #define ARRAY_NEW_MULTI \ |
| fputc (' ', out); print_constant_ref (out, jcf, IMMEDIATE_u2); \ |
| fprintf (out, " %d", IMMEDIATE_u1); /* number of dimensions */ |
| |
| #define TEST(OPERAND_TYPE, OPERAND_VALUE) \ |
| fprintf (out, " %d", oldpc + IMMEDIATE_s2) |
| |
| #define BRANCH(OPERAND_TYPE, OPERAND_VALUE) \ |
| saw_index = 0, INT_temp = (OPERAND_VALUE); \ |
| fprintf (out, " %ld", (long) (saw_index ? INT_temp : oldpc + INT_temp)) |
| |
| #define JSR(OPERAND_TYPE, OPERAND_VALUE) \ |
| saw_index = 0, INT_temp = (OPERAND_VALUE); \ |
| fprintf (out, " %ld", (long) (saw_index ? INT_temp : oldpc + INT_temp)) |
| |
| #undef RET /* Defined by config/i386/i386.h */ |
| #define RET(OPERAND_TYPE, OPERAND_VALUE) \ |
| INT_temp = saw_wide ? IMMEDIATE_u2 : (OPERAND_VALUE); \ |
| saw_wide = 0; \ |
| fprintf (out, " %ld", (long) INT_temp); |
| |
| #define SWITCH(OPERAND_TYPE, TABLE_OR_LOOKUP) \ |
| PC = (PC + 3) / 4 * 4; TABLE_OR_LOOKUP##_SWITCH |
| |
| #define LOOKUP_SWITCH \ |
| { jint default_offset = IMMEDIATE_s4; jint npairs = IMMEDIATE_s4; \ |
| fprintf (out, " npairs=%ld, default=%ld", (long) npairs, (long) default_offset+oldpc); \ |
| while (--npairs >= 0) { \ |
| jint match = IMMEDIATE_s4; jint offset = IMMEDIATE_s4; \ |
| fprintf (out, "\n%10ld: %ld", (long)match, (long)(offset+oldpc)); } \ |
| } |
| |
| #define TABLE_SWITCH \ |
| { jint default_offset = IMMEDIATE_s4; \ |
| jint low = IMMEDIATE_s4; jint high = IMMEDIATE_s4; \ |
| fprintf (out, " low=%ld, high=%ld, default=%ld", \ |
| (long) low, (long) high, (long) default_offset+oldpc); \ |
| for (; low <= high; low++) { \ |
| jint offset = IMMEDIATE_s4; \ |
| fprintf (out, "\n%10ld: %ld", (long)low, (long)(offset+oldpc)); } \ |
| } |
| |
| #define SPECIAL(OPERAND_TYPE, OPERAND_VALUE) \ |
| SPECIAL_##OPERAND_VALUE(OPERAND_TYPE) |
| |
| #define SPECIAL_IINC(OPERAND_TYPE) \ |
| i = saw_wide ? IMMEDIATE_u2 : IMMEDIATE_u1; \ |
| fprintf (out, " %d", i); \ |
| i = saw_wide ? IMMEDIATE_s2 : IMMEDIATE_s1; \ |
| saw_wide = 0; \ |
| fprintf (out, " %d", i) |
| |
| #define SPECIAL_WIDE(OPERAND_TYPE) \ |
| saw_wide = 1; |
| |
| #define SPECIAL_EXIT(OPERAND_TYPE) /* nothing */ |
| #define SPECIAL_ENTER(OPERAND_TYPE) /* nothing */ |
| #define SPECIAL_BREAK(OPERAND_TYPE) /* nothing */ |
| #define SPECIAL_THROW(OPERAND_TYPE) /* nothing */ |
| |
| #define IMPL(OPERAND_TYPE, OPERAND_VALUE) \ |
| fprintf (out, " %d", IMMEDIATE_u##OPERAND_VALUE) |
| |
| #define COND(OPERAND_TYPE, OPERAND_VALUE) \ |
| TEST(OPERAND_TYPE, OPERAND_VALUE) |
| |
| #include "javaop.def" |
| |
| load_store: |
| if (oldpc+1 == PC) /* nothing - local index implied by opcode */; |
| else |
| { |
| saw_wide = 0; |
| fprintf (out, " %ld", (long) INT_temp); |
| } |
| fputc ('\n', out); |
| break; |
| |
| default: |
| fprintf (out, "%3d: unknown(%3d)\n", oldpc, byte_ops[PC]); |
| } |
| } |
| } |