#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "java-tree.h"
#include "javaop.h"
#include "java-opcodes.h"
#include "jcf.h"
#include "java-except.h"
#include "toplev.h"
static void push_pending_label (tree);
static tree merge_types (tree, tree);
static const char *check_pending_block (tree);
static void type_stack_dup (int, int);
static char *pop_argument_types (tree);
extern int stack_pointer;
tree current_subr;
tree pending_blocks;
static void
push_pending_label (tree target_label)
{
if (! LABEL_CHANGED (target_label))
{
LABEL_PENDING_CHAIN (target_label) = pending_blocks;
pending_blocks = target_label;
LABEL_CHANGED (target_label) = 1;
}
}
static const char *
check_pending_block (tree target_label)
{
int changed = merge_type_state (target_label);
if (changed)
{
if (changed < 0)
return "types could not be merged";
push_pending_label (target_label);
}
if (current_subr == NULL_TREE)
{
if (LABEL_IN_SUBR (target_label))
return "might transfer control into subroutine";
}
else
{
if (LABEL_IN_SUBR (target_label))
{
if (LABEL_SUBR_START (target_label) != current_subr)
return "transfer out of subroutine";
}
else if (! LABEL_VERIFIED (target_label))
{
LABEL_IN_SUBR (target_label) = 1;
LABEL_SUBR_START (target_label) = current_subr;
}
else
return "transfer out of subroutine";
}
return NULL;
}
static int
subroutine_nesting (tree label)
{
int nesting = 0;
while (label != NULL_TREE && LABEL_IN_SUBR (label))
{
if (! LABEL_IS_SUBR_START (label))
label = LABEL_SUBR_START (label);
label = LABEL_SUBR_CONTEXT (label);
nesting++;
}
return nesting;
}
static tree
merge_types (tree type1, tree type2)
{
if (type1 == type2)
return type1;
if (type1 == TYPE_UNKNOWN || type2 == TYPE_UNKNOWN
|| type1 == TYPE_RETURN_ADDR || type2 == TYPE_RETURN_ADDR)
return TYPE_UNKNOWN;
if (TREE_CODE (type1) == POINTER_TYPE && TREE_CODE (type2) == POINTER_TYPE)
{
int depth1, depth2;
tree tt1, tt2;
if (type1 == ptr_type_node || type2 == object_ptr_type_node)
return type2;
if (type2 == ptr_type_node || type1 == object_ptr_type_node)
return type1;
tt1 = TREE_TYPE (type1);
tt2 = TREE_TYPE (type2);
if (!TYPE_SIZE (tt1))
{
load_class (tt1, 1);
safe_layout_class (tt1);
}
if (!TYPE_SIZE (tt2))
{
load_class (tt2, 1);
safe_layout_class (tt2);
}
if (TYPE_ARRAY_P (tt1) || TYPE_ARRAY_P (tt2))
{
if (TYPE_ARRAY_P (tt1) == TYPE_ARRAY_P (tt2))
{
tree el_type1 = TYPE_ARRAY_ELEMENT (tt1);
tree el_type2 = TYPE_ARRAY_ELEMENT (tt2);
tree el_type = NULL_TREE;
if (el_type1 == el_type2)
el_type = el_type1;
else if (TREE_CODE (el_type1) == POINTER_TYPE
&& TREE_CODE (el_type2) == POINTER_TYPE)
el_type = merge_types (el_type1, el_type2);
if (el_type != NULL_TREE)
{
HOST_WIDE_INT len1 = java_array_type_length (tt1);
HOST_WIDE_INT len2 = java_array_type_length (tt2);
if (len1 != len2)
len1 = -1;
else if (el_type1 == el_type2)
return type1;
return promote_type (build_java_array_type (el_type, len1));
}
}
return object_ptr_type_node;
}
if (CLASS_INTERFACE (TYPE_NAME (tt1)))
{
if (CLASS_INTERFACE (TYPE_NAME (tt2)))
{
return object_ptr_type_node;
}
else
{
if (can_widen_reference_to (tt2, tt1))
return type1;
else
return object_ptr_type_node;
}
}
else if (CLASS_INTERFACE (TYPE_NAME (tt2)))
{
if (can_widen_reference_to (tt1, tt2))
return type2;
else
return object_ptr_type_node;
}
type1 = tt1;
type2 = tt2;
depth1 = class_depth (type1);
depth2 = class_depth (type2);
for ( ; depth1 > depth2; depth1--)
type1 = BINFO_TYPE (BINFO_BASE_BINFO (TYPE_BINFO (type1), 0));
for ( ; depth2 > depth1; depth2--)
type2 = BINFO_TYPE (BINFO_BASE_BINFO (TYPE_BINFO (type2), 0));
while (type1 != type2)
{
type1 = BINFO_TYPE (BINFO_BASE_BINFO (TYPE_BINFO (type1), 0));
type2 = BINFO_TYPE (BINFO_BASE_BINFO (TYPE_BINFO (type2), 0));
}
return promote_type (type1);
}
if (INTEGRAL_TYPE_P (type1) && INTEGRAL_TYPE_P (type2)
&& TYPE_PRECISION (type1) <= 32 && TYPE_PRECISION (type2) <= 32)
return int_type_node;
return TYPE_UNKNOWN;
}
int
merge_type_state (tree label)
{
int nlocals = DECL_MAX_LOCALS (current_function_decl);
int cur_length = stack_pointer + nlocals;
tree vec = LABEL_TYPE_STATE (label);
tree return_map;
if (vec == NULL_TREE)
{
vec = make_tree_vec (cur_length);
LABEL_TYPE_STATE (label) = vec;
while (--cur_length >= 0)
TREE_VEC_ELT (vec, cur_length) = type_map[cur_length];
return 1;
}
else
{
int i;
int changed = 0;
if (LABEL_IS_SUBR_START (label) && LABEL_VERIFIED (label)
&& current_subr != label)
return_map = LABEL_RETURN_TYPE_STATE (label);
else
return_map = NULL_TREE;
if (TREE_VEC_LENGTH (vec) != cur_length)
{
return -1;
}
for (i = 0; i < cur_length; i++)
{
tree old_type = TREE_VEC_ELT (vec, i);
tree new_type = merge_types (old_type, type_map[i]);
if (TREE_VEC_ELT (vec, i) != new_type)
{
if (return_map == NULL_TREE || i >= nlocals
|| TREE_VEC_ELT (return_map, i) != TYPE_UNUSED
|| (TYPE_IS_WIDE (new_type)
&& TREE_VEC_ELT (return_map, i+1) != TYPE_UNUSED))
changed = 1;
}
TREE_VEC_ELT (vec, i) = new_type;
if (new_type == TYPE_UNKNOWN)
{
if (i >= nlocals)
return -1;
}
else if (TYPE_IS_WIDE (new_type))
i++;
}
return changed;
}
}
static void
type_stack_dup (int size, int offset)
{
tree type[4];
int index;
for (index = 0; index < size + offset; index++)
{
type[index] = stack_type_map[stack_pointer - 1];
if (type[index] == void_type_node)
{
index++;
type[index] = stack_type_map[stack_pointer - 2];
if (! TYPE_IS_WIDE (type[index]))
abort ();
if (index == size || index == size + offset)
abort ();
}
pop_type (type[index]);
}
for (index = size; --index >= 0; )
{
if (type[index] != void_type_node)
push_type (type[index]);
}
for (index = size + offset; --index >= 0; )
{
if (type[index] != void_type_node)
push_type (type[index]);
}
}
struct pc_index
{
int start_pc;
int index;
};
#define INVALIDATE_PC (prevpc = -1, oldpc = PC, PC = INVALID_PC)
#define INVALID_PC (-1)
#define VERIFICATION_ERROR(MESSAGE) \
do { message = MESSAGE; goto verify_error; } while (0)
#define VERIFICATION_ERROR_WITH_INDEX(MESSAGE) \
do { message = MESSAGE; goto error_with_index; } while (0)
static char *
pop_argument_types (tree arg_types)
{
if (arg_types == end_params_node)
return NULL;
if (TREE_CODE (arg_types) == TREE_LIST)
{
char *message = pop_argument_types (TREE_CHAIN (arg_types));
if (message == NULL)
pop_type_0 (TREE_VALUE (arg_types), &message);
return message;
}
abort ();
}
#define POP_TYPE(TYPE, MESSAGE) \
do { pmessage = NULL; pop_type_0 (TYPE, &pmessage); \
if (pmessage != NULL) goto pop_type_error; \
} while (0)
#define POP_TYPE_CONV(TYPE, POPPED_TYPE, MESSAGE) \
do { pmessage = NULL; POPPED_TYPE = pop_type_0 (TYPE, &pmessage); \
if (pmessage != NULL) goto pop_type_error; \
} while (0)
#define PUSH_TYPE(TYPE) \
do { if (! push_type_0 (TYPE)) { goto stack_overflow; }} while (0)
#define PUSH_PENDING(LABEL) \
do { tree tmplab = LABEL; \
if ((message = check_pending_block (tmplab)) != NULL) \
{ oldpc = LABEL_PC (tmplab); goto verify_error; }} while (0)
#ifdef __GNUC__
#define CHECK_PC_IN_RANGE(PC) __extension__ \
({if (PC < 0 || PC > length) goto bad_pc; (void)1;})
#else
#define CHECK_PC_IN_RANGE(PC) (PC < 0 || PC > length ? (abort (), 0) : 1)
#endif
#define BCODE byte_ops
int
verify_jvm_instructions (JCF* jcf, const unsigned char *byte_ops, long length)
{
tree label;
int wide = 0;
int op_code;
int PC;
int oldpc = 0;
int prevpc = 0;
const char *message = 0;
char *pmessage;
int i;
int index;
unsigned char *p;
struct eh_range *prev_eh_ranges = NULL_EH_RANGE;
struct eh_range *eh_ranges;
tree return_type = TREE_TYPE (TREE_TYPE (current_function_decl));
struct pc_index *starts;
int eh_count;
jint int_value = -1;
pending_blocks = NULL_TREE;
current_subr = NULL_TREE;
method_init_exceptions ();
JCF_SEEK (jcf, DECL_CODE_OFFSET (current_function_decl) + length);
eh_count = JCF_readu2 (jcf);
starts = xmalloc (eh_count * sizeof (struct pc_index));
for (i = 0; i < eh_count; ++i)
{
starts[i].start_pc = GET_u2 (jcf->read_ptr + 8 * i);
starts[i].index = i;
}
for (i = 0; i < eh_count; ++i)
{
int start_pc, end_pc, handler_pc, catch_type;
p = jcf->read_ptr + 8 * starts[i].index;
start_pc = GET_u2 (p);
end_pc = GET_u2 (p+2);
handler_pc = GET_u2 (p+4);
catch_type = GET_u2 (p+6);
if (start_pc < 0 || start_pc >= length
|| end_pc < 0 || end_pc > length || start_pc >= end_pc
|| handler_pc < 0 || handler_pc >= length
|| ! (instruction_bits[start_pc] & BCODE_INSTRUCTION_START)
|| (end_pc < length &&
! (instruction_bits[end_pc] & BCODE_INSTRUCTION_START))
|| ! (instruction_bits[handler_pc] & BCODE_INSTRUCTION_START))
{
error ("bad pc in exception_table");
free (starts);
return 0;
}
add_handler (start_pc, end_pc,
lookup_label (handler_pc),
catch_type == 0 ? NULL_TREE
: get_class_constant (jcf, catch_type));
instruction_bits[handler_pc] |= BCODE_EXCEPTION_TARGET;
}
free (starts);
for (PC = 0;;)
{
tree type, tmp;
if (((PC != INVALID_PC
&& instruction_bits[PC] & BCODE_TARGET) != 0)
|| PC == 0)
{
PUSH_PENDING (lookup_label (PC));
INVALIDATE_PC;
}
if (current_subr && PC == INVALID_PC)
{
if (pending_blocks == NULL_TREE
|| (subroutine_nesting (pending_blocks)
< subroutine_nesting (current_subr)))
{
int size
= DECL_MAX_LOCALS (current_function_decl) + stack_pointer;
tree ret_map = LABEL_RETURN_TYPE_STATE (current_subr);
tmp = LABEL_RETURN_LABELS (current_subr);
current_subr = LABEL_SUBR_CONTEXT (current_subr);
if (RETURN_MAP_ADJUSTED (ret_map))
{
for ( ; tmp != NULL_TREE; tmp = TREE_CHAIN (tmp))
{
tree return_label = TREE_VALUE (tmp);
tree return_state = LABEL_TYPE_STATE (return_label);
if (return_state == NULL_TREE)
{
}
else
{
for (index = size; --index >= 0; )
{
type_map[index]
= TREE_VEC_ELT (ret_map, index);
if (type_map[index] == TYPE_UNUSED)
type_map[index]
= TREE_VEC_ELT (return_state, index);
}
}
PUSH_PENDING (return_label);
}
}
}
}
if (PC == INVALID_PC)
{
label = pending_blocks;
if (label == NULL_TREE)
break;
pending_blocks = LABEL_PENDING_CHAIN (label);
LABEL_CHANGED (label) = 0;
if (LABEL_IN_SUBR (label))
current_subr = LABEL_SUBR_START (label);
else
current_subr = NULL_TREE;
load_type_state (label);
PC = LABEL_PC (label);
}
else if (PC >= length)
VERIFICATION_ERROR ("falling through the end of the method");
oldpc = PC;
if (! (instruction_bits[PC] & BCODE_INSTRUCTION_START) && ! wide)
VERIFICATION_ERROR ("PC not at instruction start");
instruction_bits[PC] |= BCODE_VERIFIED;
eh_ranges = find_handler (oldpc);
op_code = byte_ops[PC++];
switch (op_code)
{
int is_static, is_putting;
case OPCODE_nop:
break;
case OPCODE_iconst_m1:
case OPCODE_iconst_0: case OPCODE_iconst_1: case OPCODE_iconst_2:
case OPCODE_iconst_3: case OPCODE_iconst_4: case OPCODE_iconst_5:
i = op_code - OPCODE_iconst_0;
goto push_int;
push_int:
if (byte_ops[PC] == OPCODE_newarray
|| byte_ops[PC] == OPCODE_anewarray)
int_value = i;
PUSH_TYPE (int_type_node); break;
case OPCODE_lconst_0: case OPCODE_lconst_1:
PUSH_TYPE (long_type_node); break;
case OPCODE_fconst_0: case OPCODE_fconst_1: case OPCODE_fconst_2:
PUSH_TYPE (float_type_node); break;
case OPCODE_dconst_0: case OPCODE_dconst_1:
PUSH_TYPE (double_type_node); break;
case OPCODE_bipush:
i = IMMEDIATE_s1;
goto push_int;
case OPCODE_sipush:
i = IMMEDIATE_s2;
goto push_int;
case OPCODE_iload: type = int_type_node; goto general_load;
case OPCODE_lload: type = long_type_node; goto general_load;
case OPCODE_fload: type = float_type_node; goto general_load;
case OPCODE_dload: type = double_type_node; goto general_load;
case OPCODE_aload: type = ptr_type_node; goto general_load;
general_load:
index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
wide = 0;
goto load;
case OPCODE_iload_0: type = int_type_node; index = 0; goto load;
case OPCODE_iload_1: type = int_type_node; index = 1; goto load;
case OPCODE_iload_2: type = int_type_node; index = 2; goto load;
case OPCODE_iload_3: type = int_type_node; index = 3; goto load;
case OPCODE_lload_0: type = long_type_node; index = 0; goto load;
case OPCODE_lload_1: type = long_type_node; index = 1; goto load;
case OPCODE_lload_2: type = long_type_node; index = 2; goto load;
case OPCODE_lload_3: type = long_type_node; index = 3; goto load;
case OPCODE_fload_0: type = float_type_node; index = 0; goto load;
case OPCODE_fload_1: type = float_type_node; index = 1; goto load;
case OPCODE_fload_2: type = float_type_node; index = 2; goto load;
case OPCODE_fload_3: type = float_type_node; index = 3; goto load;
case OPCODE_dload_0: type = double_type_node; index = 0; goto load;
case OPCODE_dload_1: type = double_type_node; index = 1; goto load;
case OPCODE_dload_2: type = double_type_node; index = 2; goto load;
case OPCODE_dload_3: type = double_type_node; index = 3; goto load;
case OPCODE_aload_0: type = ptr_type_node; index = 0; goto load;
case OPCODE_aload_1: type = ptr_type_node; index = 1; goto load;
case OPCODE_aload_2: type = ptr_type_node; index = 2; goto load;
case OPCODE_aload_3: type = ptr_type_node; index = 3; goto load;
load:
if (index < 0
|| (index + TYPE_IS_WIDE (type)
>= DECL_MAX_LOCALS (current_function_decl)))
VERIFICATION_ERROR_WITH_INDEX
("invalid local variable index %d in load");
tmp = type_map[index];
if (tmp == TYPE_UNKNOWN)
VERIFICATION_ERROR_WITH_INDEX
("loading local variable %d which has unknown type");
else if (tmp == TYPE_SECOND
|| (TYPE_IS_WIDE (type)
&& type_map[index+1] != void_type_node)
|| (type == ptr_type_node
? TREE_CODE (tmp) != POINTER_TYPE
: type == int_type_node
? (! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32)
: type != tmp))
VERIFICATION_ERROR_WITH_INDEX
("loading local variable %d which has invalid type");
PUSH_TYPE (tmp);
goto note_used;
case OPCODE_istore: type = int_type_node; goto general_store;
case OPCODE_lstore: type = long_type_node; goto general_store;
case OPCODE_fstore: type = float_type_node; goto general_store;
case OPCODE_dstore: type = double_type_node; goto general_store;
case OPCODE_astore: type = object_ptr_type_node; goto general_store;
general_store:
index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
wide = 0;
goto store;
case OPCODE_istore_0: type = int_type_node; index = 0; goto store;
case OPCODE_istore_1: type = int_type_node; index = 1; goto store;
case OPCODE_istore_2: type = int_type_node; index = 2; goto store;
case OPCODE_istore_3: type = int_type_node; index = 3; goto store;
case OPCODE_lstore_0: type = long_type_node; index=0; goto store;
case OPCODE_lstore_1: type = long_type_node; index=1; goto store;
case OPCODE_lstore_2: type = long_type_node; index=2; goto store;
case OPCODE_lstore_3: type = long_type_node; index=3; goto store;
case OPCODE_fstore_0: type=float_type_node; index=0; goto store;
case OPCODE_fstore_1: type=float_type_node; index=1; goto store;
case OPCODE_fstore_2: type=float_type_node; index=2; goto store;
case OPCODE_fstore_3: type=float_type_node; index=3; goto store;
case OPCODE_dstore_0: type=double_type_node; index=0; goto store;
case OPCODE_dstore_1: type=double_type_node; index=1; goto store;
case OPCODE_dstore_2: type=double_type_node; index=2; goto store;
case OPCODE_dstore_3: type=double_type_node; index=3; goto store;
case OPCODE_astore_0: type = ptr_type_node; index = 0; goto store;
case OPCODE_astore_1: type = ptr_type_node; index = 1; goto store;
case OPCODE_astore_2: type = ptr_type_node; index = 2; goto store;
case OPCODE_astore_3: type = ptr_type_node; index = 3; goto store;
store:
if (index < 0
|| (index + TYPE_IS_WIDE (type)
>= DECL_MAX_LOCALS (current_function_decl)))
{
VERIFICATION_ERROR_WITH_INDEX
("invalid local variable index %d in store");
return 0;
}
POP_TYPE_CONV (type, type, NULL);
type_map[index] = type;
prev_eh_ranges = NULL_EH_RANGE;
find_local_variable (index, type, oldpc);
if (TYPE_IS_WIDE (type))
type_map[index+1] = TYPE_SECOND;
note_used:
if (current_subr)
{
tree vec = LABEL_RETURN_TYPE_STATE (current_subr);
tree subr_vec = LABEL_TYPE_STATE (current_subr);
int len = 1 + TYPE_IS_WIDE (type);
while (--len >= 0)
{
if (TREE_VEC_ELT (vec, index) == TYPE_UNUSED)
TREE_VEC_ELT (vec, index) = TREE_VEC_ELT (subr_vec, index);
}
}
break;
case OPCODE_iadd:
case OPCODE_iand:
case OPCODE_idiv:
case OPCODE_imul:
case OPCODE_ior:
case OPCODE_irem:
case OPCODE_ishl:
case OPCODE_ishr:
case OPCODE_isub:
case OPCODE_iushr:
case OPCODE_ixor:
type = int_type_node; goto binop;
case OPCODE_ineg:
case OPCODE_i2c:
case OPCODE_i2b:
case OPCODE_i2s:
type = int_type_node; goto unop;
case OPCODE_ladd:
case OPCODE_land:
case OPCODE_ldiv:
case OPCODE_lsub:
case OPCODE_lmul:
case OPCODE_lrem:
case OPCODE_lor:
case OPCODE_lxor:
type = long_type_node; goto binop;
case OPCODE_lneg:
type = long_type_node; goto unop;
case OPCODE_fadd: case OPCODE_fsub:
case OPCODE_fmul: case OPCODE_fdiv: case OPCODE_frem:
type = float_type_node; goto binop;
case OPCODE_fneg:
type = float_type_node; goto unop;
case OPCODE_dadd: case OPCODE_dsub:
case OPCODE_dmul: case OPCODE_ddiv: case OPCODE_drem:
type = double_type_node; goto binop;
case OPCODE_dneg:
type = double_type_node; goto unop;
unop:
pop_type (type);
PUSH_TYPE (type);
break;
binop:
pop_type (type);
pop_type (type);
PUSH_TYPE (type);
break;
case OPCODE_lshl:
case OPCODE_lshr:
case OPCODE_lushr:
pop_type (int_type_node);
pop_type (long_type_node);
PUSH_TYPE (long_type_node);
break;
case OPCODE_iinc:
index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
PC += wide + 1;
wide = 0;
if (index < 0 || index >= DECL_MAX_LOCALS (current_function_decl))
VERIFICATION_ERROR ("invalid local variable index in iinc");
tmp = type_map[index];
if (tmp == NULL_TREE
|| ! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32)
VERIFICATION_ERROR ("invalid local variable type in iinc");
break;
case OPCODE_i2l:
pop_type (int_type_node); PUSH_TYPE (long_type_node); break;
case OPCODE_i2f:
pop_type (int_type_node); PUSH_TYPE (float_type_node); break;
case OPCODE_i2d:
pop_type (int_type_node); PUSH_TYPE (double_type_node); break;
case OPCODE_l2i:
pop_type (long_type_node); PUSH_TYPE (int_type_node); break;
case OPCODE_l2f:
pop_type (long_type_node); PUSH_TYPE (float_type_node); break;
case OPCODE_l2d:
pop_type (long_type_node); PUSH_TYPE (double_type_node); break;
case OPCODE_f2i:
pop_type (float_type_node); PUSH_TYPE (int_type_node); break;
case OPCODE_f2l:
pop_type (float_type_node); PUSH_TYPE (long_type_node); break;
case OPCODE_f2d:
pop_type (float_type_node); PUSH_TYPE (double_type_node); break;
case OPCODE_d2i:
pop_type (double_type_node); PUSH_TYPE (int_type_node); break;
case OPCODE_d2l:
pop_type (double_type_node); PUSH_TYPE (long_type_node); break;
case OPCODE_d2f:
pop_type (double_type_node); PUSH_TYPE (float_type_node); break;
case OPCODE_lcmp:
type = long_type_node; goto compare;
case OPCODE_fcmpl:
case OPCODE_fcmpg:
type = float_type_node; goto compare;
case OPCODE_dcmpl:
case OPCODE_dcmpg:
type = double_type_node; goto compare;
compare:
pop_type (type); pop_type (type);
PUSH_TYPE (int_type_node); break;
case OPCODE_ifeq:
case OPCODE_ifne:
case OPCODE_iflt:
case OPCODE_ifge:
case OPCODE_ifgt:
case OPCODE_ifle:
pop_type (int_type_node); goto cond;
case OPCODE_ifnull:
case OPCODE_ifnonnull:
pop_type (ptr_type_node ); goto cond;
case OPCODE_if_icmpeq:
case OPCODE_if_icmpne:
case OPCODE_if_icmplt:
case OPCODE_if_icmpge:
case OPCODE_if_icmpgt:
case OPCODE_if_icmple:
pop_type (int_type_node); pop_type (int_type_node); goto cond;
case OPCODE_if_acmpeq:
case OPCODE_if_acmpne:
pop_type (object_ptr_type_node); pop_type (object_ptr_type_node);
goto cond;
cond:
PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s2));
break;
case OPCODE_goto:
PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s2));
INVALIDATE_PC;
break;
case OPCODE_wide:
switch (byte_ops[PC])
{
case OPCODE_iload: case OPCODE_lload:
case OPCODE_fload: case OPCODE_dload: case OPCODE_aload:
case OPCODE_istore: case OPCODE_lstore:
case OPCODE_fstore: case OPCODE_dstore: case OPCODE_astore:
case OPCODE_iinc:
case OPCODE_ret:
wide = 1;
break;
default:
VERIFICATION_ERROR ("invalid use of wide instruction");
}
break;
case OPCODE_return: type = void_type_node; goto ret;
case OPCODE_ireturn:
if ((TREE_CODE (return_type) == BOOLEAN_TYPE
|| TREE_CODE (return_type) == CHAR_TYPE
|| TREE_CODE (return_type) == INTEGER_TYPE)
&& TYPE_PRECISION (return_type) <= 32)
type = return_type;
else
type = NULL_TREE;
goto ret;
case OPCODE_lreturn: type = long_type_node; goto ret;
case OPCODE_freturn: type = float_type_node; goto ret;
case OPCODE_dreturn: type = double_type_node; goto ret;
case OPCODE_areturn:
if (TREE_CODE (return_type) == POINTER_TYPE)
type = return_type;
else
type = NULL_TREE;
goto ret;
ret:
if (type != return_type)
VERIFICATION_ERROR ("incorrect ?return opcode");
if (type != void_type_node)
POP_TYPE (type, "return value has wrong type");
INVALIDATE_PC;
break;
case OPCODE_getstatic: is_putting = 0; is_static = 1; goto field;
case OPCODE_putstatic: is_putting = 1; is_static = 1; goto field;
case OPCODE_getfield: is_putting = 0; is_static = 0; goto field;
case OPCODE_putfield: is_putting = 1; is_static = 0; goto field;
field:
{
tree field_signature, field_type;
index = IMMEDIATE_u2;
if (index <= 0 || index >= JPOOL_SIZE (current_jcf))
VERIFICATION_ERROR_WITH_INDEX ("bad constant pool index %d");
if (JPOOL_TAG (current_jcf, index) != CONSTANT_Fieldref)
VERIFICATION_ERROR
("field instruction does not reference a Fieldref");
field_signature
= COMPONENT_REF_SIGNATURE (¤t_jcf->cpool, index);
field_type = get_type_from_signature (field_signature);
if (is_putting)
POP_TYPE (field_type, "incorrect type for field");
if (! is_static)
{
int clindex
= COMPONENT_REF_CLASS_INDEX (¤t_jcf->cpool, index);
tree self_type = get_class_constant (current_jcf, clindex);
POP_TYPE (self_type, "incorrect type for field reference");
}
if (! is_putting)
PUSH_TYPE (field_type);
break;
}
case OPCODE_new:
PUSH_TYPE (get_class_constant (jcf, IMMEDIATE_u2));
break;
case OPCODE_dup: wide = 1; index = 0; goto dup;
case OPCODE_dup_x1: wide = 1; index = 1; goto dup;
case OPCODE_dup_x2: wide = 1; index = 2; goto dup;
case OPCODE_dup2: wide = 2; index = 0; goto dup;
case OPCODE_dup2_x1: wide = 2; index = 1; goto dup;
case OPCODE_dup2_x2: wide = 2; index = 2; goto dup;
dup:
if (wide + index > stack_pointer)
VERIFICATION_ERROR ("stack underflow - dup* operation");
type_stack_dup (wide, index);
wide = 0;
break;
case OPCODE_pop: index = 1; goto pop;
case OPCODE_pop2: index = 2; goto pop;
pop:
if (stack_pointer < index)
VERIFICATION_ERROR ("stack underflow");
stack_pointer -= index;
break;
case OPCODE_swap:
if (stack_pointer < 2)
VERIFICATION_ERROR ("stack underflow (in swap)");
else
{
tree type1 = stack_type_map[stack_pointer - 1];
tree type2 = stack_type_map[stack_pointer - 2];
if (type1 == void_type_node || type2 == void_type_node)
VERIFICATION_ERROR ("verifier (swap): double or long value");
stack_type_map[stack_pointer - 2] = type1;
stack_type_map[stack_pointer - 1] = type2;
}
break;
case OPCODE_ldc: index = IMMEDIATE_u1; goto ldc;
case OPCODE_ldc2_w:
case OPCODE_ldc_w:
index = IMMEDIATE_u2; goto ldc;
ldc:
if (index <= 0 || index >= JPOOL_SIZE (current_jcf))
VERIFICATION_ERROR_WITH_INDEX ("bad constant pool index %d in ldc");
int_value = -1;
switch (JPOOL_TAG (current_jcf, index) & ~CONSTANT_ResolvedFlag)
{
case CONSTANT_Integer: type = int_type_node; goto check_ldc;
case CONSTANT_Float: type = float_type_node; goto check_ldc;
case CONSTANT_String: type = string_type_node; goto check_ldc;
case CONSTANT_Long: type = long_type_node; goto check_ldc;
case CONSTANT_Double: type = double_type_node; goto check_ldc;
check_ldc:
if (TYPE_IS_WIDE (type) == (op_code == OPCODE_ldc2_w))
break;
default:
VERIFICATION_ERROR ("bad constant pool tag in ldc");
}
if (type == int_type_node)
{
i = TREE_INT_CST_LOW (get_constant (current_jcf, index));
goto push_int;
}
PUSH_TYPE (type);
break;
case OPCODE_invokevirtual:
case OPCODE_invokespecial:
case OPCODE_invokestatic:
case OPCODE_invokeinterface:
{
tree sig, method_name, method_type, self_type;
int self_is_interface, tag;
index = IMMEDIATE_u2;
if (index <= 0 || index >= JPOOL_SIZE (current_jcf))
VERIFICATION_ERROR_WITH_INDEX
("bad constant pool index %d for invoke");
tag = JPOOL_TAG (current_jcf, index);
if (op_code == OPCODE_invokeinterface)
{
if (tag != CONSTANT_InterfaceMethodref)
VERIFICATION_ERROR
("invokeinterface does not reference an InterfaceMethodref");
}
else
{
if (tag != CONSTANT_Methodref)
VERIFICATION_ERROR ("invoke does not reference a Methodref");
}
sig = COMPONENT_REF_SIGNATURE (¤t_jcf->cpool, index);
self_type
= get_class_constant (current_jcf,
COMPONENT_REF_CLASS_INDEX
(¤t_jcf->cpool, index));
if (! CLASS_LOADED_P (self_type))
load_class (self_type, 1);
self_is_interface = CLASS_INTERFACE (TYPE_NAME (self_type));
method_name = COMPONENT_REF_NAME (¤t_jcf->cpool, index);
method_type = parse_signature_string ((const unsigned char *) IDENTIFIER_POINTER (sig),
IDENTIFIER_LENGTH (sig));
if (TREE_CODE (method_type) != FUNCTION_TYPE)
VERIFICATION_ERROR ("bad method signature");
pmessage = pop_argument_types (TYPE_ARG_TYPES (method_type));
if (pmessage != NULL)
{
message = "invalid argument type";
goto pop_type_error;
}
if (ID_CLINIT_P (method_name))
VERIFICATION_ERROR ("invoke opcode can't invoke <clinit>");
if (op_code != OPCODE_invokespecial && ID_INIT_P (method_name))
VERIFICATION_ERROR ("invoke opcode can't invoke <init>");
if (op_code != OPCODE_invokestatic)
POP_TYPE (self_type,
"stack type not subclass of invoked method's class");
switch (op_code)
{
case OPCODE_invokeinterface:
{
int nargs = IMMEDIATE_u1;
int notZero = IMMEDIATE_u1;
if (!nargs || notZero)
VERIFICATION_ERROR
("invalid argument number in invokeinterface");
if (! self_is_interface)
VERIFICATION_ERROR
("invokeinterface calls method not in interface");
break;
default:
if (self_is_interface)
VERIFICATION_ERROR ("method in interface called");
}
}
if (TREE_TYPE (method_type) != void_type_node)
PUSH_TYPE (TREE_TYPE (method_type));
break;
}
case OPCODE_arraylength:
pop_type (ptr_type_node);
PUSH_TYPE (int_type_node);
break;
case OPCODE_iastore: type = int_type_node; goto astore;
case OPCODE_lastore: type = long_type_node; goto astore;
case OPCODE_fastore: type = float_type_node; goto astore;
case OPCODE_dastore: type = double_type_node; goto astore;
case OPCODE_aastore: type = ptr_type_node; goto astore;
case OPCODE_bastore: type = int_type_node; goto astore;
case OPCODE_castore: type = int_type_node; goto astore;
case OPCODE_sastore: type = int_type_node; goto astore;
astore:
pop_type (type);
pop_type (int_type_node);
pop_type (ptr_type_node);
break;
case OPCODE_iaload: type = int_type_node; goto aload;
case OPCODE_laload: type = long_type_node; goto aload;
case OPCODE_faload: type = float_type_node; goto aload;
case OPCODE_daload: type = double_type_node; goto aload;
case OPCODE_aaload: type = ptr_type_node; goto aload;
case OPCODE_baload: type = promote_type (byte_type_node); goto aload;
case OPCODE_caload: type = promote_type (char_type_node); goto aload;
case OPCODE_saload: type = promote_type (short_type_node); goto aload;
aload:
pop_type (int_type_node);
tmp = pop_type (ptr_type_node);
if (is_array_type_p (tmp))
type = TYPE_ARRAY_ELEMENT (TREE_TYPE (tmp));
else if (tmp != TYPE_NULL)
VERIFICATION_ERROR ("array load from non-array type");
PUSH_TYPE (type);
break;
case OPCODE_anewarray:
type = get_class_constant (current_jcf, IMMEDIATE_u2);
type = promote_type (type);
goto newarray;
case OPCODE_newarray:
index = IMMEDIATE_u1;
type = decode_newarray_type (index);
if (type == NULL_TREE)
VERIFICATION_ERROR ("invalid type code in newarray opcode");
goto newarray;
newarray:
if (int_value >= 0 && prevpc >= 0)
{
switch (byte_ops[prevpc])
{
case OPCODE_iconst_0: case OPCODE_iconst_1:
case OPCODE_iconst_2: case OPCODE_iconst_3:
case OPCODE_iconst_4: case OPCODE_iconst_5:
case OPCODE_bipush: case OPCODE_sipush:
case OPCODE_ldc: case OPCODE_ldc_w:
break;
default:
int_value = -1;
}
}
else
int_value = -1;
type = build_java_array_type (type, int_value);
pop_type (int_type_node);
PUSH_TYPE (type);
break;
case OPCODE_multianewarray:
{
int ndim, i;
index = IMMEDIATE_u2;
ndim = IMMEDIATE_u1;
if (ndim < 1)
VERIFICATION_ERROR
("number of dimension lower that 1 in multianewarray" );
for (i = 0; i < ndim; i++)
pop_type (int_type_node);
PUSH_TYPE (get_class_constant (current_jcf, index));
break;
}
case OPCODE_aconst_null:
PUSH_TYPE (ptr_type_node);
break;
case OPCODE_athrow:
POP_TYPE (throwable_type_node, "missing throwable at athrow" );
INVALIDATE_PC;
break;
case OPCODE_checkcast:
POP_TYPE (object_ptr_type_node,
"checkcast operand is not a pointer");
type = get_class_constant (current_jcf, IMMEDIATE_u2);
PUSH_TYPE (type);
break;
case OPCODE_instanceof:
POP_TYPE (object_ptr_type_node,
"instanceof operand is not a pointer");
get_class_constant (current_jcf, IMMEDIATE_u2);
PUSH_TYPE (int_type_node);
break;
case OPCODE_tableswitch:
{
jint low, high;
POP_TYPE (int_type_node, "missing int for tableswitch");
while (PC%4)
{
if (byte_ops[PC++])
VERIFICATION_ERROR ("bad alignment in tableswitch pad");
}
PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
low = IMMEDIATE_s4;
high = IMMEDIATE_s4;
if (low > high)
VERIFICATION_ERROR ("unsorted low/high value in tableswitch");
while (low++ <= high)
PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
INVALIDATE_PC;
break;
}
case OPCODE_lookupswitch:
{
jint npairs, last = 0, not_registered = 1;
POP_TYPE (int_type_node, "missing int for lookupswitch");
while (PC%4)
{
if (byte_ops[PC++])
VERIFICATION_ERROR ("bad alignment in lookupswitch pad");
}
PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
npairs = IMMEDIATE_s4;
if (npairs < 0)
VERIFICATION_ERROR ("invalid number of targets in lookupswitch");
while (npairs--)
{
int match = IMMEDIATE_s4;
if (not_registered)
not_registered = 0;
else if (last >= match)
VERIFICATION_ERROR ("unsorted match value in lookupswitch");
last = match;
PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
}
INVALIDATE_PC;
break;
}
case OPCODE_monitorenter:
case OPCODE_monitorexit:
pop_type (ptr_type_node);
break;
case OPCODE_goto_w:
PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
INVALIDATE_PC;
break;
case OPCODE_jsr:
{
tree target = lookup_label (oldpc + IMMEDIATE_s2);
tree return_label = lookup_label (PC);
PUSH_TYPE (return_address_type_node);
if (LABEL_RETURN_LABEL (target) == NULL_TREE)
{
tree return_type_map;
int nlocals = DECL_MAX_LOCALS (current_function_decl);
index = nlocals + DECL_MAX_STACK (current_function_decl);
return_type_map = make_tree_vec (index);
while (index > nlocals)
TREE_VEC_ELT (return_type_map, --index) = TYPE_UNKNOWN;
while (index > 0)
TREE_VEC_ELT (return_type_map, --index) = TYPE_UNUSED;
LABEL_RETURN_LABEL (target)
= build_decl (LABEL_DECL, NULL_TREE, TREE_TYPE (target));
LABEL_PC (LABEL_RETURN_LABEL (target)) = INVALID_PC;
LABEL_RETURN_TYPE_STATE (target) = return_type_map;
LABEL_IS_SUBR_START (target) = 1;
LABEL_IN_SUBR (target) = 1;
LABEL_SUBR_START (target) = target;
LABEL_SUBR_CONTEXT (target) = current_subr;
}
else if (! LABEL_IS_SUBR_START (target)
|| LABEL_SUBR_CONTEXT (target) != current_subr)
VERIFICATION_ERROR ("label part of different subroutines");
i = merge_type_state (target);
if (i != 0)
{
if (i < 0)
VERIFICATION_ERROR ("types could not be merged at jsr");
push_pending_label (target);
}
current_subr = target;
if (! value_member (return_label, LABEL_RETURN_LABELS (target)))
{
LABEL_RETURN_LABELS (target)
= tree_cons (NULL_TREE, return_label,
LABEL_RETURN_LABELS (target));
}
if (LABEL_VERIFIED (target))
{
tree return_map = LABEL_RETURN_TYPE_STATE (target);
int len = TREE_VEC_LENGTH (return_map);
stack_pointer = len - DECL_MAX_LOCALS (current_function_decl);
while (--len >= 0)
{
if (TREE_VEC_ELT (return_map, len) != TYPE_UNUSED)
type_map[len] = TREE_VEC_ELT (return_map, len);
}
current_subr = LABEL_SUBR_CONTEXT (target);
if (RETURN_MAP_ADJUSTED (return_map))
PUSH_PENDING (return_label);
}
INVALIDATE_PC;
}
break;
case OPCODE_ret:
if (current_subr == NULL_TREE)
VERIFICATION_ERROR ("ret instruction not in a jsr subroutine");
else
{
tree ret_map = LABEL_RETURN_TYPE_STATE (current_subr);
int size
= DECL_MAX_LOCALS (current_function_decl) + stack_pointer;
index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
wide = 0;
INVALIDATE_PC;
if (index < 0 || index >= DECL_MAX_LOCALS (current_function_decl)
|| type_map[index] != TYPE_RETURN_ADDR)
VERIFICATION_ERROR ("invalid ret index");
if (! RETURN_MAP_ADJUSTED (ret_map))
{
TREE_VEC_LENGTH (ret_map) = size;
for (index = size; --index >= 0; )
{
if (TREE_VEC_ELT (ret_map, index) != TYPE_UNUSED)
TREE_VEC_ELT (ret_map, index) = type_map[index];
}
RETURN_MAP_ADJUSTED (ret_map) = 1;
}
else
{
if (TREE_VEC_LENGTH (ret_map) != size)
VERIFICATION_ERROR ("inconsistent stack size on ret");
for (index = 0; index < size; index++)
{
tree type = TREE_VEC_ELT (ret_map, index);
if (type != TYPE_UNUSED)
{
type = merge_types (type, type_map[index]);
TREE_VEC_ELT (ret_map, index) = type;
if (type == TYPE_UNKNOWN)
{
if (index >= size - stack_pointer)
VERIFICATION_ERROR
("inconsistent types on ret from jsr");
}
else if (TYPE_IS_WIDE (type))
index++;
}
}
}
}
break;
case OPCODE_jsr_w:
case OPCODE_ret_w:
default:
error ("unknown opcode %d@pc=%d during verification", op_code, PC-1);
return 0;
}
prevpc = oldpc;
if (eh_ranges != prev_eh_ranges)
{
int save_stack_pointer = stack_pointer;
int index = DECL_MAX_LOCALS (current_function_decl);
tree save_type = type_map[index];
tree save_current_subr = current_subr;
struct eh_range *ranges = find_handler (oldpc);
stack_pointer = 1;
for ( ; ranges != NULL_EH_RANGE; ranges = ranges->outer)
{
tree chain = ranges->handlers;
current_subr = save_current_subr;
for ( ; current_subr != NULL_TREE;
current_subr = LABEL_SUBR_CONTEXT (current_subr))
{
tree return_labels = LABEL_RETURN_LABELS (current_subr);
int return_pc = LABEL_PC (TREE_VALUE (return_labels));
if (return_pc <= ranges->start_pc
|| return_pc > ranges->end_pc)
break;
}
for ( ; chain != NULL_TREE; chain = TREE_CHAIN (chain))
{
tree handler = TREE_VALUE (chain);
tree type = TREE_PURPOSE (chain);
if (type == NULL_TREE)
type = throwable_type_node;
type_map[index] = promote_type (type);
PUSH_PENDING (handler);
}
}
stack_pointer = save_stack_pointer;
current_subr = save_current_subr;
type_map[index] = save_type;
prev_eh_ranges = eh_ranges;
}
}
return 1;
pop_type_error:
error ("verification error at PC=%d", oldpc);
if (message != NULL)
error ("%s", message);
error ("%s", pmessage);
free (pmessage);
return 0;
stack_overflow:
message = "stack overflow";
goto verify_error;
bad_pc:
message = "program counter out of range";
goto verify_error;
error_with_index:
error ("verification error at PC=%d", oldpc);
error (message, index);
return 0;
verify_error:
error ("verification error at PC=%d", oldpc);
error ("%s", message);
return 0;
}