netscape-revival
sun-java/runtime/decode.c
/*
* @(#)decode.c 1.57 95/11/16
*
* Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
/*-
* decode an java object file
*/
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "typecodes.h"
#include "opcodes.h"
#include "oobj.h"
#include "decode.h"
#include "tree.h"
#include "typecodes.h"
#include "signature.h"
#include <stddef.h>
#include "interpreter.h"
#include "utf.h"
#include <ctype.h>
#include "byteorder.h"
#include "sys_api.h"
bool_t VerifyClass(ClassClass *cb);
#undef putchar
#define putchar(ch) printf("%c", ch)
/* initialize opnames[256]; */
#include "opcodes.init"
extern char *progname;
static char *currentclassname;
int decode_verbose;
int retcode;
static void PrintConstant(ClassClass *cb, int index);
static void PrintUtfReadable(FILE *fd, char *utfstring);
static void PrintInitialValue(struct fieldblock *fb);
static char *PrintableClassname(char *classname);
char *
SprintReverseType(char *s, char *fname, char *out)
{
char buf[1000];
register char *tn = "?";
if (fname == 0)
fname = "";
switch (*s++) {
default:
switch (s[-1]) {
case SIGNATURE_BYTE:
tn = "byte";
break;
case SIGNATURE_SHORT:
tn = "short";
break;
case SIGNATURE_CHAR:
tn = "char";
break;
case SIGNATURE_ENUM:
tn = "enum";
break;
case SIGNATURE_FLOAT:
tn = "float";
break;
case SIGNATURE_DOUBLE:
tn = "double";
break;
case SIGNATURE_INT:
tn = "int";
break;
case SIGNATURE_LONG:
tn = "long";
break;
case SIGNATURE_CLASS:
tn = buf;
if (PrintAsC) {
register char *sp = "struct Class";
while (*sp)
*tn++ = *sp++;
}
for (; *s && *s != SIGNATURE_ENDCLASS;) {
char c = *s++;
*tn++ = (c == '/') ? '.' : c;
}
*tn = 0;
if (*s == SIGNATURE_ENDCLASS)
s++;
tn = buf;
break;
case SIGNATURE_VOID:
tn = "void";
break;
case SIGNATURE_BOOLEAN:
tn = "boolean";
break;
}
sprintf(out, *fname ? "%s %s" : "%s", tn, fname);
break;
case 0:
strcpy(out, fname);
s--;
break;
case SIGNATURE_ARRAY:
if (*s == SIGNATURE_CHAR) {
s++;
if (PrintAsC)
sprintf(out, *fname ? "char *%s" : "char *", fname);
else
sprintf(out, *fname ? "char %s[]" : "char []", fname);
} else {
sprintf(buf, "%s[]", fname);
s = SprintReverseType(s, buf, out);
}
break;
case SIGNATURE_FUNC:
{
int constructor = strcmp(fname, "<init>") == 0;
sprintf(buf, "%s%s(",
currentclassname && PrintAsC ?
PrintableClassname(currentclassname) : "",
constructor ? PrintAsC ? "Initializor" :
PrintableClassname(currentclassname) : fname);
fname = buf + strlen(buf);
if (PrintAsC && currentclassname) {
sprintf(fname, "Class%s%s",
PrintableClassname(currentclassname),
*s != SIGNATURE_ENDFUNC ? "," : "");
fname += strlen(fname);
}
while (*s != SIGNATURE_ENDFUNC && *s) {
s = SprintReverseType(s, (char *) 0, fname);
fname += strlen(fname);
if (*s != SIGNATURE_ENDFUNC)
*fname++ = ',';
}
*fname++ = ')';
*fname++ = 0;
s = SprintReverseType(constructor ? "" : *s ? s + 1 : s, buf, out);
}
break;
}
return s;
}
static void
PrintType(char *signature, char *fieldname)
{
char buf[1000];
(void) SprintReverseType(signature, fieldname, buf);
printf("%s", buf);
}
void
PrintEscapedString(unsigned short *s, long len)
{
register c;
while (--len >= 0) {
if ((c = *s++) >= 040 && c < 0177)
printf("%c", c);
else
switch (c) {
case '\n':
printf("\\n");
break;
case '\t':
printf("\\t");
break;
case '\r':
printf("\\r");
break;
case '\b':
printf("\\b");
break;
case '\f':
printf("\\f");
break;
default:
printf("\\<%X>", c);
break;
}
}
}
void
PrintCodeSequence(struct methodblock *mb)
{
ClassClass *cb = fieldclass(&mb->fb);
unsigned char *initial_pc = mb->code;
unsigned char *max_pc = initial_pc + mb->code_length;
unsigned char *pc;
for (pc = initial_pc; pc < max_pc; ) {
int opcode = pc[0];
int base_offset = pc - initial_pc;
if (opcode == opc_wide)
opcode = 256 + pc[1];
printf("%4d %s", pc - initial_pc, opnames[opcode & 0xFF]);
switch (opcode) {
case opc_aload: case opc_astore:
case opc_fload: case opc_fstore:
case opc_iload: case opc_istore:
case opc_lload: case opc_lstore:
case opc_dload: case opc_dstore:
case opc_ret:
printf(" %d", pc[1]);
pc += 2;
break;
case opc_aload + 256: case opc_astore + 256:
case opc_fload + 256: case opc_fstore + 256:
case opc_iload + 256: case opc_istore + 256:
case opc_lload + 256: case opc_lstore + 256:
case opc_dload + 256: case opc_dstore + 256:
case opc_ret + 256:
printf("_w %d", (pc[2] << 8) | pc[3]);
pc += 4;
break;
case opc_iinc:
printf(" %d %d", pc[1], ((char *)pc)[2]);
pc += 3;
break;
case opc_iinc + 256:
printf("_w %d %d", ((pc[2] << 8) + pc[3]),
(short) ((pc[4] << 8) | pc[5]));
pc += 6;
break;
case opc_tableswitch:{
long *ltbl = (long *) REL_ALIGN(initial_pc, pc-initial_pc + 1); /* UCALIGN((int)pc + 1); */
int default_skip = ntohl(ltbl[0]); /* default skip pamount */
int low = ntohl(ltbl[1]);
int high = ntohl(ltbl[2]);
int count = high - low;
int i;
printf(" %d to %d: default=%d",
low, high, default_skip + base_offset);
for (i = 0, ltbl += 3; i <= count; i++, ltbl++)
printf("\n\t%5d: %d", i + low,
ntohl(ltbl[0]) + base_offset);
pc = (unsigned char *) ltbl;
break;
}
case opc_lookupswitch:{
register long *ltbl = (long *) REL_ALIGN(initial_pc, pc-initial_pc + 1); /* UCALIGN((int)pc + 1); */
int skip = ntohl(ltbl[0]); /* default skip amount */
int npairs = ntohl(ltbl[1]);
int i;
printf(" %d: default=%d", npairs, skip + base_offset);
for (i = 0, ltbl += 2; i < npairs; i++, ltbl += 2)
printf("\n\t%5d: %d", ntohl(ltbl[0]),
ntohl(ltbl[1]) + base_offset);
pc = (unsigned char *) ltbl;
break;
}
case opc_newarray:
switch (pc[1]) {
case T_INT: printf(" int"); break;
case T_LONG: printf(" long"); break;
case T_FLOAT: printf(" float"); break;
case T_DOUBLE: printf(" double"); break;
case T_CHAR: printf(" char"); break;
case T_SHORT: printf(" short"); break;
case T_BYTE: printf(" byte"); break;
default: printf(" BOGUS TYPE"); break;
}
pc += 2;
break;
case opc_anewarray: {
int index = (pc[1] << 8) | pc[2];
printf(" class #%d ", index);
PrintConstant(cb, index);
pc += 3;
break;
}
case opc_sipush:
printf(" %d", (short) ((pc[1] << 8) | pc[2]));
pc += 3;
break;
case opc_bipush:
printf(" %d", (char) pc[1]);
pc += 2;
break;
case opc_ldc: {
int index = pc[1];
printf(" #%d ", index);
PrintConstant(cb, index);
pc += 2;
break;
}
case opc_ldc_w: case opc_ldc2_w:
case opc_instanceof: case opc_checkcast:
case opc_new:
case opc_putstatic: case opc_getstatic:
case opc_putfield: case opc_getfield:
case opc_invokevirtual:
case opc_invokenonvirtual:
case opc_invokestatic: {
int index = (pc[1] << 8) | pc[2];
printf(" #%d ", index);
PrintConstant(cb, index);
pc += 3;
break;
}
case opc_invokeinterface: {
int index = (pc[1] << 8) | pc[2];
printf(" (args %d) #%d ", pc[3], index);
PrintConstant(cb, index);
pc += 5;
break;
}
case opc_multianewarray: {
int index = (pc[1] << 8) | pc[2];
printf(" #%d dim #%d ", index, pc[3]);
PrintConstant(cb, index);
pc += 4;
break;
}
case opc_jsr: case opc_goto:
case opc_ifeq: case opc_ifge: case opc_ifgt:
case opc_ifle: case opc_iflt: case opc_ifne:
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge:
case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt:
case opc_if_acmpeq: case opc_if_acmpne:
case opc_ifnull: case opc_ifnonnull:
printf(" %d", base_offset + (short) ((pc[1] << 8) | pc[2]));
pc += 3;
break;
case opc_jsr_w:
case opc_goto_w:
printf(" %d", base_offset +
(long) ((pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) + pc[4]));
pc += 5;
break;
default:
pc++;
break;
}
putchar('\n');
}
if (mb->exception_table_length > 0) {
struct CatchFrame *cf = mb->exception_table;
int cnt = mb->exception_table_length;
ClassClass *cb = fieldclass(&mb->fb);
printf("Exception table:\n from to target type\n");
for (; --cnt >= 0; cf += 1) {
short catchType = cf->catchType;
printf(" %4d %4d %4d ",
cf->start_pc, cf->end_pc, cf->handler_pc);
if (cf->catchType == 0)
printf("any\n");
else {
PrintConstant(cb, catchType);
printf("\n");
}
}
}
}
void
PrintLocalVariableTable(struct methodblock *mb)
{
ClassClass *cb = fieldclass(&mb->fb);
struct localvar *lv = mb->localvar_table;
union cp_item_type *constant_pool = cb->constantpool;
int i;
for (i = 0; i < (int)mb->localvar_table_length; i++, lv++) {
printf(" ");
PrintType(constant_pool[lv->sigoff].cp, constant_pool[lv->nameoff].cp);
printf(" pc=%d, length=%d, slot=%d\n", lv->pc0, lv->length, lv->slot);
}
}
void
PrintLineNumberTable(struct methodblock *mb)
{
struct lineno *ln = mb->line_number_table;
int i;
for (i = 0; i < (int)mb->line_number_table_length; i++, ln++) {
printf(" line %d: %d\n", ln->line_number, ln->pc);
}
}
void PrintConstant(ClassClass *cb, int index)
{
union cp_item_type *constant_pool = cb->constantpool;
unsigned char *type_data = constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].p;
Java8 t;
switch(type_data[index] & CONSTANT_POOL_ENTRY_TYPEMASK) {
case CONSTANT_Utf8:
printf("<\"%s\">", constant_pool[index].cp);
break;
case CONSTANT_Integer:
printf("<Integer %d>", constant_pool[index].i);
break;
case CONSTANT_Float:
printf("<Real %f>", *(float *)&constant_pool[index]);
break;
case CONSTANT_Long: {
char buf[32];
int64_t value = GET_INT64(t, &constant_pool[index]);
printf("<Long %s>", int642CString(value, buf, sizeof(buf)));
break;
}
case CONSTANT_Double:
printf("<Double %f>", GET_DOUBLE(t,&constant_pool[index]));
break;
case CONSTANT_Class:
printf("<Class %s>", PrintableClassname(
GetClassConstantClassName(constant_pool, index)));
break;
case CONSTANT_String: {
unsigned key = constant_pool[index].i;
printf("<String \"");
PrintUtfReadable(stdout, constant_pool[key].cp);
printf("\">");
break;
}
case CONSTANT_Methodref:
case CONSTANT_Fieldref:
case CONSTANT_InterfaceMethodref: {
unsigned type = type_data[index];
unsigned key = constant_pool[index].i;
unsigned classkey = key >> 16;
unsigned nametypekey = key & 0xFFFF;
unsigned nametypeindex = constant_pool[nametypekey].i;
unsigned fieldnameindex = nametypeindex >> 16;
unsigned fieldtypeindex = nametypeindex & 0xFFFF;
char *type_name = type == CONSTANT_Methodref ? "Method"
: type == CONSTANT_Fieldref ? "Field"
: "InterfaceMethod";
printf("<%s %s.%s%s%s>",
type_name,
PrintableClassname(GetClassConstantClassName(
constant_pool, classkey)),
constant_pool[fieldnameindex].cp,
type == CONSTANT_Fieldref ? " " : "",
constant_pool[fieldtypeindex].cp);
break;
}
case CONSTANT_NameAndType: {
unsigned key = constant_pool[index].i;
unsigned fieldnameindex = key >> 16;
unsigned fieldtypeindex = key & 0xFFFF;
printf("<NameAndType %s %s>",
constant_pool[fieldnameindex].cp,
constant_pool[fieldtypeindex].cp);
break;
default:
printf("<Unknown %d (0x%x)>", type_data[index], type_data[index]);
break;
}
}
}
void
PrintClassSequence(register ClassClass * cb)
{
register struct fieldblock *fb;
register struct methodblock *mb;
register nslots;
currentclassname = classname(cb);
printf("%s%s%s%s%s%s %s ",
(cb->access & ACC_PROTECTED) ? "protected " : "",
(cb->access & ACC_PUBLIC) ? "public " : "",
(cb->access & ACC_PRIVATE) ? "private " : "",
(cb->access & ACC_FINAL) ? "final ": "",
(cb->access & ACC_ABSTRACT) ? "abstract ": "",
(cb->access & ACC_INTERFACE) ? "interface" : "class",
PrintableClassname(currentclassname));
if (cb->super_name) {
printf("extends %s ", PrintableClassname(classsupername(cb)));
}
if (cb->implements_count > 0) {
short *p = cb->implements;
unsigned i;
unsigned count = cb->implements_count;
for (i = 0; i < count; i++) {
int class_index = p[i];
int string_index = cb->constantpool[class_index].i;
char *class_name = cb->constantpool[string_index].cp;
printf("%s %s", i == 0 ? "implements" : ",", class_name);
}
printf(" ");
}
printf("{\n");
for (nslots = cb->fields_count, fb = cbFields(cb); --nslots >= 0; fb++) {
if ((fb->access & ACC_PRIVATE) == 0 || PrintPrivate) {
printf(" %s%s%s%s%s%s%s",
(fb->access & ACC_PUBLIC) ? "public " : "",
(fb->access & ACC_PROTECTED) ? "protected " : "",
(fb->access & ACC_PRIVATE) ? "private " : "",
(fb->access & ACC_STATIC) ? "static " : "",
(fb->access & ACC_FINAL) ? "final " : "",
(fb->access & ACC_TRANSIENT) ? "transient " : "",
(fb->access & ACC_THREADSAFE) ? "threadsafe " : "");
PrintType(fieldsig(fb), fieldname(fb));
if (fb->access & ACC_VALKNOWN)
PrintInitialValue(fb);
printf(";\n");
}
}
for (nslots = cb->methods_count, mb = cbMethods(cb); --nslots >= 0; mb++) {
fb = &mb->fb;
if ((fb->access & ACC_PRIVATE) == 0 || PrintPrivate) {
printf(" %s%s%s%s%s%s%s%s",
(fb->access & ACC_PROTECTED) ? "protected " : "",
(fb->access & ACC_PUBLIC) ? "public " : "",
(fb->access & ACC_PRIVATE) ? "private " : "",
(fb->access & ACC_FINAL) ? "final ": "",
(fb->access & ACC_NATIVE) ? "native ": "",
(fb->access & ACC_ABSTRACT) ? "abstract ": "",
(fb->access & ACC_STATIC) ? "static " : "",
(fb->access & ACC_SYNCHRONIZED) ? "synchronized ": ""
);
PrintType(fieldsig(fb), fieldname(fb));
printf(";\n");
if (decode_verbose)
printf("\t/* Stack=%d, Locals=%d, Args_size=%d */\n",
mb->maxstack, mb->nlocals, mb->args_size);
}
}
if (PrintConstantPool) {
printf("\n");
for (nslots = 1; nslots < cb->constantpool_count; nslots++) {
printf("Constant %d = ", nslots);
PrintConstant(cb, nslots);
printf("\n");
}
}
if (PrintCode) {
printf("\n");
for (nslots = cb->methods_count, mb = cbMethods(cb);
--nslots >= 0; mb++) {
fb = &mb->fb;
if (mb->code_length > 0) {
printf("Method ");
PrintType(fieldsig(fb), fieldname(fb));
printf("\n");
PrintCodeSequence(mb);
printf("\n");
}
}
}
if (PrintLineTable) {
printf("\n");
for (nslots = cb->methods_count, mb = cbMethods(cb);
--nslots >= 0; mb++) {
fb = &mb->fb;
if (mb->line_number_table_length > 0) {
printf("Line numbers for method ");
PrintType(fieldsig(fb), fieldname(fb));
printf("\n");
PrintLineNumberTable(mb);
printf("\n");
}
}
}
if (PrintLocalTable) {
printf("\n");
for (nslots = cb->methods_count, mb = cbMethods(cb);
--nslots >= 0; mb++) {
fb = &mb->fb;
if (mb->localvar_table_length > 0) {
printf("Local variables for method ");
PrintType(fieldsig(fb), fieldname(fb));
printf("\n");
PrintLocalVariableTable(mb);
printf("\n");
}
}
}
printf("}\n");
}
static void
PrintInitialValue(struct fieldblock *fb)
{
Java8 t;
switch (fb->signature[0]) {
case SIGNATURE_FLOAT:
printf(" = %g", *(float *)normal_static_address(fb));
break;
case SIGNATURE_DOUBLE:
printf(" = %f", GET_DOUBLE(t, twoword_static_address(fb)));
break;
case SIGNATURE_LONG: {
char buf[32];
printf(" = %s", int642CString(GET_INT64(t, twoword_static_address(fb)),
buf, sizeof(buf)));
break;
}
case SIGNATURE_CLASS:
/* ResolveClassStringConstant() below causes the UTF string to be
stored into normal_static_address */
printf(" = \"");
PrintUtfReadable(stdout, *(char **)normal_static_address(fb));
printf("\"");
break;
default:
printf(" = %d", *(int *)normal_static_address(fb));
break;
}
}
void
PrintClassSequenceAsC(register ClassClass * cb)
{
register struct fieldblock *fb;
register struct methodblock *mb;
register nslots;
currentclassname = classname(cb);
printf("\n\ntypedef struct Class%s {\n struct ClassObject header;\n",
PrintableClassname(currentclassname));
for (nslots = cb->fields_count, fb = cbFields(cb); --nslots >= 0; fb++)
if ((fb->access & ACC_PRIVATE) == 0 || PrintPrivate) {
printf(" ");
PrintType(fieldsig(fb), fieldname(fb));
printf(";\n");
}
printf("} Class%s;\n", PrintableClassname(currentclassname));
for (nslots = cb->methods_count, mb = cbMethods(cb); --nslots >= 0; mb++) {
fb = &mb->fb;
if (mb->code_length == 0) {
printf(" extern ");
PrintType(fieldsig(fb), fieldname(fb));
printf(";\n");
}
}
}
void
DecodeFile(fn)
register char *fn;
{
ClassClass *cb;
SkipSourceChecks = TRUE;
cb = FindClass(0, fn, verifyclasses ? TRUE : FALSE);
if (cb == NULL) {
retcode = 1;
return;
}
if (verifyclasses != VERIFY_NONE) {
char *class_name = PrintableClassname(classname(cb));
bool_t result;
if (decode_verbose) {
printf("%s %s ",
(cb->access & ACC_INTERFACE) ? "interface" : "class",
class_name);
if (cb->super_name)
printf("extends %s ", classsupername(cb));
printf("\n");
}
result = VerifyClass(cb);
printf("Class %s %s\n", class_name, result ? "succeeds" : "fails");
retcode = 0;
return;
}
if (PrintAsC) {
char define[1000];
register char *s, *d;
register c;
if (cb->source_name)
s = classsrcname(cb);
else s = "SourceFileInfoMissing";
printf("/* Header generated from %s */\n\n", s);
for (d = define; (c = *s++);)
if (('a' <= c && c <= 'z')
|| ('A' <= c && c <= 'Z')
|| ('0' <= c && c <= '9'))
*d++ = c;
else
*d++ = '_';
*d++ = 0;
printf("#ifndef _FN_%s\n#define _FN_%s\n\n", define, define);
} else if (cb->source_name)
printf("Compiled from %s\n", classsrcname(cb));
if (PrintAsC) {
PrintClassSequenceAsC(cb);
printf("#endif\n");
} else {
PrintClassSequence(cb);
}
}
static char *PrintableClassname(char *class_name)
{
char *p;
static char class_copy[257];
strncpy(class_copy, class_name, 256);
/* Convert all slashes in the classname to periods */
for (p = class_copy; ((p = strchr(p, '/')) != 0); *p++ = '.');
return class_copy;
}
static void PrintUtfReadable(FILE *fd, char *utfstring)
{
if (is_simple_utf(utfstring)) {
fprintf(fd, "%s", utfstring);
} else {
while (*utfstring != 0) {
unicode ch = next_utf2unicode(&utfstring);
if ((ch <= 0x7f) && isprint(ch)) {
putc(ch, fd);
} else {
switch (ch) {
case '\n': fprintf(fd, "\\n"); break;
case '\t': fprintf(fd, "\\t"); break;
case '\r': fprintf(fd, "\\r"); break;
case '\b': fprintf(fd, "\\b"); break;
case '\f': fprintf(fd, "\\f"); break;
default: fprintf(fd, "\\%o", ch); break;
}
}
}
}
}
/* ResolveClassStringConstant is called by the classloader to resolve the
* constant pool entry, before assigning a value to a final [i.e. constant]
* String.
*
* For the decoder, we just need a pointer to the UTF string.
*/
bool_t
ResolveClassStringConstant(ClassClass *cb, cp_index_type index, struct execenv *ee)
{
union cp_item_type *constant_pool = cbConstantPool(cb);
unsigned char *type_table = constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].p;
if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, index)) {
unsigned value_index = constant_pool[index].i;
char *value = constant_pool[value_index].cp;
constant_pool[index].cp = value;
CONSTANT_POOL_TYPE_TABLE_SET_RESOLVED(type_table, index);
}
return TRUE;
}
void
DumpThreads() { }
HObject *
AllocHandle(struct methodtable *mptr, ClassObject *p)
{
JHandle *h = sysMalloc(sizeof(JHandle));
h->methods = mptr;
h->obj = (ClassObject *) p;
return (HObject *) h;
}
long
CallInterpreted(register struct methodblock * mb, void *obj,...)
{
return 0;
}