/*
* @(#)check_class.c 1.26 95/11/29
*
* 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.
*/
/*-
* code for verifying the date in a ClassClass structure for internal
* consistency.
*/
#include <ctype.h>
#include "oobj.h"
#include "interpreter.h"
#include "bool.h"
#include "utf.h"
#include "tree.h"
#include "sys_api.h"
extern bool_t verify_class_codes(ClassClass *cb);
static bool_t verify_constant_pool(ClassClass *cb);
static bool_t is_legal_fieldname(ClassClass *cb, char *name, int type);
static bool_t is_legal_method_signature(ClassClass *cb, char *name, char *signature);
static bool_t is_legal_field_signature(ClassClass *cb, char *name, char *signature);
static char *skip_over_fieldname(char *name, bool_t slash_okay);
static char *skip_over_field_signature(char *name, bool_t void_okay);
static void CCerror (ClassClass *cb, char *format, ...);
/* Argument for is_legal_fieldname */
enum { LegalClass, LegalField, LegalMethod };
bool_t
VerifyClass(ClassClass *cb)
{
bool_t result = TRUE;
struct methodblock *mb;
struct fieldblock *fb;
int32_t i;
if (CCIs(cb, Verified))
return TRUE;
if (!verify_constant_pool(cb))
return FALSE;
/* Make sure all the method names and signatures are okay */
for (i = (int32_t) cb->methods_count, mb = cb->methods; --i >= 0; mb++) {
char *name = mb->fb.name;
char *signature = mb->fb.signature;
if (! (is_legal_fieldname(cb, name, LegalMethod) &&
is_legal_method_signature(cb, name, signature)))
result = FALSE;
}
/* Make sure all the field names and signatures are okay */
for (i = (int32_t)cb->fields_count, fb = cb->fields; --i >= 0; fb++) {
if (! (is_legal_fieldname(cb, fb->name, LegalField) &&
is_legal_field_signature(cb, fb->name, fb->signature)))
result = FALSE;
}
/* Make sure we are not overriding any final methods or classes*/
if (cbSuperclass(cb)) {
ClassClass *super_cb;
struct methodblock *mb;
unsigned bitvector_size = (unsigned)(cbMethodTableSize(cb) + 31) >> 5;
long *bitvector = sysCalloc(bitvector_size, sizeof(long));
if (bitvector == NULL) {
return FALSE;
}
for (super_cb = unhand(cbSuperclass(cb)); ;
super_cb = unhand(cbSuperclass(super_cb))) {
sysAssert(cbMethodTableSize(cb) >= cbMethodTableSize(super_cb));
sysAssert(cbMethodTableSize(super_cb)); /* all resolved classes have *some* methods */
if (cbAccess(super_cb) & ACC_FINAL) {
CCerror(cb, "Class %s is subclass of final class %s",
classname(cb), classname(super_cb));
result = FALSE;
}
mb = cbMethods(super_cb);
for (i = (int32_t)super_cb->methods_count; --i >= 0; mb++) {
if (mb->fb.access & ACC_FINAL) {
uint32_t offset = mb->fb.u.offset;
sysAssert(offset < cbMethodTableSize(cb));
bitvector[offset >> 5] |= (1 << (offset & 0x1F));
}
}
if (cbSuperclass(super_cb) == 0) break;
}
for (i = (int32_t)cb->methods_count, mb = cbMethods(cb); --i >= 0; mb++) {
fb_offset_t offset = mb->fb.u.offset;
if ((offset > 0)
&& bitvector[offset >> 5] & (1 << (offset & 0x1F))) {
CCerror(cb, "Class %s overrides final method %s.%s",
classname(cb), mb->fb.name, mb->fb.signature);
result = FALSE;
}
}
sysFree(bitvector);
} else if (cb != classJavaLangObject) {
CCerror(cb, "Class %s does not have superclass", classname(cb));
result = FALSE;
}
if (result)
result = verify_class_codes(cb);
if (result)
CCSet(cb, Verified);
return result;
}
static bool_t
verify_constant_pool(ClassClass *cb)
{
union cp_item_type *cp = cb->constantpool;
cp_index_type cp_count = cb->constantpool_count;
unsigned char *type_table = cp[CONSTANT_POOL_TYPE_TABLE_INDEX].p;
cp_index_type i;
int type;
const int utf8_resolved = (CONSTANT_Utf8 | CONSTANT_POOL_ENTRY_RESOLVED);
/* Let's make two quick passes over the constant pool. The first one
* checks that everything is of the right type. */
for (i = 1; i < cp_count; i++) {
switch(type = type_table[i]) {
case CONSTANT_String:
case CONSTANT_Class: {
cp_index_type index = cp[i].i;
if ( (index < 1)
|| (index >= cp_count)
|| (type_table[index] != utf8_resolved)) {
CCerror(cb, "Bad index in constant pool #%d", i);
return FALSE;
}
break;
}
case CONSTANT_String | CONSTANT_POOL_ENTRY_RESOLVED:
/* This can only happen if a string is the "initial" value of
* some final static String. We assume that the checking has
* already been done.
*/
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
case CONSTANT_NameAndType: {
cp_index_type index = cp[i].u;
cp_index_type key1 = index >> 16;
cp_index_type key2 = index & 0xFFFF;
if (key1 < 1 || key1 >= cp_count
|| key2 < 1 || key2 >= cp_count) {
CCerror(cb, "Bad index in constant pool #%d", i);
return FALSE;
}
if (type == CONSTANT_NameAndType) {
if ( (type_table[key1] != utf8_resolved)
&& (type_table[key2] != utf8_resolved)) {
CCerror(cb, "Bad index in constant pool.");
return FALSE;
}
} else {
if ( ((type_table[key1] & CONSTANT_POOL_ENTRY_TYPEMASK)
!= CONSTANT_Class)
&& ((type_table[key2] != CONSTANT_NameAndType))) {
CCerror(cb, "Bad index in constant pool #%d", i);
return FALSE;
}
}
break;
}
case CONSTANT_Class | CONSTANT_POOL_ENTRY_RESOLVED:
/* The only entry that should be resolved if "this" */
if (cp[i].p == cb) break;
/* FALL THROUGH */
case CONSTANT_Fieldref | CONSTANT_POOL_ENTRY_RESOLVED:
case CONSTANT_Methodref | CONSTANT_POOL_ENTRY_RESOLVED:
case CONSTANT_InterfaceMethodref | CONSTANT_POOL_ENTRY_RESOLVED:
case CONSTANT_NameAndType | CONSTANT_POOL_ENTRY_RESOLVED:
CCerror(cb, "Improperly resolved constant pool #%d", i);
return FALSE;
case CONSTANT_Utf8 | CONSTANT_POOL_ENTRY_RESOLVED:
case CONSTANT_Integer | CONSTANT_POOL_ENTRY_RESOLVED:
case CONSTANT_Float | CONSTANT_POOL_ENTRY_RESOLVED:
break;
case CONSTANT_Long | CONSTANT_POOL_ENTRY_RESOLVED:
case CONSTANT_Double | CONSTANT_POOL_ENTRY_RESOLVED:
if ((i + 1 >= cp_count) ||
(type_table[i + 1] != CONSTANT_POOL_ENTRY_RESOLVED)) {
CCerror(cb, "Improper constant pool long/double #%d", i);
return FALSE;
} else {
i++;
break;
}
case CONSTANT_Integer:
case CONSTANT_Float:
case CONSTANT_Long:
case CONSTANT_Double:
case CONSTANT_Utf8:
CCerror(cb, "Improperly unresolved constant pool #%d", i);
return FALSE;
default:
CCerror(cb, "Illegal constant pool type at #%d", i);
return FALSE;
}
}
for (i = 1; i < cp_count; i++) {
switch(type = type_table[i]) {
case CONSTANT_Class: {
cp_index_type index = cp[i].i;
if (!is_legal_fieldname(cb, cp[index].cp, LegalClass))
return FALSE;
break;
}
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref: {
cp_index_type index = cp[i].u;
cp_index_type name_type_index = index & 0xFFFF;
cp_index_type name_type_key = cp[name_type_index].u;
cp_index_type name_index = name_type_key >> 16;
cp_index_type signature_index = name_type_key & 0xFFFF;
char *name = cp[name_index].cp;
char *signature = cp[signature_index].cp;
if (type == CONSTANT_Fieldref) {
if (! (is_legal_fieldname(cb, name, LegalField) &&
is_legal_field_signature(cb, name, signature)))
return FALSE;
} else {
if (! (is_legal_fieldname(cb, name, LegalMethod) &&
is_legal_method_signature(cb, name, signature)))
return FALSE;
}
break;
}
}
}
return TRUE;
}
/* Return true if the entire second argument consists of a legal fieldname
* (or classname, if the third argument is LegalClass).
*/
static bool_t
is_legal_fieldname(ClassClass *cb, char *name, int type)
{
bool_t result;
if (name[0] == '<') {
result = (type == LegalMethod) &&
((strcmp(name, "<init>") == 0) ||
(strcmp(name, "<clinit>") == 0));
} else {
char *p;
if (type == LegalClass && name[0] == SIGNATURE_ARRAY) {
p = skip_over_field_signature(name, FALSE);
} else {
p = skip_over_fieldname(name, type == LegalClass);
}
result = (p != 0 && p[0] == '\0');
}
if (!result) {
char *thing = (type == LegalField) ? "Field"
: (type == LegalMethod) ? "Method" : "Class";
CCerror(cb, "Illegal %s name \"%s\"", thing, name);
return FALSE;
} else {
return TRUE;
}
}
/* Return true if the entire string consists of a legal field signature */
static bool_t
is_legal_field_signature(ClassClass *cb, char *fieldname, char *signature)
{
char *p = skip_over_field_signature(signature, FALSE);
if (p != 0 && p[0] == '\0') {
return TRUE;
} else {
CCerror(cb, "Field \"%s\" has illegal signature \"%s\"",
fieldname, signature);
return FALSE;
}
}
static bool_t
is_legal_method_signature(ClassClass *cb, char *methodname, char *signature)
{
char *p = signature;
char *next_p;
/* The first character must be a '(' */
if (*p++ == SIGNATURE_FUNC) {
/* Skip over however many legal field signatures there are */
while ((next_p = skip_over_field_signature(p, FALSE)) != 0)
p = next_p;
/* The first non-signature thing better be a ')' */
if (*p++ == SIGNATURE_ENDFUNC) {
if (methodname[0] == '<') {
/* All internal methods must return void */
if ((p[0] == SIGNATURE_VOID) && (p[1] == '\0'))
return TRUE;
} else {
/* Now, we better just have a return value. */
next_p = skip_over_field_signature(p, TRUE);
if (next_p && next_p[0] == '\0')
return TRUE;
}
}
}
CCerror(cb, "Method \"%s\" has illegal signature \"%s\"",
methodname, signature);
return FALSE;
}
/* Take pointer to a string. Skip over the longest part of the string that
* could be taken as a fieldname. Allow '/' if slash_okay is TRUE.
*
* Return a pointer to just past the fieldname. Return NULL if no fieldname
* at all was found.
*/
static char *
skip_over_fieldname(char *name, bool_t slash_okay)
{
bool_t first;
char *p;
for (p = name, first = TRUE; ; first = FALSE) {
char *old_p = p;
unicode ch = next_utf2unicode(&p);
if (isalpha(ch) || (!first && isdigit(ch)) || (slash_okay && ch == '/' && !first)
|| ch == '_' || ch == '$' || ch >= 0x00c0) {
/* donothing */
} else {
return first ? 0 : old_p;
}
}
}
/* Take pointer to a string. Skip over the longest part of the string that
* could be taken as a field signature. Allow "void" if void_okay.
*
* Return a pointer to just past the signature. Return NULL if no legal
* signature is found.
*/
static char *
skip_over_field_signature(char *name, bool_t void_okay)
{
for (;;) {
switch (name[0]) {
case SIGNATURE_VOID:
if (!void_okay) return 0;
/* FALL THROUGH */
case SIGNATURE_BOOLEAN:
case SIGNATURE_BYTE:
case SIGNATURE_CHAR:
case SIGNATURE_SHORT:
case SIGNATURE_INT:
case SIGNATURE_FLOAT:
case SIGNATURE_LONG:
case SIGNATURE_DOUBLE:
return name + 1;
case SIGNATURE_CLASS: {
/* Skip over the classname, if one is there. */
char *p = skip_over_fieldname(name + 1, TRUE);
/* The next character better be a semicolon. */
if (p && p[0] == ';')
return p + 1;
return 0;
}
case SIGNATURE_ARRAY:
/* The rest of what's there better be a legal signature. */
name++;
void_okay = FALSE;
break;
default:
return 0;
}
}
}
static void
CCerror (ClassClass *cb, char *format, ...)
{
va_list args;
#define BUF_LEN 1024
char buf[BUF_LEN];
int i = 0;
i += jio_snprintf(buf, BUF_LEN, "VERIFIER CLASS ERROR %s:\n",
classname(cb));
va_start(args, format);
i += jio_vsnprintf(buf + i, BUF_LEN - i, format, args);
va_end(args);
i += jio_snprintf(buf + i, BUF_LEN - i, "\n");
#ifdef DEBUG
fprintf(stderr, buf);
#endif /* DEBUG */
PrintToConsole(buf);
}