/* javaclfl.c: Copyright (C) Codemist Ltd., 1996. */
#include <stdio.h>
#include <string.h>
#include "machine.h"
#include "tags.h"
#include "cslerror.h"
#include "externs.h"
#include "read.h"
#include "stream.h"
#include "arith.h"
#include "entries.h"
#include "javahost.h"
#include "javaglb.h"
/* #include "javatype.h" */
#define D_CLASSFILE 1
#define D_CLASSFILEIO 2
#define D_CLASSFILECODE 4
static int depth;
static unsigned32 rd1(FILE *f)
{ unsigned32 x = getc(f);
if (debugging & D_CLASSFILEIO) jdebug("rd1 %d", x);
return x;
}
static unsigned32 rd2(FILE *f)
{ unsigned32 x = getc(f);
x = x<<8 | getc(f);
if (debugging & D_CLASSFILEIO) jdebug("rd2 0x%.4x", x);
return x;
}
static unsigned32 rd4(FILE *f)
{ unsigned32 x = getc(f);
x = x<<8 | getc(f);
x = x<<8 | getc(f);
x = x<<8 | getc(f);
if (debugging & D_CLASSFILEIO) jdebug("rd4 0x%.8x", x);
return x;
}
static char *rd1string(FILE *f, unsigned32 n)
{ int i;
unsigned8 *p = (unsigned8 *)jmalloc((n+1) * sizeof(*p));
for (i=0; i<n; i++) p[i] = rd1(f);
p[n] = 0; /* @@@ ease C access for debugging (temp?) */
return (char *)p;
}
static char *rd2string(FILE *f, unsigned32 n)
{ int i;
unsigned16 *p = (unsigned16 *)jmalloc(n * sizeof(*p));
for (i=0; i<n; i++) p[i] = rd2(f);
return (char *)p;
}
static char *cpname(unsigned32 cpx, ClassFile *cf)
{ if (cpx < cf->constant_pool_count)
{ Cp_Info *cpitem = &cf->constant_pool[cpx];
if (cpitem->tag == CONSTANT_Utf8) return cpitem->u.utf8;
return "<non-utf8 const pool index>";
}
return "<bad const pool index>";
}
static Attribute_Sort lookup_attribute_sort(char *utf8, unsigned32 len)
{ /* Should use 'len' not rely on rd1string() zero padding. */
if (strcmp(utf8,"SourceFile") == 0) return ATT_SourceFile;
if (strcmp(utf8,"ConstantValue") == 0) return ATT_ConstantValue;
if (strcmp(utf8,"Code") == 0) return ATT_Code;
if (strcmp(utf8,"Exceptions") == 0) return ATT_Exceptions;
if (strcmp(utf8,"LineNumberTable") == 0) return ATT_LineNumberTable;
if (strcmp(utf8,"LocalVariableTable") == 0) return ATT_LocalVariableTable;
return ATT_unknown;
}
static int rdAttribute_Info(FILE *f, ClassFile *cf, unsigned32 n, Attribute_Info **res);
static int rdSourceFile_Attribute(FILE *f, ClassFile *cf,
SourceFile_Attribute **res)
{ SourceFile_Attribute *p = (SourceFile_Attribute *)jmalloc(sizeof(*p));
depth++;
p->sourcefile_index = rd2(f);
if (debugging & D_CLASSFILE)
jdebug("%*sAttribute Sourcefile '%s'",
depth*2, "", cpname(p->sourcefile_index, cf));
*res = p;
return (depth--, 0);
}
static int rdConstantValue_Attribute(FILE *f, ClassFile *cf,
ConstantValue_Attribute **res)
{ ConstantValue_Attribute *p = (ConstantValue_Attribute *)jmalloc(sizeof(*p));
depth++;
p->constantvalue_index = rd2(f);
if (debugging & D_CLASSFILE)
jdebug("%*sAttribute ConstantValue [%d]",
depth*2, "", p->constantvalue_index);
*res = p;
return (depth--, 0);
}
static int rdCode_Attribute(FILE *f, ClassFile *cf, Code_Attribute **res)
{ Code_Attribute *p = (Code_Attribute *)jmalloc(sizeof(*p));
unsigned32 i,n;
depth++;
p->max_stack = rd2(f);
p->max_locals = rd2(f);
p->code_length = n = rd4(f);
p->code = (unsigned8 *)jmalloc(n * sizeof(*p->code));
for (i=0; i<n; i++) p->code[i] = rd1(f);
p->exception_table_length = n = rd2(f);
p->exception_table = (Exception_Info *)jmalloc(n * sizeof(*p->exception_table));
for (i=0; i<n; i++)
{ Exception_Info *q = &p->exception_table[i];
q->start_pc = rd2(f);
q->end_pc = rd2(f);
q->handler_pc = rd2(f);
q->catch_type = rd2(f);
}
p->attributes_count = n = rd2(f);
if (debugging & D_CLASSFILE)
jdebug("%*sAttribute Code \
(stack %d, locals %d, bytes %d, exceptions %d)",
depth*2, "",
p->max_stack, p->max_locals,
p->code_length, p->exception_table_length);
if (debugging & D_CLASSFILECODE)
javadecode(p->code, p->code_length,
cf->constant_pool, cf->constant_pool_count);
if (rdAttribute_Info(f, cf, n, &p[i].attributes)) return 5;
*res = p;
return (depth--, 0);
}
static int rdExceptions_Attribute(FILE *f, ClassFile *cf,
Exceptions_Attribute **res)
{ Exceptions_Attribute *p = (Exceptions_Attribute *)jmalloc(sizeof(*p));
unsigned32 i,n;
depth++;
p->number_of_exceptions = n = rd2(f);
p->exception_index_table = (unsigned16 *)jmalloc(n * sizeof(*p->exception_index_table));
for (i=0; i<n; i++)
p->exception_index_table[i] = rd2(f);
if (debugging & D_CLASSFILE)
jdebug("%*sAttribute Exceptions (entries %d)",
depth*2, "", p->number_of_exceptions);
*res = p;
return (depth--, 0);
}
static int rdLineNumberTable_Attribute(FILE *f, ClassFile *cf,
LineNumberTable_Attribute **res)
{ LineNumberTable_Attribute *p = (LineNumberTable_Attribute *)jmalloc(sizeof(*p));
unsigned32 i,n;
depth++;
p->line_number_table_length = n = rd2(f);
p->line_number_table = (LineNumber_Info *)jmalloc(n * sizeof(*p->line_number_table));
for (i=0; i<n; i++)
{ LineNumber_Info *q = &p->line_number_table[i];
q->start_pc = rd2(f);
q->line_number = rd2(f);
}
if (debugging & D_CLASSFILE)
jdebug("%*sAttribute LineNumberTable (entries %d)",
depth*2, "", p->line_number_table_length);
*res = p;
return (depth--, 0);
}
static int rdLocalVariableTable_Attribute(FILE *f, ClassFile *cf,
LocalVariableTable_Attribute **res)
{ LocalVariableTable_Attribute *p = (LocalVariableTable_Attribute *)jmalloc(sizeof(*p));
unsigned32 i,n;
depth++;
p->local_variable_table_length = n = rd2(f);
p->local_variable_table = (LocalVariable_Info *)jmalloc(n * sizeof(*p->local_variable_table));
for (i=0; i<n; i++)
{ LocalVariable_Info *q = &p->local_variable_table[i];
q->start_pc = rd2(f);
q->length = rd2(f);
q->name_index = rd2(f);
q->signature_index = rd2(f);
q->slot = rd2(f);
}
if (debugging & D_CLASSFILE)
jdebug("%*sAttribute LocalVariableTable (entries %d)",
depth*2, "", p->local_variable_table_length);
*res = p;
return (depth--, 0);
}
/* 'Attributes' occur more than once in a Classfile, hence they are */
/* stored in *res (OK/NOK is return value), but reading Attributes */
/* also requires cf to access the previously read Cp_Info. */
static int rdAttribute_Info(FILE *f, ClassFile *cf, unsigned32 n, Attribute_Info **res)
{ Attribute_Info *p;
unsigned32 i;
if (n > 0xffff) return 1;
p = (Attribute_Info *)jmalloc(n * sizeof(*p));
if (debugging & D_CLASSFILE)
jdebug("%*sAttributes_Info %d", depth*2, "", n);
for (i=0; i<n; i++)
{ unsigned32 cpx = rd2(f);
unsigned32 len = rd4(f);
Cp_Info *cpitem = &cf->constant_pool[cpx];
if (cpitem->tag != CONSTANT_Utf8) return 3;
switch (p[i].sort = lookup_attribute_sort(cpitem->u.utf8, cpitem->len))
{
case ATT_Code:
if (rdCode_Attribute(f, cf, &p[i].uattr.code)) return 6;
break;
case ATT_SourceFile:
if (rdSourceFile_Attribute(f, cf, &p[i].uattr.sourcefile)) return 6;
break;
case ATT_ConstantValue:
if (rdConstantValue_Attribute(f, cf, &p[i].uattr.constantvalue))
return 6;
break;
case ATT_Exceptions:
if (rdExceptions_Attribute(f, cf, &p[i].uattr.exceptions))
return 6;
break;
case ATT_LineNumberTable:
if (rdLineNumberTable_Attribute(f, cf, &p[i].uattr.linenumbertable))
return 6;
break;
case ATT_LocalVariableTable:
if (rdLocalVariableTable_Attribute(f, cf, &p[i].uattr.localvariabletable))
return 6;
break;
default: /* currently ignore other attributes */
if (debugging & D_CLASSFILE)
jdebug("%*sAttribute '%s' unknown", (depth+1)*2, "",
cpitem->u.utf8);
while (len) rd1(f), len--;
}
}
*res = p;
return 0;
}
static int rdCp_Info(FILE *f, ClassFile *cf, unsigned32 n)
{ Cp_Info *p;
unsigned32 i, nn;
depth++;
if (n > 0xffff) return 1;
if (debugging & D_CLASSFILE) jdebug("Cp_Info %d", n);
p = (Cp_Info *)jmalloc(n * sizeof(*p));
/* Yes, the next line really is '1'! */
for (i=1; i<n; i++) switch (p[i].tag = rd1(f))
{
default: jsyserr("tag %d", p[i].tag); return 2;
case CONSTANT_Class:
p[i].u.val = rd2(f); break;
case CONSTANT_FieldRef:
case CONSTANT_MethodRef:
case CONSTANT_InterfaceMethodRef:
/* stuff name_and_type_index into len */
p[i].u.val = rd2(f); p[i].len = rd2(f); break;
case CONSTANT_String:
p[i].u.val = rd2(f); break;
case CONSTANT_Integer:
case CONSTANT_Float: p[i].u.val = rd4(f);
break;
case CONSTANT_Long:
case CONSTANT_Double: p[i].u.val = rd4(f);
i++; p[i].tag = CONSTANT_Xhalf; p[i].u.val = rd4(f);
break;
case CONSTANT_Utf8: if ((nn = rd2(f)) > 0xffff) nn = 0; /* EOF */
p[i].len = nn; p[i].u.utf8 = rd1string(f, nn);
break;
case CONSTANT_Unicode: if ((nn = rd2(f)) > 0xffff) nn = 0; /* EOF */
p[i].len = nn; p[i].u.utf8 = rd2string(f, nn);
break;
case CONSTANT_NameAndType:
/* stuff signature_index into len */
p[i].u.val = rd2(f); p[i].len = rd2(f); break;
}
cf->constant_pool = p;
return (depth--, 0);
}
static int rdInterface_Info(FILE *f, ClassFile *cf, unsigned32 n)
{ unsigned16 *p;
unsigned32 i;
depth++;
if (n > 0xffff) return 1;
if (debugging & D_CLASSFILE) jdebug("Interface_Info %d", n);
p = (unsigned16 *)jmalloc(n * sizeof(*p));
for (i=0; i<n; i++) p[i] = rd2(f);
cf->interfaces = p;
return (depth--, 0);
}
static int rdField_Info(FILE *f, ClassFile *cf, unsigned32 n)
{ Field_Info *p;
unsigned32 i;
depth++;
if (n > 0xffff) return 1;
if (debugging & D_CLASSFILE) jdebug("Field_Info %d", n);
p = (Field_Info *)jmalloc(n * sizeof(*p));
for (i=0; i<n; i++)
{ unsigned32 nn;
p[i].access_flags = rd2(f);
p[i].name_index = rd2(f);
p[i].signature_index = rd2(f);
p[i].attributes_count = nn = rd2(f);
if (debugging & D_CLASSFILE)
jdebug(" Field '%s' signature '%s' flags 0x%x",
cpname(p[i].name_index, cf),
cpname(p[i].signature_index, cf),
p[i].access_flags);
if (rdAttribute_Info(f, cf, nn, &p[i].attributes)) return 4;
}
cf->fields = p;
return (depth--, 0);
}
static int rdMethod_Info(FILE *f, ClassFile *cf, unsigned32 n)
{ Method_Info *p;
unsigned32 i;
depth++;
if (n > 0xffff) return 1;
if (debugging & D_CLASSFILE) jdebug("Method_Info %d", n);
p = (Method_Info *)jmalloc(n * sizeof(*p));
for (i=0; i<n; i++)
{ unsigned32 nn;
p[i].access_flags = rd2(f);
p[i].name_index = rd2(f);
p[i].signature_index = rd2(f);
p[i].attributes_count = nn = rd2(f);
if (debugging & D_CLASSFILE)
jdebug(" Method '%s' signature '%s' flags 0x%x",
cpname(p[i].name_index, cf),
cpname(p[i].signature_index, cf),
p[i].access_flags);
if (rdAttribute_Info(f, cf, nn, &p[i].attributes)) return 4;
}
cf->methods = p;
return (depth--, 0);
}
ClassFile *rdClassFile(char *name)
{ FILE *f = fopen(name, "rb");
ClassFile *cf = rdClassFILE1(f, name);
fclose(f);
return cf;
}
ClassFile *rdClassFILE1(FILE *f, char *name)
{
ClassFile *cf;
unsigned32 n;
depth = 0;
if (f == 0)
{ jsyserr("cannot read '%s'", name); return 0; }
if (rd4(f) != JAVA_MAGIC)
{ jsyserr("not class file '%s'", name); return 0; }
if ((n = rd2(f)) > JAVA_THIS_MIN)
jdebug("ClassFile '%s' wrong minor vsn %d", name, n);
if ((n = rd2(f)) != JAVA_THIS_MAJ)
jdebug("ClassFile '%s' wrong major vsn %d", name, n);
cf = (ClassFile *)jmalloc(sizeof(ClassFile));
cf->constant_pool_count = n = rd2(f);
if (rdCp_Info(f, cf, n)) goto corrupt;
cf->access_flags = rd2(f);
cf->this_class = rd2(f);
cf->super_class = rd2(f);
cf->interfaces_count = n = rd2(f);
if (rdInterface_Info(f, cf, n)) goto corrupt;
cf->fields_count = n = rd2(f);
if (rdField_Info(f, cf, n)) goto corrupt;
cf->methods_count = n = rd2(f);
if (rdMethod_Info(f, cf, n)) goto corrupt;
cf->attributes_count = n = rd2(f);
if (rdAttribute_Info(f, cf, n, &cf->attributes)) goto corrupt;
if (rd1(f) != EOF) jdebug("junk at end of ClassFile '%s'", name);
return cf;
corrupt:
jsyserr("ClassFile '%s' corrupted", name);
return 0;
}