#ifdef VISUAL_CPP
#define snprintf _snprintf
#endif
#include "method_desc.hh"
void print_call_sequence(callee_desc* callee, int loop_id, int path_id)
{
if (callee != NULL) {
print_call_sequence((callee_desc*)callee->backtrace, loop_id, path_id);
callee->message(msg_loop, (void*)loop_id, (void*)path_id,
callee->method);
}
}
int method_desc::demangle_method_name(char* buf)
{
char* dst = buf;
const char* src = desc.as_asciz();
assert(*src++ == '(');
dst += sprintf(dst, "%s.%s(", cls->name.as_asciz(), name.as_asciz());
int first_parameter = true;
while (*src != ')') {
if (!first_parameter) {
*dst++ = ',';
*dst++ = ' ';
}
first_parameter = false;
int indirect = 0;
while (*src == '[') {
indirect += 1;
src += 1;
}
switch (*src++) {
case 'I':
dst += sprintf(dst, "int");
break;
case 'S':
dst += sprintf(dst, "short");
break;
case 'D':
dst += sprintf(dst, "double");
break;
case 'J':
dst += sprintf(dst, "long");
break;
case 'F':
dst += sprintf(dst, "float");
break;
case 'B':
dst += sprintf(dst, "byte");
break;
case 'C':
dst += sprintf(dst, "char");
break;
case 'Z':
dst += sprintf(dst, "boolean");
break;
case 'L':
while (*src != ';') {
if (*src == '/') *dst++ = '.';
else *dst++ = *src;
src += 1;
}
src += 1;
}
while (indirect != 0) {
*dst++ = '[';
*dst++ = ']';
indirect -= 1;
}
}
*dst++ = ')';
*dst = 0;
return dst - buf;
}
void method_desc::check_invocations()
{
for (int i = 0; i < 32; i++) {
if (null_parameter_mask & unchecked_use_mask & (1 << i)) {
message_at(msg_null_param, cls->source_file, first_line, this, i);
}
}
}
bool method_desc::build_call_graph(method_desc* caller, callee_desc* callee,
int caller_attr)
{
callee->backtrace = caller;
for (overridden_method* ovr = overridden; ovr != NULL; ovr = ovr->next) {
ovr->method->build_call_graph(caller, callee, caller_attr);
}
if ((attr & m_synchronized) || (attr & m_sync_block)) {
if (!(caller_attr & callee_desc::i_self)) {
#ifdef DUMP_EDGES
char buf[2][MAX_MSG_LENGTH];
caller->demangle_method_name(buf[0]);
demangle_method_name(buf[1]);
printf("Call graph edge %s -> %s\n", buf[0], buf[1]);
#endif
graph_edge* edge = new graph_edge(vertex, caller, callee);
assert(caller->vertex != NULL);
caller->vertex->attach(edge);
}
return true;
} else if (!(attr & m_deadlock_free)) {
bool can_cause_deadlock = false;
for (callee = callees; callee != NULL; callee = callee->next) {
if (callee->backtrace != caller) {
can_cause_deadlock |=
callee->method->build_call_graph(caller, callee,
caller_attr&callee->attr);
}
}
if (!can_cause_deadlock) {
attr |= m_deadlock_free;
}
return can_cause_deadlock;
}
return false;
}
void method_desc::check_synchronization()
{
if (attr & m_concurrent) {
for (callee_desc* callee = callees; callee != NULL;
callee = callee->next)
{
if (!(callee->method->attr & (m_serialized|m_synchronized)) &&
!(callee->method->cls->attr & class_desc::cl_system) &&
!(callee->attr & (callee_desc::i_self
|callee_desc::i_synchronized)) &&
(strstr(callee->method->name.as_asciz(), "init>") == NULL) )
{
callee->message(msg_concurrent_call, callee->method);
}
}
for (access_desc* accessor = accessors; accessor != NULL;
accessor = accessor->next)
{
if (!(accessor->field->attr
& (field_desc::f_volatile|field_desc::f_final
|field_desc::f_serialized))
&& !(accessor->field->cls->attr & class_desc::cl_system)
&& !(accessor->attr&(access_desc::a_new|access_desc::a_self)))
{
accessor->message(msg_concurrent_access,
&accessor->field->name,accessor->field->cls);
}
}
}
}
void method_desc::find_access_dependencies()
{
for (callee_desc* callee = callees; callee != NULL; callee = callee->next){
class_desc* caller_class = callee->method->accessor;
if (caller_class == NULL) {
callee->method->accessor = cls;
} else if (!cls->in_relationship_with(caller_class)) {
// Method is called from two unrelated classes, so if this
// two methods are exeuted concurretly we will have access conflict
callee->method->attr &= ~m_serialized;
}
if ((!(attr & m_static) && (callee->method->attr & m_static))
|| !(attr & m_concurrent))
{
//
// If caller is instance method and callee - static method
// of the class, or caller is not included in concurrent closure
// (and so it can be exeuted in main thread of control), then
// any invocation of callee method from concurrent thread can
// cause concurrent execution of this method. If no method
// from concurrent closure invoke this method, then
// "m_serialized" attribute will not checked at all, otherwise
// message about concurrent invocation of non-synchronized method
// will be reported.
//
callee->method->attr &= ~m_serialized;
}
}
for (access_desc* accessor = accessors; accessor != NULL;
accessor = accessor->next)
{
class_desc* accessor_class = accessor->field->accessor;
if (accessor_class == NULL) {
accessor->field->accessor = cls;
} else if (!cls->in_relationship_with(accessor_class)) {
accessor->field->attr &= ~field_desc::f_serialized;
}
if ((attr & m_static) && (accessor->field->attr & field_desc::f_static)
&& cls->isa(accessor->field->cls))
{
accessor->attr |= access_desc::a_self;
}
if ((!(attr & m_static)
&& (accessor->field->attr & field_desc::f_static))
|| !(attr & m_concurrent))
{
accessor->field->attr &= ~field_desc::f_serialized;
}
}
}
void method_desc::build_call_graph()
{
if (attr & m_synchronized) {
for (callee_desc* callee=callees; callee != NULL; callee=callee->next){
callee->method->build_call_graph(this, callee, callee->attr);
}
}
}
int method_desc::print_call_path_to(callee_desc* target,
int loop_id, int path_id, int caller_attr,
callee_desc* prev)
{
if (attr & (m_deadlock_free|m_visited)) {
return path_id;
}
attr |= m_visited;
if (prev != NULL) {
for (overridden_method* ovr = overridden;
ovr != NULL && path_id < max_shown_paths;
ovr = ovr->next)
{
path_id = ovr->method->print_call_path_to(target, loop_id, path_id,
caller_attr, prev);
}
}
for (callee_desc* callee = callees;
callee != NULL && path_id < max_shown_paths;
callee = callee->next)
{
int callee_attr = callee->attr & caller_attr;
if (callee->method->attr & m_synchronized) {
if (callee == target && !(callee_attr & callee_desc::i_self)) {
print_call_sequence(prev, loop_id, ++path_id);
}
} else {
callee->backtrace = prev;
path_id = callee->method->print_call_path_to(target, loop_id,
path_id, callee_attr,
callee);
}
}
attr &= ~m_visited;
return path_id;
}
void method_desc::add_to_concurrent_closure(callee_desc* caller,
int caller_attr, int depth)
{
for (overridden_method* ovr=overridden; ovr != NULL; ovr=ovr->next) {
add_to_concurrent_closure(caller, caller_attr, depth);
}
if (attr & m_synchronized) {
if ((caller_attr & (callee_desc::i_synchronized|callee_desc::i_self))
== callee_desc::i_synchronized && (attr & m_wait))
{
int callee_attr = callee_desc::i_self|callee_desc::i_wait_deadlock;
callee_desc* up = caller;
while (true) {
callee_attr &= up->attr;
if (up->backtrace == NULL
|| (!(callee_attr & callee_desc::i_self) &&
((up->attr & callee_desc::i_synchronized)
|| (((callee_desc*)up->backtrace)->method->attr
& m_synchronized))))
{
break;
}
up = (callee_desc*)up->backtrace;
}
// Check if this pass was already found
if (!(callee_attr & callee_desc::i_wait_deadlock)) {
message_at(msg_wait, cls->source_file, wait_line, this);
callee_desc* bt = caller;
while (true) {
bt->message(msg_wait_path, bt->method);
bt->attr |= callee_desc::i_wait_deadlock;
if (bt == up) break;
bt = (callee_desc*)bt->backtrace;
}
}
}
attr |= m_concurrent;
if ((caller_attr & (callee_desc::i_synchronized|callee_desc::i_self))
== callee_desc::i_synchronized && !(attr & m_visited))
{
attr |= m_visited;
for (callee_desc* callee = callees;
callee != NULL;
callee = callee->next)
{
if (callee->attr & callee_desc::i_self) {
callee->backtrace = caller;
callee->method->add_to_concurrent_closure
(callee, callee_desc::i_synchronized, 0);
}
}
attr &= ~m_visited;
}
}
else if (!(attr & m_visited)
&& !((depth != 0 || (caller_attr & callee_desc::i_self))
&& (attr & m_concurrent)))
{
if (attr & m_concurrent) {
depth += 1;
}
attr |= m_visited|m_concurrent;
for (callee_desc* callee=callees; callee != NULL; callee=callee->next){
int callee_attr = caller_attr;
if (callee->attr & callee_desc::i_synchronized) {
callee_attr = callee_desc::i_synchronized;
} else {
callee_attr &= callee->attr|~callee_desc::i_self;
}
callee->backtrace = caller;
callee->method->add_to_concurrent_closure(callee, callee_attr,
depth);
}
attr &= ~m_visited;
}
}
void method_desc::build_concurrent_closure()
{
//
// Check if "run" method of class implementing Runnable interface
// is synchronized and mark this method as concurrent.
//
int caller_attr = 0;
attr |= m_visited;
if (attr & m_synchronized) {
caller_attr = callee_desc::i_synchronized;
}
else if (name == "run" && (cls->implements("java/lang/Runnable")
|| cls->isa("java/lang/Thread")))
{
if (cls->implements("java/lang/Runnable")) {
message_at(msg_run_nosync, cls->source_file, first_line, this);
}
} else {
for (callee_desc* callee=callees; callee != NULL; callee=callee->next){
if (callee->attr & callee_desc::i_synchronized) {
callee->backtrace = NULL;
callee->method->
add_to_concurrent_closure(callee,
callee_desc::i_synchronized, 0);
}
}
attr &= ~m_visited;
return;
}
for (callee_desc* callee=callees; callee != NULL; callee=callee->next) {
int callee_attr = callee->attr;
if (callee_attr & callee_desc::i_synchronized) {
// Synchronized construction was used. Clear "i_self" bit,
// because two monitors are already locked
callee_attr &= ~callee_desc::i_self;
}
callee->backtrace = NULL;
callee->method->add_to_concurrent_closure(callee,
callee_attr|caller_attr, 0);
}
attr &= ~m_visited;
}
void method_desc::calculate_attributes()
{
vertex = (attr & m_static) ? cls->metaclass_vertex : cls->class_vertex;
//
// Find out all "self" static invocations
//
for (callee_desc* callee = callees; callee != NULL; callee=callee->next) {
if ((attr & callee->method->attr & m_static)
&& cls->isa(callee->method->cls))
{
callee->attr |= callee_desc::i_self;
}
}
}
int method_desc::get_line_number(int pc)
{
while (line_table[pc] == 0 && pc > 0) {
pc -= 1;
}
return line_table[pc];
}
void method_desc::message(int code, int pc, ...)
{
va_list ap;
va_start(ap, pc);
#ifdef PRINT_PC
printf("In %s.%s pc=%d\n", cls->name.as_asciz(), name.as_asciz(), pc);
#endif
format_message(code, cls->source_file, get_line_number(pc), ap);
va_end(ap);
}
void method_desc::check_variable_for_null(int pc, vbm_operand* sp)
{
if (!(sp->mask & var_desc::vs_not_null)) {
if (sp->index >= 0) {
message(msg_null_var, pc, &vars[sp->index].name);
} else {
message(msg_null_ptr, pc);
}
}
else if ((unsigned)sp->index < 32
&& (sp->mask & var_desc::vs_not_null) != var_desc::vs_not_null)
{
// formal parameter was not assigned value
// and was not checked for NULL
if (null_parameter_mask & (1 << sp->index)) {
message(msg_null_param, pc, this, sp->index);
} else {
unchecked_use_mask |= 1 << sp->index;
}
}
}
void method_desc::check_array_index(int pc, vbm_operand* sp)
{
check_variable_for_null(pc, sp);
if (sp[1].min < 0) {
if (sp[1].max < 0) {
message(msg_bad_index, pc, sp[1].min, sp[1].max);
} else if (sp[1].min > -128) {
message(msg_maybe_bad_index, pc, sp[1].min, sp[1].max);
}
}
if (sp[1].max >= sp->max && sp->max != MAX_ARRAY_LENGTH) {
if (sp[1].min >= sp->max) {
message(msg_bad_index, pc, sp[1].min, sp[1].max);
} else if (sp[1].max - sp->max < 127) {
message(msg_maybe_bad_index, pc, sp[1].min, sp[1].max);
}
}
}
void method_desc::basic_blocks_analysis()
{
byte* pc = code;
byte* end = pc + code_length;
int i;
var_store_count = n_vars > 0 ? new int[n_vars] : NULL;
for (i = n_vars; --i >= 0;) {
vars[i].type = tp_void;
vars[i].name = utf_string("???");
vars[i].min = 0;
vars[i].max = MAX_ARRAY_LENGTH;
var_store_count[i] = 0;
}
while (pc != end) {
int addr = pc - code;
int offs;
switch (*pc) {
case jsr:
case ifeq:
case ifne:
case iflt:
case ifge:
case ifgt:
case ifle:
case if_icmpeq:
case if_icmpne:
case if_icmplt:
case if_icmpge:
case if_icmpgt:
case if_icmple:
case if_acmpeq:
case if_acmpne:
case ifnull:
case ifnonnull:
case goto_near:
offs = (short)unpack2(pc+1);
if (offs < 0) { // backward jump
new ctx_reset(&context[addr+offs], var_store_count, n_vars);
new ctx_split(&context[addr], ctx_split::jmp_backward);
} else if (offs > 0) { // forward jump
new ctx_merge(&context[addr+offs],
new ctx_split(&context[addr], ctx_split::jmp_forward));
}
pc += 3;
break;
case jsr_w:
case goto_w:
offs = unpack4(pc+1);
if (offs < 0) { // backward jump
new ctx_reset(&context[addr+offs], var_store_count, n_vars);
} else if (offs > 0) { // forward jump
new ctx_merge(&context[addr+offs],
new ctx_split(&context[addr], ctx_split::jmp_forward));
}
pc += 5;
break;
case tableswitch:
{
pc += 4 - (addr & 3);
offs = unpack4(pc); // default label
int low = unpack4(pc+4);
int high = unpack4(pc+8);
int n_forward_jumps = 0;
pc += 12;
ctx_split* select = new ctx_split(&context[addr]);
if (offs < 0) {
new ctx_reset(&context[addr+offs], var_store_count, n_vars);
} else if (offs > 0) {
new ctx_merge(&context[addr+offs], select);
n_forward_jumps += 1;
}
while (low <= high) {
offs = unpack4(pc);
if (offs < 0) {
new ctx_reset(&context[addr+offs],
var_store_count, n_vars);
} else if (offs > 0) {
new ctx_merge(&context[addr+offs], select, low);
n_forward_jumps += 1;
}
pc += 4;
low += 1;
}
select->n_branches = n_forward_jumps;
}
break;
case lookupswitch:
{
pc += 4 - (addr & 3);
offs = unpack4(pc); // default label
int n_pairs = unpack4(pc+4);
int n_forward_jumps = 0;
pc += 8;
ctx_split* select = new ctx_split(&context[addr]);
if (offs < 0) {
new ctx_reset(&context[addr+offs], var_store_count, n_vars);
} else if (offs > 0) {
new ctx_merge(&context[addr+offs], select);
n_forward_jumps += 1;
}
while (--n_pairs >= 0) {
offs = unpack4(pc+4);
if (offs < 0) {
new ctx_reset(&context[addr+offs],
var_store_count, n_vars);
} else if (offs > 0) {
new ctx_merge(&context[addr+offs], select, unpack4(pc));
n_forward_jumps += 1;
}
pc += 8;
}
select->n_branches = n_forward_jumps;
}
break;
case istore:
case lstore:
case astore:
pc += 1;
var_store_count[*pc++] += 1;
break;
case istore_0:
case istore_1:
case istore_2:
case istore_3:
var_store_count[*pc++ - istore_0] += 1;
break;
case lstore_0:
case lstore_1:
case lstore_2:
case lstore_3:
var_store_count[*pc++ - lstore_0] += 1;
break;
case astore_0:
case astore_1:
case astore_2:
case astore_3:
var_store_count[*pc++ - astore_0] += 1;
break;
case iinc:
var_store_count[pc[1]] += 1;
pc += 3;
break;
case wide:
pc += 1;
switch (*pc++) {
case istore:
case lstore:
case astore:
var_store_count[unpack2(pc)] += 1;
break;
case iinc:
var_store_count[unpack2(pc)] += 1;
pc += 2;
}
pc += 2;
break;
default:
pc += vbm_instruction_length[*pc];
}
}
for (i = n_vars; --i >= 0;) {
var_store_count[i] = 0;
}
}
void method_desc::parse_code(constant** constant_pool,
const field_desc* is_this)
{
#ifdef DEBUG
printf("Method %s\n", name.as_asciz());
#endif
const int indirect = 0x100;
byte* pc = code;
byte* end = pc + code_length;
const int max_stack_size = 256;
vbm_operand stack[max_stack_size+2];
int i;
for (i = 0; i < max_stack_size+2; i++) {
stack[i].equals = NULL;
}
vbm_operand* stack_bottom = stack+2; // avoid checks for non-empty stack
vbm_operand* sp = stack_bottom;
local_context* ctx;
byte prev_cop = nop;
bool super_finalize = false;
#ifdef INT8_DEFINED
int8 left_min;
int8 left_max;
int8 left_mask;
int8 right_min;
int8 right_max;
int8 right_mask;
#define LOAD_INT8_OPERANDS() \
left_min = LOAD_INT8(sp-4, min);\
left_max = LOAD_INT8(sp-4, max);\
left_mask = LOAD_INT8(sp-4, mask);\
right_min = LOAD_INT8(sp-2, min);\
right_max = LOAD_INT8(sp-2, max);\
right_mask = LOAD_INT8(sp-2, mask)
#define LOAD_INT8_OPERAND() \
left_min = LOAD_INT8(sp-2, min);\
left_max = LOAD_INT8(sp-2, max);\
left_mask = LOAD_INT8(sp-2, mask)
#endif
in_monitor = false;
stack[0].type = stack[1].type = tp_void;
cls->locks.clear();
basic_blocks_analysis();
for (i = 0; i < 32; i++) {
if (null_parameter_mask & (1 << i)) {
assert(i < n_vars);
vars[i].mask = 0;
vars[i].min = 0;
vars[i].max = MAX_ARRAY_LENGTH;
if (vars[i].type == tp_void) {
vars[i].type = tp_object;
}
}
}
if (!(attr & m_static)) {
vars[0].type = tp_self;
vars[0].mask = var_desc::vs_not_null;
vars[0].min = 0;
vars[0].max = MAX_ARRAY_LENGTH;
vars[0].equals = is_this;
}
if (attr & m_synchronized) { // add "this" to lock set if needed
cls->locks.acquire(is_this);
locksAtEntry.acquire(is_this);
}
while (pc < end) {
int addr = pc - code;
byte cop = *pc++;
#ifdef DUMP_BYTE_CODES
printf("%s: min=%d, max=%d, mask=0x%x\n",
vbm_instruction_mnemonic[cop],
sp[-1].min, sp[-1].max, sp[-1].mask);
#endif
for (ctx = context[addr]; ctx != NULL; ctx = ctx->next) {
sp = ctx->transfer(this, sp, cop, prev_cop);
}
switch (cop) {
case nop:
break;
case aconst_null:
sp->type = tp_object;
sp->mask = 0;
sp->min = sp->max = 0;
sp->index = NO_ASSOC_VAR;
sp += 1;
break;
case iconst_m1:
case iconst_0:
case iconst_1:
case iconst_2:
case iconst_3:
case iconst_4:
case iconst_5:
sp->type = tp_byte;
sp->mask = sp->min = sp->max = cop - iconst_0;
sp->index = NO_ASSOC_VAR;
sp += 1;
break;
case fconst_0:
case fconst_1:
case fconst_2:
sp->type = tp_float;
sp->index = NO_ASSOC_VAR;
sp += 1;
break;
case lconst_0:
case lconst_1:
sp[0].type = sp[1].type = tp_long;
sp[0].min = sp[0].max = sp[0].mask = 0;
sp[1].min = sp[1].max = sp[1].mask = cop - lconst_0;
sp[0].index = sp[1].index = NO_ASSOC_VAR;
sp += 2;
break;
case dconst_0:
case dconst_1:
sp[0].type = sp[1].type = tp_double;
sp[0].index = sp[1].index = NO_ASSOC_VAR;
sp += 2;
break;
case bipush:
sp->type = tp_int;
sp->mask = sp->min = sp->max = (signed char)*pc++;
sp->index = NO_ASSOC_VAR;
sp += 1;
break;
case sipush:
sp->type = tp_int;
sp->mask = sp->min = sp->max = (short)unpack2(pc);
sp->index = NO_ASSOC_VAR;
pc += 2;
sp += 1;
break;
case ldc:
{
constant* cp = constant_pool[*pc++];
sp->type = cp->type();
if (sp->type == tp_int) {
sp->mask = sp->min = sp->max = ((const_int*)cp)->value;
} else {
if (sp->type == tp_string) {
sp->min = sp->max = ((const_string*)cp)->length();
} else {
sp->min = 0;
sp->max = MAX_ARRAY_LENGTH;
}
sp->mask = var_desc::vs_not_null;
}
sp->index = NO_ASSOC_VAR;
sp->equals = is_const;
sp += 1;
}
break;
case ldc_w:
{
constant* cp = constant_pool[unpack2(pc)];
sp->type = cp->type();
if (sp->type == tp_int) {
sp->mask = sp->min = sp->max = ((const_int*)cp)->value;
} else {
if (sp->type == tp_string) {
sp->min = sp->max = ((const_string*)cp)->length();
} else {
sp->min = 0;
sp->max = MAX_ARRAY_LENGTH;
}
sp->mask = var_desc::vs_not_null;
}
sp->index = NO_ASSOC_VAR;
pc += 2;
sp += 1;
}
break;
case ldc2_w:
{
const_long* cp = (const_long*)constant_pool[unpack2(pc)];
sp->type = tp_long;
sp->index = NO_ASSOC_VAR;
sp->mask = sp->min = sp->max = cp->value.high;
sp += 1;
sp->type = tp_long;
sp->index = NO_ASSOC_VAR;
sp->mask = sp->min = sp->max = cp->value.low;
sp += 1;
pc += 2;
}
break;
case iload:
if (vars[*pc].type == tp_void) {
vars[*pc].type = tp_int;
vars[*pc].min = ranges[tp_int].min;
vars[*pc].max = ranges[tp_int].max;
vars[*pc].mask = ALL_BITS;
}
sp->type = vars[*pc].type;
sp->min = vars[*pc].min;
sp->max = vars[*pc].max;
sp->mask = vars[*pc].mask;
sp->index = *pc++;
sp += 1;
break;
case aload:
if (vars[*pc].type == tp_void) {
vars[*pc].type = tp_object;
vars[*pc].min = 0;
vars[*pc].max = MAX_ARRAY_LENGTH;
vars[*pc].mask = var_desc::vs_unknown;
}
sp->type = vars[*pc].type;
sp->mask = vars[*pc].mask;
sp->min = vars[*pc].min;
sp->max = vars[*pc].max;
sp->equals = vars[*pc].equals;
#ifdef DUMP_STACK
printf("<stack_top> := %x %s\n",
(int)sp->equals,
sp->equals == NULL ?
"<unknown>" : sp->equals->name.as_asciz()
);
#endif
sp->index = *pc++;
sp += 1;
break;
case lload:
{
int index = *pc++;
if (vars[index].type == tp_void) {
vars[index].type = tp_long;
vars[index].min = 0x80000000;
vars[index].max = 0x7fffffff;
vars[index].mask = 0xffffffff;
vars[index+1].min = 0x00000000;
vars[index+1].max = 0xffffffff;
vars[index+1].mask = 0xffffffff;
}
sp->type = tp_long;
sp->min = vars[index].min;
sp->max = vars[index].max;
sp->mask = vars[index].mask;
sp->index = index;
sp += 1;
index += 1;
sp->type = tp_long;
sp->min = vars[index].min;
sp->max = vars[index].max;
sp->mask = vars[index].mask;
sp += 1;
}
break;
case dload:
sp[0].type = sp[1].type = tp_double;
sp[0].index = sp[1].index = NO_ASSOC_VAR;
sp += 2; pc += 1;
break;
case fload:
sp->type = tp_float;
sp->index = NO_ASSOC_VAR;
sp += 1;
pc += 1;
break;
case iload_0:
case iload_1:
case iload_2:
case iload_3:
{
var_desc* var = &vars[cop - iload_0];
if (var->type == tp_void) {
var->type = tp_int;
var->min = ranges[tp_int].min;
var->max = ranges[tp_int].max;
var->mask = ALL_BITS;
}
sp->type = var->type;
sp->min = var->min;
sp->max = var->max;
sp->mask = var->mask;
sp->index = cop - iload_0;
sp += 1;
}
break;
case lload_0:
case lload_1:
case lload_2:
case lload_3:
{
int index = cop - lload_0;
if (vars[index].type == tp_void) {
vars[index].type = tp_long;
vars[index].min = 0x80000000;
vars[index].max = 0x7fffffff;
vars[index].mask = 0xffffffff;
vars[index+1].min = 0x00000000;
vars[index+1].max = 0xffffffff;
vars[index+1].mask = 0xffffffff;
}
sp->type = tp_long;
sp->min = vars[index].min;
sp->max = vars[index].max;
sp->mask = vars[index].mask;
sp->index = index;
sp += 1;
index += 1;
sp->type = tp_long;
sp->min = vars[index].min;
sp->max = vars[index].max;
sp->mask = vars[index].mask;
sp += 1;
}
break;
case dload_0:
case dload_1:
case dload_2:
case dload_3:
sp[0].type = sp[1].type = tp_double;
sp[0].index = sp[1].index = NO_ASSOC_VAR;
sp += 2;
break;
case fload_0:
case fload_1:
case fload_2:
case fload_3:
sp->type = tp_float;
sp->index = NO_ASSOC_VAR;
sp += 1;
break;
case aload_0:
case aload_1:
case aload_2:
case aload_3:
{
var_desc* var = &vars[cop - aload_0];
if (var->type == tp_void) {
if (cop == aload_0 && !(attr & m_static)) {
var->type = tp_self;
var->mask = var_desc::vs_not_null;
} else {
var->type = tp_object;
var->mask = var_desc::vs_unknown;
}
sp->min = 0;
sp->max = MAX_ARRAY_LENGTH;
}
sp->type = var->type;
sp->mask = var->mask;
sp->min = var->min;
sp->max = var->max;
sp->index = cop - aload_0;
sp->equals = vars[cop - aload_0].equals;
#ifdef DUMP_STACK
printf("<stack_top> := %x %s\n",
(int)sp->equals,
sp->equals == NULL ?
"<unknown>" : sp->equals->name.as_asciz()
);
#endif
sp += 1;
}
break;
case iaload:
case faload:
case aaload:
case baload:
case caload:
case saload:
sp -= 2;
check_array_index(addr, sp);
if (IS_ARRAY_TYPE(sp->type)) {
sp->type -= indirect;
} else {
sp->type =
(cop == aaload) ? tp_object :
(cop == baload) ? tp_byte :
(cop == caload) ? tp_char :
(cop == iaload) ? tp_int :
(cop == saload) ? tp_short : tp_float;
}
if (IS_INT_TYPE(sp->type)) {
sp->min = ranges[sp->type].min;
sp->max = ranges[sp->type].max;
sp->mask = sp->min|sp->max;
} else {
sp->min = 0;
sp->max = MAX_ARRAY_LENGTH;
sp->mask = var_desc::vs_unknown;
}
sp->index = NO_ASSOC_VAR;
sp += 1;
break;
case laload:
check_array_index(addr, sp-2);
sp[-1].type = sp[-2].type = tp_long;
sp[-1].index = sp[-2].index = NO_ASSOC_VAR;
sp[-2].min = 0x80000000;
sp[-2].max = 0x7fffffff;
sp[-2].mask = 0xffffffff;
sp[-1].min = 0x00000000;
sp[-1].max = 0xffffffff;
sp[-1].mask = 0xffffffff;
break;
case daload:
check_array_index(addr, sp-2);
sp[-1].type = sp[-2].type = tp_double;
sp[-1].index = sp[-2].index = NO_ASSOC_VAR;
break;
case istore:
{
var_desc* var = &vars[*pc];
sp -= 1;
var->max = sp->max;
var->min = sp->min;
var->mask = sp->mask;
var_store_count[*pc] += 1;
if (var->type == tp_void) {
var->type = tp_int;
}
pc += 1;
}
break;
case astore:
sp -= 1;
vars[*pc].mask = sp->mask;
vars[*pc].min = sp->min;
vars[*pc].max = sp->max;
var_store_count[*pc] += 1;
if (vars[*pc].type == tp_void) {
vars[*pc].type = tp_object;
}
vars[*pc].equals = sp->equals;
#ifdef DUMP_STACK
for (i = 0; i <= *pc; i++) {
printf("<var_%d> := %x %s\n",
i,
(int)vars[i].equals,
vars[i].equals == NULL ?
"<unknown>" : vars[i].equals->name.as_asciz()
);
}
#endif
pc += 1;
break;
case fstore:
pc += 1; sp -= 1;
break;
case lstore:
{
int index = *pc++;
sp -= 2;
vars[index].type = tp_long;
vars[index].max = sp[0].max;
vars[index].min = sp[0].min;
vars[index].mask = sp[0].mask;
vars[index+1].max = sp[1].max;
vars[index+1].min = sp[1].min;
vars[index+1].mask = sp[1].mask;
var_store_count[index] += 1;
}
break;
case dstore:
pc += 1; sp -= 2;
break;
case istore_0:
case istore_1:
case istore_2:
case istore_3:
{
var_desc* var = &vars[cop - istore_0];
sp -= 1;
var->max = sp->max;
var->min = sp->min;
var->mask = sp->mask;
if (var->type == tp_void) {
var->type = tp_int;
}
var_store_count[cop - istore_0] += 1;
}
break;
case astore_0:
case astore_1:
case astore_2:
case astore_3:
sp -= 1;
vars[cop - astore_0].mask = sp->mask;
vars[cop - astore_0].min = sp->min;
vars[cop - astore_0].max = sp->max;
if (vars[cop - astore_0].type == tp_void) {
vars[cop - astore_0].type = tp_object;
}
vars[cop - astore_0].equals = sp->equals;
var_store_count[cop - astore_0] += 1;
#ifdef DUMP_STACK
for (i = 0; i <= cop-astore_0; i++) {
printf("<var_%d> := %x %s\n",
i,
(int)vars[i].equals,
vars[i].equals == NULL ?
"<unknown>" : vars[i].equals->name.as_asciz()
);
}
#endif
break;
case fstore_0:
case fstore_1:
case fstore_2:
case fstore_3:
sp -= 1;
break;
case lstore_0:
case lstore_1:
case lstore_2:
case lstore_3:
{
int index = cop - lstore_0;
sp -= 2;
vars[index].type = tp_long;
vars[index].max = sp[0].max;
vars[index].min = sp[0].min;
vars[index].mask = sp[0].mask;
vars[index+1].max = sp[1].max;
vars[index+1].min = sp[1].min;
vars[index+1].mask = sp[1].mask;
var_store_count[index] += 1;
}
break;
case dstore_0:
case dstore_1:
case dstore_2:
case dstore_3:
sp -= 2;
break;
case iastore:
case fastore:
case aastore:
case bastore:
case castore:
case sastore:
sp -= 3;
check_array_index(addr, sp);
break;
case lastore:
case dastore:
sp -= 4;
check_array_index(addr, sp);
break;
case pop:
sp -= 1;
break;
case pop2:
sp -= 2;
break;
case dup_x0:
*sp = *(sp-1); sp += 1;
break;
case dup_x1:
sp[0]=sp[-1]; sp[-1]=sp[-2]; sp[-2]=sp[0]; sp++;
break;
case dup_x2:
sp[0]=sp[-1]; sp[-1]=sp[-2]; sp[-2]=sp[-3]; sp[-3]=sp[0]; sp++;
break;
case dup2_x0:
sp[0]=sp[-2]; sp[1] = sp[-1]; sp += 2;
break;
case dup2_x1:
sp[1]=sp[-1]; sp[0]=sp[-2]; sp[-1]=sp[-3]; sp[-2]=sp[1];
sp[-3]=sp[0]; sp += 2;
break;
case dup2_x2:
sp[1]=sp[-1]; sp[0]=sp[-2]; sp[-1]=sp[-3]; sp[-2] = sp[-4];
sp[-3]=sp[1]; sp[-4] = sp[0]; sp += 2;
break;
case Jswap:
{
vbm_operand tmp = sp[-1];
sp[-1] = sp[-2];
sp[-2] = tmp;
}
break;
case iadd:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, "+");
}
if (((sp[-1].max ^ sp[-2].max) < 0
|| ((sp[-1].max + sp[-2].max) ^ sp[-1].max) >= 0)
&& ((sp[-1].min ^ sp[-2].min) < 0
|| ((sp[-1].min + sp[-2].min) ^ sp[-1].min) >= 0))
{
sp[-2].max += sp[-1].max;
sp[-2].min += sp[-1].min;
} else {
sp[-2].max = ranges[tp_int].max;
sp[-2].min = ranges[tp_int].min;
}
sp[-2].mask = make_mask(minimum(first_set_bit(sp[-2].mask),
first_set_bit(sp[-1].mask)));
sp[-2].type = tp_int;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
case isub:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, "-");
}
if (((sp[-2].max ^ sp[-1].min) >= 0
|| ((sp[-2].max - sp[-1].min) ^ sp[-2].max) >= 0)
&& ((sp[-2].min ^ sp[-1].max) >= 0
|| ((sp[-2].min - sp[-1].max) ^ sp[-2].min) >= 0))
{
sp[-2].max -= sp[-1].min;
sp[-2].min -= sp[-1].max;
} else {
sp[-2].max = ranges[tp_int].max;
sp[-2].min = ranges[tp_int].min;
}
sp[-2].mask = make_mask(minimum(first_set_bit(sp[-2].mask),
first_set_bit(sp[-1].mask)));
sp[-2].type = tp_int;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
case imul:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, "*");
}
sp[-2].mask = make_mask(first_set_bit(sp[-2].mask) +
first_set_bit(sp[-1].mask));
if (sp[-2].mask == 0) {
message(msg_zero_result, addr, "*");
sp[-2].max = sp[-2].min = 0;
} else {
vbm_operand result;
if (calculate_multiply_range(result, sp[-2], sp[-1])) {
assert(result.min <= result.max);
sp[-2].max = result.max;
sp[-2].min = result.min;
} else { // overflow
sp[-2].max = ranges[tp_int].max;
sp[-2].min = ranges[tp_int].min;
}
}
sp[-2].type = tp_int;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
case idiv:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, "/");
}
if ((sp[-1].min > 0
&& (sp[-2].max < sp[-1].min && sp[-2].min > -sp[-1].min))
|| (sp[-1].max < 0
&& (-sp[-2].max > sp[-1].max && sp[-2].min > sp[-1].max)))
{
message(msg_zero_result, addr, "/");
sp[-2].min = sp[-2].max = 0;
} else if (sp[-1].min > 0) {
if (sp[-2].max < 0) {
sp[-2].min = sp[-2].min/sp[-1].min;
sp[-2].max = sp[-2].max/sp[-1].max;
} else if (sp[-2].min < 0) {
sp[-2].min = sp[-2].min/sp[-1].min;
sp[-2].max = sp[-2].max/sp[-1].min;
} else {
sp[-2].min = sp[-2].min/sp[-1].max;
sp[-2].max = sp[-2].max/sp[-1].min;
}
} else {
sp[-2].min = ranges[tp_int].min;
sp[-2].max = ranges[tp_int].max;
}
sp[-2].mask = make_mask(first_set_bit(sp[-2].mask) -
first_set_bit(sp[-1].mask));
sp[-2].type = tp_int;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
case irem:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, "%");
}
if ((sp[-1].min > 0
&& (sp[-2].max < sp[-1].min && sp[-2].min > -sp[-1].min))
|| (sp[-1].max < 0
&& (-sp[-2].max > sp[-1].max && sp[-2].min > sp[-1].max)))
{
message(msg_no_effect, addr);
} else if (sp[-1].min > 0) {
sp[-2].min = (sp[-2].min < 0) ? 1-sp[-1].max : 0;
sp[-2].max = sp[-1].max-1;
} else {
sp[-2].min = ranges[tp_int].min;
sp[-2].max = ranges[tp_int].max;
}
sp[-2].mask = ALL_BITS;
sp[-2].type = tp_int;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
case ishl:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, "<<");
}
if (sp[-1].max < 0) {
message(msg_shift_count, addr, "<<", "<=", (void*)sp[-1].max);
} else if(sp[-1].min >= 32) {
message(msg_shift_count, addr, "<<", ">=", (void*)sp[-1].min);
}
else if ((unsigned(sp[-1].max - sp[-1].min) < 256)
&& (sp[-1].max >= 32 || sp[-1].min < 0))
{
//
// Produce this message only if range of expression was
// restricted in comparence with domains of builtin types
//
message(msg_shift_range, addr, "<<",
(void*)sp[-1].min, (void*)sp[-1].max);
}
sp[-2].mask = make_lshift_mask(sp[-2].mask,
sp[-1].min, sp[-1].max);
if (sp[-2].mask == 0) {
message(msg_zero_result, addr, "<<");
}
if ((unsigned)sp[-1].max < 32 && (unsigned)sp[-1].min < 32) {
if (sp[-2].max >= 0) {
if (sp[-2].max << sp[-1].max >> sp[-1].max == sp[-2].max){
sp[-2].max = sp[-2].max << sp[-1].max;
} else {
sp[-2].max = ranges[tp_int].max;
sp[-2].min = ranges[tp_int].min;
}
} else {
if (sp[-2].max << sp[-1].min >> sp[-1].min == sp[-2].max){
sp[-2].max = sp[-2].max << sp[-1].min;
} else {
sp[-2].max = ranges[tp_int].max;
sp[-2].min = ranges[tp_int].min;
}
}
if (sp[-2].min >= 0) {
sp[-2].min <<= sp[-1].min;
} else {
if (sp[-2].min << sp[-1].max >> sp[-1].max == sp[-2].min){
sp[-2].min = sp[-2].min << sp[-1].max;
} else {
sp[-2].max = ranges[tp_int].max;
sp[-2].min = ranges[tp_int].min;
}
}
} else {
sp[-2].max = ranges[tp_int].max;
sp[-2].min = ranges[tp_int].min;
}
sp[-2].type = tp_int;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
case ishr:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, ">>");
}
if (sp[-1].max < 0) {
message(msg_shift_count, addr, ">>", "<=", (void*)sp[-1].max);
} else if(sp[-1].min >= 32) {
message(msg_shift_count, addr, ">>", ">=", (void*)sp[-1].min);
}
else if ((unsigned(sp[-1].max - sp[-1].min) < 256)
&& (sp[-1].max >= 32 || sp[-1].min < 0))
{
//
// Produce this message only if range of expression was
// restricted in comparence with domains of builtin types
//
message(msg_shift_range, addr, ">>",
(void*)sp[-1].min, (void*)sp[-1].max);
}
sp[-2].mask = make_rshift_mask(sp[-2].mask,
sp[-1].min, sp[-1].max);
if (sp[-2].mask == 0) {
message(msg_zero_result, addr, ">>");
}
if ((unsigned)sp[-1].max < 32 && (unsigned)sp[-1].min < 32) {
sp[-2].max = (sp[-2].max >= 0)
? sp[-2].max >> sp[-1].min : sp[-2].max >> sp[-1].max;
sp[-2].min = (sp[-2].min >= 0)
? sp[-2].min >> sp[-1].max : sp[-2].min >> sp[-1].min;
} else {
if (sp[-2].max < 0) sp[-2].max = -1;
if (sp[-2].min > 0) sp[-2].min = 0;
}
sp[-2].type = tp_int;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
case iushr:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, ">>>");
}
if (sp[-1].max < 0) {
message(msg_shift_count, addr, ">>>", "<=", (void*)sp[-1].max);
} else if(sp[-1].min >= 32) {
message(msg_shift_count, addr, ">>>", ">=", (void*)sp[-1].min);
}
else if ((unsigned(sp[-1].max - sp[-1].min) < 256)
&& (sp[-1].max >= 32 || sp[-1].min < 0))
{
//
// Produce this message only if range of expression was
// restricted in comparence with domains of builtin types
//
message(msg_shift_range, addr, ">>>",
(void*)sp[-1].min, (void*)sp[-1].max);
}
sp[-2].mask = make_rushift_mask(sp[-2].mask,
sp[-1].min, sp[-1].max);
if (sp[-2].mask == 0) {
message(msg_zero_result, addr, ">>>");
}
if ((unsigned)sp[-1].max < 32 && (unsigned)sp[-1].min < 32
&& sp[-2].min >= 0)
{
sp[-2].max >>= sp[-1].min;
sp[-2].min >>= sp[-1].max;
} else {
sp[-2].max = ranges[tp_int].max;
sp[-2].min = ranges[tp_int].min;
}
sp[-2].type = tp_int;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
case iand:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, "&");
}
if (*pc == ifeq || *pc == ifne) {
if (sp[-1].index >= 0 &&
sp[-2].min == sp[-2].mask && sp[-2].max == sp[-2].mask)
{
sp[-2].index = sp[-1].index;
}
else if (sp[-2].index < 0 ||
sp[-1].min != sp[-1].mask || sp[-1].max != sp[-1].mask)
{
sp[-2].index = NO_ASSOC_VAR;
}
} else {
sp[-2].index = NO_ASSOC_VAR;
}
if ((sp[-2].mask &= sp[-1].mask) == 0) {
message(msg_zero_result, addr, "&");
}
sp[-2].min = sp[-2].mask < 0 ? ranges[tp_int].min : 0;
sp[-2].max = (sp[-1].max & sp[-2].max) >= 0
? sp[-2].mask & ranges[tp_int].max : 0;
sp[-2].type = tp_int;
sp -= 1;
break;
case ior:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, "|");
}
sp[-2].mask |= sp[-1].mask;
sp[-2].min = minimum(sp[-1].min, sp[-2].min);
sp[-2].max = sp[-2].mask & ranges[tp_int].max;
if (sp[-2].min > sp[-2].max) {
sp[-2].max = sp[-2].min;
}
sp[-2].type = tp_int;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
case ixor:
if ((sp[-1].min == 0 && sp[-1].max == 0) ||
(sp[-2].min == 0 && sp[-2].max == 0))
{
message(msg_zero_operand, addr, "^");
}
sp[-2].mask |= sp[-1].mask;
sp[-2].min = sp[-2].mask < 0 ? ranges[tp_int].min : 0;
sp[-2].max = sp[-2].mask & ranges[tp_int].max;
sp[-2].type = tp_int;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
#ifdef INT8_DEFINED
case ladd:
LOAD_INT8_OPERANDS();
if ((right_min == 0 && right_max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, "+");
}
if (((right_max ^ left_max) < 0
|| ((right_max + left_max) ^ right_max) >= 0)
&& ((right_min ^ left_min) < 0
|| ((right_min + left_min) ^ right_min) >= 0))
{
left_max += right_max;
left_min += right_min;
} else {
left_min = INT8_MIN;
left_max = INT8_MAX;
}
left_mask = make_int8_mask(minimum(first_set_bit(right_mask),
first_set_bit(left_mask)));
STORE_INT8(sp-4, min, left_min);
STORE_INT8(sp-4, max, left_max);
STORE_INT8(sp-4, mask, left_mask);
sp[-4].index = NO_ASSOC_VAR;
sp -= 2;
break;
case lsub:
LOAD_INT8_OPERANDS();
if ((right_min == 0 && right_max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, "-");
}
if (((left_max ^ right_min) >= 0
|| ((left_max - right_min) ^ left_max) >= 0)
&& ((left_min ^ right_max) >= 0
|| ((left_min - right_max) ^ left_min) >= 0))
{
left_max -= right_min;
left_min -= right_max;
} else {
left_max = INT8_MAX;
left_min = INT8_MIN;
}
left_mask = make_int8_mask(minimum(first_set_bit(left_mask),
first_set_bit(right_mask)));
STORE_INT8(sp-4, min, left_min);
STORE_INT8(sp-4, max, left_max);
STORE_INT8(sp-4, mask, left_mask);
sp[-4].index = NO_ASSOC_VAR;
sp -= 2;
break;
case lmul:
LOAD_INT8_OPERANDS();
if ((right_min == 0 && right_max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, "*");
}
left_mask = make_int8_mask(first_set_bit(left_mask) +
first_set_bit(right_mask));
if (left_mask == 0) {
message(msg_zero_result, addr, "*");
STORE_INT8(sp-4, min, 0);
STORE_INT8(sp-4, max, 0);
} else {
if (!calculate_int8_multiply_range(sp-4, sp-2)) {
STORE_INT8(sp-4, min, INT8_MIN);
STORE_INT8(sp-4, max, INT8_MAX);
}
}
sp[-4].index = NO_ASSOC_VAR;
sp -= 2;
break;
case lidiv:
LOAD_INT8_OPERANDS();
if ((right_min == 0 && right_max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, "/");
}
if ((right_min > 0
&& (left_max < right_min && left_min > -right_min))
|| (right_max < 0
&& (-left_max > right_max && left_min > right_max)))
{
message(msg_zero_result, addr, "/");
left_min = left_max = 0;
} else if (right_min > 0) {
if (left_max < 0) {
left_min = left_min/right_min;
left_max = left_max/right_max;
} else if (left_min < 0) {
left_min = left_min/right_min;
left_max = left_max/right_min;
} else {
left_min = left_min/right_max;
left_max = left_max/right_min;
}
} else {
left_min = INT8_MIN;
left_max = INT8_MAX;
}
left_mask = make_int8_mask(first_set_bit(left_mask) -
first_set_bit(right_mask));
STORE_INT8(sp-4, min, left_min);
STORE_INT8(sp-4, max, left_max);
STORE_INT8(sp-4, mask, left_mask);
sp[-4].index = NO_ASSOC_VAR;
sp -= 2;
break;
case lrem:
LOAD_INT8_OPERANDS();
if ((right_min == 0 && right_max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, "%");
}
if ((right_min > 0
&& (left_max < right_min && left_min > -right_min))
|| (right_max < 0
&& (-left_max > right_max && left_min > right_max)))
{
message(msg_no_effect, addr);
} else if (right_min > 0) {
left_min = (left_min < 0) ? 1-right_max : 0;
left_max = right_max-1;
} else {
left_min = INT8_MIN;
left_max = INT8_MAX;
}
STORE_INT8(sp-4, min, left_min);
STORE_INT8(sp-4, max, left_max);
STORE_INT8(sp-4, mask, INT8_ALL_BITS);
sp[-4].index = NO_ASSOC_VAR;
sp -= 2;
break;
case lshl:
sp -= 1;
LOAD_INT8_OPERAND();
if ((sp->min == 0 && sp->max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, "<<");
}
if (sp->max < 0) {
message(msg_shift_count, addr, "<<", "<=", (void*)sp->max);
} else if(sp->min >= 64) {
message(msg_shift_count, addr, "<<", ">=", (void*)sp->min);
}
else if ((unsigned(sp->max - sp->min) < 256)
&& (sp->max >= 64 || sp->min < 0))
{
//
// Produce this message only if range of expression was
// restricted in comparence with domains of builtin types
//
message(msg_shift_range, addr, "<<",
(void*)sp->min, (void*)sp->max);
}
left_mask = make_int8_lshift_mask(left_mask,
sp->min, sp->max);
if (left_mask == 0) {
message(msg_zero_result, addr, "<<");
}
if ((unsigned)sp->max < 64 && (unsigned)sp->min < 64) {
if (left_max >= 0) {
if (left_max << sp->max >> sp->max == left_max){
left_max = left_max << sp->max;
} else {
left_max = INT8_MAX;
left_min = INT8_MIN;
}
} else {
if (left_max << sp->min >> sp->min == left_max){
left_max = left_max << sp->min;
} else {
left_max = INT8_MAX;
left_min = INT8_MIN;
}
}
if (left_min >= 0) {
left_min <<= sp->min;
} else {
if (left_min << sp->max >> sp->max == left_min){
left_min = left_min << sp->max;
} else {
left_max = INT8_MAX;
left_min = INT8_MIN;
}
}
} else {
left_max = INT8_MAX;
left_min = INT8_MIN;
}
STORE_INT8(sp-2, min, left_min);
STORE_INT8(sp-2, max, left_max);
STORE_INT8(sp-2, mask, left_mask);
sp[-2].index = NO_ASSOC_VAR;
break;
case lshr:
sp -= 1;
LOAD_INT8_OPERAND();
if ((sp->min == 0 && sp->max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, ">>");
}
if (sp->max < 0) {
message(msg_shift_count, addr, ">>", "<=", (void*)sp->max);
} else if(sp->min >= 64) {
message(msg_shift_count, addr, ">>", ">=", (void*)sp->min);
}
else if ((unsigned(sp->max - sp->min) < 256)
&& (sp->max >= 64 || sp->min < 0))
{
//
// Produce this message only if range of expression was
// restricted in comparence with domains of builtin types
//
message(msg_shift_range, addr, ">>",
(void*)sp->min, (void*)sp->max);
}
left_mask = make_int8_rshift_mask(left_mask,
sp->min, sp->max);
if (left_mask == 0) {
message(msg_zero_result, addr, ">>");
}
if ((unsigned)sp->max < 64 && (unsigned)sp->min < 64) {
left_max = (left_max >= 0)
? left_max >> sp->min : left_max >> sp->max;
left_min = (left_min >= 0)
? left_min >> sp->max : left_min >> sp->min;
} else {
if (left_max < 0) left_max = -1;
if (left_min > 0) left_min = 0;
}
STORE_INT8(sp-2, min, left_min);
STORE_INT8(sp-2, max, left_max);
STORE_INT8(sp-2, mask, left_mask);
sp[-2].index = NO_ASSOC_VAR;
break;
case lushr:
sp -= 1;
LOAD_INT8_OPERAND();
if ((sp->min == 0 && sp->max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, ">>>");
}
if (sp->max < 0) {
message(msg_shift_count, addr, ">>>", "<=", (void*)sp->max);
} else if(sp->min >= 64) {
message(msg_shift_count, addr, ">>>", ">=", (void*)sp->min);
}
else if ((unsigned(sp->max - sp->min) < 256)
&& (sp->max >= 64 || sp->min < 0))
{
//
// Produce this message only if range of expression was
// restricted in comparence with domains of builtin types
//
message(msg_shift_range, addr, ">>>",
(void*)sp->min, (void*)sp->max);
}
left_mask = make_int8_rushift_mask(left_mask, sp->min, sp->max);
if (left_mask == 0) {
message(msg_zero_result, addr, ">>>");
}
if ((unsigned)sp->max < 64 && (unsigned)sp->min < 64
&& left_min >= 0)
{
left_max >>= sp->min;
left_min >>= sp->max;
} else {
left_max = INT8_MAX;
left_min = INT8_MIN;
}
STORE_INT8(sp-2, min, left_min);
STORE_INT8(sp-2, max, left_max);
STORE_INT8(sp-2, mask, left_mask);
sp[-2].index = NO_ASSOC_VAR;
break;
case land:
LOAD_INT8_OPERANDS();
if ((right_min == 0 && right_max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, "&");
}
if ((left_mask &= right_mask) == 0) {
message(msg_zero_result, addr, "&");
}
left_min = left_mask < 0 ? INT8_MIN : 0;
left_max = (right_max & left_max) >= 0
? left_mask & INT8_MAX : 0;
STORE_INT8(sp-4, min, left_min);
STORE_INT8(sp-4, max, left_max);
STORE_INT8(sp-4, mask, left_mask);
sp[-4].index = NO_ASSOC_VAR;
sp -= 2;
break;
case lor:
LOAD_INT8_OPERANDS();
if ((right_min == 0 && right_max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, "|");
}
left_mask |= right_mask;
if (right_min < left_min) {
left_min = right_min;
}
left_max = left_mask & INT8_MAX;
if (left_max < left_min) {
left_max = left_min;
}
STORE_INT8(sp-4, min, left_min);
STORE_INT8(sp-4, max, left_max);
STORE_INT8(sp-4, mask, left_mask);
sp[-4].index = NO_ASSOC_VAR;
sp -= 2;
break;
case lxor:
LOAD_INT8_OPERANDS();
if ((right_min == 0 && right_max == 0) ||
(left_min == 0 && left_max == 0))
{
message(msg_zero_operand, addr, "^");
}
left_mask |= right_mask;
left_min = left_mask < 0 ? INT8_MIN : 0;
left_max = left_mask & INT8_MAX;
STORE_INT8(sp-4, min, left_min);
STORE_INT8(sp-4, max, left_max);
STORE_INT8(sp-4, mask, left_mask);
sp[-4].index = NO_ASSOC_VAR;
sp -= 2;
break;
case lneg:
LOAD_INT8_OPERAND();
if (left_min == left_max) {
left_min = left_max = left_mask = -left_max;
} else {
int8 min = left_min;
left_min = (left_max == INT8_MAX)? INT8_MIN : -left_max;
left_max = (min == INT8_MIN) ? INT8_MAX : -min;
left_mask = make_mask(first_set_bit(left_mask));
}
STORE_INT8(sp-2, min, left_min);
STORE_INT8(sp-2, max, left_max);
STORE_INT8(sp-2, mask, left_mask);
sp[-2].index = NO_ASSOC_VAR;
break;
#else
case ladd:
case lsub:
case lmul:
case lidiv:
case lrem:
case lor:
case lxor:
case land:
sp -= 2;
break;
case lshl:
case lshr:
case lushr:
sp -= 1;
break;
case lneg:
break;
#endif
case fadd:
case fsub:
case fmul:
case fdiv:
case frem:
sp -= 1;
break;
case dadd:
case dsub:
case dmul:
case ddiv:
case drem:
sp -= 2;
break;
case ineg:
if (sp[-1].min == sp[-1].max) {
sp[-1].mask = sp[-1].min = sp[-1].max = -sp[-1].max;
} else {
int min = sp[-1].min;
sp[-1].min = (sp[-1].max == ranges[tp_int].max)
? ranges[tp_int].min : -sp[-1].max;
sp[-1].max = (min == ranges[tp_int].min)
? ranges[tp_int].max : -min;
sp[-1].mask = make_mask(first_set_bit(sp[-1].mask));
}
sp[-1].type = tp_int;
sp[-1].index = NO_ASSOC_VAR;
break;
case fneg:
case dneg:
break;
case iinc:
{
var_desc* var = &vars[*pc];
var_store_count[*pc++] += 1;
int add = (signed char)*pc++;
if (((var->max^add) < 0 || ((var->max+add) ^ add) >= 0)
&& ((var->min^add) < 0 || ((var->min+add) ^ add) >= 0))
{
var->max += add;
var->min += add;
} else {
var->max = ranges[tp_int].max;
var->min = ranges[tp_int].min;
}
var->mask = make_mask(maximum(last_set_bit(var->mask),
last_set_bit(add))+1,
minimum(first_set_bit(var->mask),
first_set_bit(add)));
}
break;
case i2l:
if (prev_cop == imul || prev_cop == ishl) {
message(msg_overflow, addr);
}
sp->min = sp[-1].min;
sp->max = sp[-1].max;
sp->mask = sp[-1].mask;
sp->type = tp_long;
sp[-1].max >>= 31; // extend sign
sp[-1].min >>= 31;
sp[-1].mask >>= 31;
sp[-1].type = tp_long;
sp[-1].index = NO_ASSOC_VAR;
sp += 1;
break;
case f2l:
sp += 1;
nobreak;
case d2l:
sp[-2].type = tp_long;
sp[-2].index= NO_ASSOC_VAR;
sp[-2].min = 0x80000000;
sp[-2].max = 0x7fffffff;
sp[-2].mask = 0xffffffff;
sp[-1].type = tp_long;
sp[-1].min = 0x00000000;
sp[-1].max = 0xffffffff;
sp[-1].mask = 0xffffffff;
break;
case i2d:
case f2d:
sp[-1].type = sp[0].type = tp_double;
sp[-1].index = sp[0].index = NO_ASSOC_VAR;
sp += 1;
break;
case i2b:
if (sp[-1].max < ranges[tp_byte].min ||
sp[-1].min > ranges[tp_byte].max)
{
message(msg_conversion, addr, "byte");
}
else if ((sp[-1].mask << 24 >> 23) != (sp[-1].mask << 1)
&& sp[-1].mask != 0xffff // char to byte
&& sp[-1].mask != 0xff) // int & 0xFF to byte
{
message(msg_truncation, addr, "byte");
}
if (sp[-1].max > ranges[tp_byte].max ||
sp[-1].min < ranges[tp_byte].min)
{
sp[-1].max = ranges[tp_byte].max;
sp[-1].min = ranges[tp_byte].min;
}
sp[-1].mask = sp[-1].mask << 24 >> 24;
sp[-1].type = tp_byte;
sp[-1].index = NO_ASSOC_VAR;
break;
case i2f:
sp[-1].type = tp_float;
sp[-1].index = NO_ASSOC_VAR;
break;
case l2i:
#ifdef INT8_DEFINED
LOAD_INT8_OPERAND();
if (left_max < ranges[tp_int].min || left_min > ranges[tp_int].max)
{
message(msg_conversion, addr, "int");
} else if (sp[-2].mask != 0 && sp[-2].mask != ~0) {
message(msg_truncation, addr, "int");
}
if (left_min >= ranges[tp_int].min &&
left_max <= ranges[tp_int].max)
{
sp[-2].min = sp[-1].min;
sp[-2].max = sp[-1].max;
} else {
sp[-2].min = ranges[tp_int].min;
sp[-2].max = ranges[tp_int].max;
}
#else
sp[-2].min = ranges[tp_int].min;
sp[-2].max = ranges[tp_int].max;
#endif
sp[-2].type = tp_int;
sp[-2].mask = sp[-1].mask;
sp[-2].index = NO_ASSOC_VAR;
sp -= 1;
break;
case d2i:
sp -= 1;
sp[-1].type = tp_int;
sp[-1].min = ranges[tp_int].min;
sp[-1].max = ranges[tp_int].max;
sp[-1].mask = ALL_BITS;
sp[-1].index = NO_ASSOC_VAR;
break;
case l2f:
case d2f:
sp -= 1;
sp[-1].type = tp_float;
sp[-1].index = NO_ASSOC_VAR;
break;
case l2d:
sp[-1].type = sp[-2].type = tp_double;
sp[-1].index = sp[-2].index = NO_ASSOC_VAR;
break;
case f2i:
sp[-1].type = tp_int;
sp[-1].min = ranges[tp_int].min;
sp[-1].max = ranges[tp_int].max;
sp[-1].mask = ALL_BITS;
sp[-1].index = NO_ASSOC_VAR;
break;
case i2c:
if (sp[-1].max < ranges[tp_char].min ||
sp[-1].min > ranges[tp_char].max)
{
message(msg_conversion, addr, "char");
}
else if ((sp[-1].mask & 0x7fff0000) != 0 &&
(sp[-1].mask & 0x7fff0000) != 0x7fff0000)
{
message(msg_truncation, addr, "char");
}
if (sp[-1].max > ranges[tp_char].max ||
sp[-1].min < ranges[tp_char].min)
{
sp[-1].min = ranges[tp_char].min;
sp[-1].max = ranges[tp_char].max;
}
sp[-1].mask &= 0xFFFF;
sp[-1].type = tp_char;
sp[-1].index = NO_ASSOC_VAR;
break;
case i2s:
if (sp[-1].max < ranges[tp_short].min ||
sp[-1].min > ranges[tp_short].max)
{
message(msg_conversion, addr, "short");
} else if ((sp[-1].mask << 16 >> 15) != (sp[-1].mask << 1)) {
message(msg_truncation, addr, "short");
}
if (sp[-1].max > ranges[tp_short].max ||
sp[-1].min < ranges[tp_short].min)
{
sp[-1].max = ranges[tp_short].max;
sp[-1].min = ranges[tp_short].min;
}
sp[-1].mask = sp[-1].mask << 16 >> 16;
sp[-1].type = tp_short;
sp[-1].index = NO_ASSOC_VAR;
break;
case lcmp:
#ifdef INT8_DEFINED
LOAD_INT8_OPERANDS();
if (*pc == ifeq || *pc == ifne) {
if ((left_mask & right_mask) == 0
&& !((left_min == 0 && left_max == 0) ||
(right_min == 0 && right_max == 0)))
{
message(msg_disjoint_mask, addr);
}
}
if (left_min > right_max || left_max < right_min
|| ((*pc == ifge || *pc == iflt) && left_min >= right_max)
|| ((*pc == ifle || *pc == ifgt) && left_max <= right_min))
{
message(msg_same_result, addr);
}
else if (((*pc == ifgt || *pc == ifle) && left_min == right_max) ||
((*pc == iflt || *pc == ifge) && left_max == right_min))
{
message(msg_weak_cmp, addr);
}
#endif
sp -= 4;
sp->type = tp_int;
sp->min = -1;
sp->max = 1;
sp->mask = ALL_BITS;
sp->index = NO_ASSOC_VAR;
sp += 1;
break;
case dcmpl:
case dcmpg:
sp -= 2;
nobreak;
case fcmpl:
case fcmpg:
sp -= 2;
sp->type = tp_int;
sp->min = -1;
sp->max = 1;
sp->mask = ALL_BITS;
sp->index = NO_ASSOC_VAR;
sp += 1;
break;
case ifeq:
case ifne:
case iflt:
case ifge:
case ifgt:
case ifle:
sp -= 1;
if (sp->min > 0 || sp->max < 0 || sp->min == sp->max
|| (sp->min == 0 && (cop == iflt || cop == ifge))
|| (sp->max == 0 && (cop == ifgt || cop == ifle)))
{
message(msg_same_result, addr);
}
else if ((sp->min == 0 && (cop == ifle || cop == ifgt)) ||
(sp->max == 0 && (cop == ifge || cop == iflt)))
{
message(msg_weak_cmp, addr);
}
pc += 2;
break;
case if_icmpeq:
case if_icmpne:
if (sp[-2].min == sp[-2].max && sp[-1].min == sp[-1].max) {
message(msg_same_result, addr);
} else if ((sp[-2].mask & sp[-1].mask) == 0
&& sp[-2].type != tp_bool && sp[-1].type != tp_bool)
{
message(msg_disjoint_mask, addr);
}
nobreak;
case if_icmplt:
case if_icmpge:
case if_icmpgt:
case if_icmple:
if (sp[-2].min > sp[-1].max || sp[-2].max < sp[-1].min
|| ((cop == if_icmpge || cop == if_icmplt)
&& sp[-2].min >= sp[-1].max)
|| ((cop == if_icmple || cop == if_icmpgt)
&& sp[-2].max <= sp[-1].min))
{
message(msg_same_result, addr);
}
else if (((cop == if_icmpgt || cop == if_icmple)
&& sp[-2].min == sp[-1].max) ||
((cop == if_icmplt || cop == if_icmpge)
&& sp[-2].max == sp[-1].min))
{
message(msg_weak_cmp, addr);
}
if ((sp[-1].type == tp_short && sp[-2].type == tp_char)
|| (sp[-2].type == tp_short && sp[-1].type == tp_char))
{
message(msg_short_char_cmp, addr);
}
sp -= 2;
pc += 2;
break;
case if_acmpeq:
case if_acmpne:
if (sp[-1].type == tp_string && sp[-2].type == tp_string) {
message(msg_string_cmp, addr);
}
sp -= 2;
pc += 2;
break;
case jsr:
case goto_near:
pc += 2;
break;
case ret:
pc += 1;
break;
case tableswitch:
{
pc += 7 - (addr & 3);
int low = unpack4(pc);
int high = unpack4(pc+4);
sp -= 1;
if (low < sp->min) {
message(msg_incomp_case, addr, low);
}
if (high > sp->max) {
message(msg_incomp_case, addr, high);
}
pc += (high - low + 1)*4 + 8;
}
break;
case lookupswitch:
{
pc += 7 - (addr & 3);
int n_pairs = unpack4(pc);
pc += 4;
sp -= 1;
while (--n_pairs >= 0) {
int val = unpack4(pc);
int label = unpack4(pc+4);
pc += 8;
if (val < sp->min || val > sp->max || (val & ~sp->mask)) {
message(msg_incomp_case, addr+label, val);
}
}
}
break;
case ireturn:
case freturn:
case areturn:
sp -= 1;
break;
case dreturn:
case lreturn:
sp -= 2;
break;
case vreturn:
break;
case getfield:
sp -= 1;
nobreak;
case getstatic:
{
const_ref* field_ref = (const_ref*)constant_pool[unpack2(pc)];
const_name_and_type* nt =
(const_name_and_type*)constant_pool[field_ref->name_and_type];
const_utf8* field_name= (const_utf8*)constant_pool[nt->name];
const_utf8* desc = (const_utf8*)constant_pool[nt->desc];
const_class*cls_info=(const_class*)constant_pool[field_ref->cls];
const_utf8* cls_name=(const_utf8*)constant_pool[cls_info->name];
class_desc* obj_cls = class_desc::get(*cls_name);
field_desc* field = obj_cls->get_field(*field_name);
field->name_and_type = nt;
if (cop == getfield) {
check_variable_for_null(addr, sp);
}
if (cls->name == *cls_name) {
field->attr |= field_desc::f_used;
}
if (!in_monitor) {
accessors = new access_desc(field, cls,
get_line_number(addr),
accessors);
if (cop == getfield) {
if (sp->type == tp_self) {
accessors->attr |= access_desc::a_self;
}
if (sp->mask & var_desc::vs_new) {
accessors->attr |= access_desc::a_new;
}
}
}
int type = get_type(*desc);
sp->type = type;
if (IS_INT_TYPE(type)) {
sp->min = ranges[type].min;
sp->max = ranges[type].max;
sp->mask = sp->min|sp->max;
} else {
sp->mask = var_desc::vs_unknown;
sp->min = 0;
sp->max = MAX_ARRAY_LENGTH;
}
sp->equals = field;
sp->index = NO_ASSOC_VAR;
sp += 1;
if (type == tp_long || type == tp_double) {
sp->type = type;
sp->max = 0xffffffff;
sp->min = 0;
sp->mask = 0xffffffff;
sp[-1].max = 0x7fffffff;
sp[-1].min = 0x80000000;
sp[-1].mask = 0xffffffff;
sp += 1;
}
pc += 2;
#ifdef DUMP_STACK
printf("<stack_top> := %x %s\n",
(int)field,
field == NULL ?
"<unknown>" : field->name.as_asciz()
);
#endif
}
break;
case putfield:
case putstatic:
{
const_ref* field_ref = (const_ref*)constant_pool[unpack2(pc)];
const_name_and_type* nt =
(const_name_and_type*)constant_pool[field_ref->name_and_type];
const_utf8* desc = (const_utf8*)constant_pool[nt->desc];
const_class*cls_info=(const_class*)constant_pool[field_ref->cls];
const_utf8* cls_name=(const_utf8*)constant_pool[cls_info->name];
const_utf8* field_name= (const_utf8*)constant_pool[nt->name];
class_desc* obj_cls = class_desc::get(*cls_name);
field_desc* field = obj_cls->get_field(*field_name);
if (cls->name == *cls_name) {
field->attr |= field_desc::f_used;
if ((name != "<init>") && (name != "<clinit>") &&
(! (cls->locks.owns(is_this)))) {
// allow assignments to lock only inside monitor or constructor
// should also check whether monitor field is owned (if so, error)
if ((cls->locks.owns(field)) ||
(locksAtEntry.owns(field))) {
if (strchr(field->name.as_asciz(), '$') == NULL) {
// don't print warning for variable names with $
// because javac sometimes does weird stuff for class locks
message_at(msg_lock_assign2, cls->source_file,
get_line_number(addr), field);
}
} else {
if (strchr(field->name.as_asciz(), '$') == NULL) {
// don't print warning for variable names with $
// because javac sometimes does weird stuff for class locks
int linenumber = get_line_number(addr);
field->write(linenumber);
if (field->equals) {
const_cast<field_desc *>(field->equals)->write(linenumber);
// mark field as written to (const_cast req'd here)
}
}
}
}
}
if (!in_monitor) {
accessors = new access_desc(field, cls,
get_line_number(addr),
accessors);
}
int type = get_type(*desc);
sp -= (type == tp_long || type == tp_double) ? 2 : 1;
if (sp->equals != NULL) {
if (sp->equals->name_and_type != nt) {
/*field_desc* aux = new field_desc(*(sp->equals));
aux->name_and_type = nt;
sp->equals = aux;
equal_descs.push_back(aux);*/
sp->equals = is_const;
}
}
field->equals = sp->equals;
if (cop == putfield) {
sp -= 1;
if (!in_monitor) {
if (sp->type == tp_self) {
accessors->attr |= access_desc::a_self;
}
if (sp->mask & var_desc::vs_new) {
accessors->attr |= access_desc::a_new;
}
}
check_variable_for_null(addr, sp);
}
#ifdef DUMP_STACK
printf("%x %s := %x %s\n",
(int)field,
field_name->as_asciz(),
(int)field->equals,
field->equals == NULL ?
"<unknown>" : field->equals->name.as_asciz()
);
#endif
pc += 2;
}
break;
case invokespecial:
case invokevirtual:
case invokestatic:
case invokeinterface:
{
const_ref* method_ref = (const_ref*)constant_pool[unpack2(pc)];
const_class* cls_info =
(const_class*)constant_pool[method_ref->cls];
utf_string cls_name(*(const_utf8*)constant_pool[cls_info->name]);
class_desc* obj_cls = class_desc::get(cls_name);
const_name_and_type* nt =
(const_name_and_type*)constant_pool[method_ref->name_and_type];
utf_string mth_name(*(const_utf8*)constant_pool[nt->name]);
utf_string mth_desc(*(const_utf8*)constant_pool[nt->desc]);
int n_params = get_number_of_parameters(mth_desc);
int fp = n_params;
if (cop != invokestatic) {
fp += 1;
check_variable_for_null(addr, sp-fp);
}
#ifdef DUMP_STACK
printf("<stack_top> = %s\n",
sp[-fp].equals == NULL ?
"<unknown>" : sp[-fp].equals->name.as_asciz()
);
#endif
if (cls_name == "java/lang/Object") {
bool hold_lock = false;
Lock wait_on;
if (sp[-fp].equals != NULL) {
wait_on = sp[-fp].equals;
} else {
wait_on = NULL;
}
if (mth_name == "notify"
|| mth_name == "notifyAll"
|| mth_name == "wait") {
// check whether lock on object which is waited on is owned
if (wait_on != NULL) {
if (cls->locks.owns(wait_on)) {
hold_lock = true;
}
}
if (!hold_lock) {
message(msg_wait_nosync, addr,
(wait_on != NULL ?
wait_on->name.as_asciz() :
"<unknown>"),
&mth_name);
}
}
if (mth_name == "wait") {
wait_line = get_line_number(addr);
attr |= m_wait;
// check whether other locks are held
int nLocks = cls->locks.nLocks() - (hold_lock? 1:0) > 0;
if (nLocks > 0) {
message(msg_wait, addr);
// print all other locks
char buf[MAX_MSG_LENGTH - 40]; // buffer for locks
char* out = buf;
int n;
monitor_stack::const_iterator entry = cls->locks.begin();
while (entry != cls->locks.end()) {
if ((n = snprintf(out,
sizeof(buf)-(out-buf),
" %s,",
(*entry)->name.as_asciz())) == -1) {
// no space in buffer left - print "..." at end of buffer
sprintf(buf+sizeof(buf)-4, "...");
out = buf + sizeof(buf) - 1;
break;
}
out += n;
entry++;
}
*(out-1) = '\0';
message(msg_locklist, addr, cls->locks.nLocks(), buf);
n_messages--; // avoid counting message twice
}
}
} // end of wait/notify treatment
if ((cop == invokespecial) && (mth_name == "finalize")) {
super_finalize = true;
} else {
method_desc* method = obj_cls->get_method(mth_name, mth_desc);
int call_attr = 0;
if (cop != invokestatic && sp[-fp].type == tp_self) {
call_attr |= callee_desc::i_self;
}
if (in_monitor) {
call_attr |= callee_desc::i_synchronized;
}
callees = new callee_desc(cls, method, callees,
get_line_number(addr), call_attr);
if ((cls->locks.nLocks() > 0)
&& (method->name != "<init>")
&& (method->name != "<clinit>")
) {
// if locks are currently held...
// ... add call from innermost "pseudo method" to method
// exception: constructors can't own locks
method->attr |= m_concurrent;
Lock curr = cls->locks.getInnermost();
if (curr == is_this) {
call_attr |= callee_desc::i_self;
}
if ((curr->cls == cls) &&
(method->cls == cls)) { // ignore calls to other classes
const char* curr_name =
compound_name(cls->name.as_asciz(), curr->name.as_asciz());
class_desc* curr_cls = class_desc::get(utf_string(curr_name));
method_desc* caller_method =
curr_cls->get_method(utf_string("<synch>"), utf_string("()"));
// caller_method->attr |= method_desc::m_synchronized;
if (caller_method->vertex == NULL) {
caller_method->vertex = new graph_vertex(curr_cls);
}
if ((!(method->locksAtEntry.owns(curr))) &&
(sp[-fp].equals == is_this) && // (method->cls == cls)
(! (attr & m_synchronized))
// synch. methods are already covered by other mechanisms
) {
// context at method entry
method->locksAtEntry.acquire(curr);
// add call graph edge lock.<synch> -> method
// only if method is of current class
// and not added before
// callee
method->attr |= method_desc::m_sync_block;
if (method->vertex == NULL) {
method->vertex = new graph_vertex(obj_cls);
}
method->callees =
new callee_desc(obj_cls, method, method->callees,
get_line_number(addr), call_attr);
// lock was not in list, add to call graph
int caller_attr = 0;
// caller_attr |= method_desc::m_synchronized;
// attr |= m_synchronized; // crucial flag for later analysis?
method->build_call_graph(caller_method, method->callees,
caller_attr);
// add edge for pseudo method to call graph
}
}
} // end of pseudo method -> method call
if (n_params < 32) {
int mask = 0;
for (i = 0; i < n_params; i++) {
if ((sp[i-n_params].type == tp_object
|| sp[i-n_params].type == tp_string)
&& sp[i-n_params].mask == 0)
{
mask |= 1 << i;
}
}
if (cop != invokestatic) {
// Reference to target object i passed as 0 parameter
mask <<= 1;
}
method->null_parameter_mask |= mask;
}
}
int type = get_type(mth_desc);
sp -= fp;
if (type != tp_void) {
if (IS_INT_TYPE(type)) {
sp->min = ranges[type].min;
sp->max = ranges[type].max;
sp->mask = sp->min|sp->max;
} else {
sp->min = 0;
sp->max = MAX_ARRAY_LENGTH;
sp->mask = var_desc::vs_unknown;
sp->equals = NULL; // we don't know what we get back!
}
sp->index = NO_ASSOC_VAR;
sp->type = type;
sp += 1;
if (type == tp_long || type == tp_double) {
sp->type = type;
sp->max = 0xffffffff;
sp->min = 0;
sp->mask = 0xffffffff;
sp[-1].max = 0x7fffffff;
sp[-1].min = 0x80000000;
sp[-1].mask = 0xffffffff;
sp += 1;
}
}
pc += (cop == invokeinterface) ? 4 : 2;
}
break;
case anew:
sp->type = tp_object;
sp->mask = var_desc::vs_new|var_desc::vs_not_null;
sp->min = 0;
sp->max = MAX_ARRAY_LENGTH;
sp->index = NO_ASSOC_VAR;
sp->equals = getNew();
sp += 1;
pc += 2;
break;
case newarray:
sp[-1].type = array_type[*pc++] + indirect;
sp[-1].mask = var_desc::vs_new|var_desc::vs_not_null;
sp[-1].index = NO_ASSOC_VAR;
if (sp[-1].min < 0) {
if (sp[-1].max < 0) {
message(msg_neg_len, addr, sp[-1].min, sp[-1].max);
} else if (sp[-1].min > -128) {
message(msg_maybe_neg_len, addr, sp[-1].min, sp[-1].max);
}
}
if (sp[-1].min < 0) {
sp[-1].min = 0;
if (sp[-1].max < 0) {
sp[-1].max = 0;
}
}
break;
case anewarray:
sp[-1].type = tp_object + indirect;
sp[-1].mask = var_desc::vs_new|var_desc::vs_not_null;
sp[-1].index = NO_ASSOC_VAR;
if (sp[-1].min < 0) {
if (sp[-1].max < 0) {
message(msg_neg_len, addr, sp[-1].min, sp[-1].max);
} else if (sp[-1].min > -128) {
message(msg_maybe_neg_len, addr, sp[-1].min, sp[-1].max);
}
}
if (sp[-1].min < 0) {
sp[-1].min = 0;
if (sp[-1].max < 0) {
sp[-1].max = 0;
}
}
pc += 2;
break;
case arraylength:
sp[-1].type = tp_int;
sp[-1].mask = make_mask(last_set_bit(sp[-1].max), 0);
break;
case athrow:
sp -= 1;
break;
case checkcast:
pc += 2;
break;
case instanceof:
sp[-1].type = tp_bool;
sp[-1].min = 0;
sp[-1].max = 1;
sp[-1].mask = 1;
sp[-1].index = NO_ASSOC_VAR;
pc += 2;
break;
case monitorenter:
{
in_monitor += 1;
sp -= 1;
if (sp->equals != NULL) {
// mark monitor ownership; use monitor count in later version
Lock curr = cls->locks.getInnermost();
cls->locks.acquire(sp->equals);
cls->usedLocks.acquire(sp->equals);
if (sp->equals == is_this) {
attr |= m_synchronized; // mark method as "synchronized"
}
if (sp->equals->name_and_type != NULL) {
class_desc* curr_cls;
const class_desc* curr_type;
method_desc* caller_method;
const char* curr_class_name;
if (curr != NULL) {
// already holding a lock
// get class descriptor of current innermost lock (a)
// instead of normal class descriptor, use TYPE.variable_name
// this will allow us to distinguish between instances (imperfectly)
/* utf_string* curr_class_type =
dynamic_cast<utf_string*>(constant_pool[curr->name_and_type->desc]); */
// if method is synchronized, use method as caller - otherwise,
// use pseudo method OWNER.var_name.<synch>
if (curr == is_this) {
curr_type = curr_cls = cls;
caller_method = this;
} else {
if (curr->name_and_type->name == 0) {
curr_cls = cls;
} else {
curr_cls = curr->cls;
}
curr_type = curr_cls;
// use pseudo class name "owner.name" to distinguish between
// fields of different objects
curr_class_name = curr_cls->name.as_asciz();
const char* curr_name =
compound_name(curr_class_name, curr->name.as_asciz());
curr_cls = class_desc::get(utf_string(curr_name));
caller_method =
curr_cls->get_method(utf_string("<synch>"), utf_string("()"));
// caller_method->attr |= method_desc::m_synchronized;
// disable?
}
} else { // no lock acquired yet in this method
// add call graph edge (this method) -> (lock)
// get method descriptor of this method
caller_method = this;
curr_type = curr_cls = cls;
curr_class_name = curr_cls->name.as_asciz();
}
if (sp->equals->cls == curr_type) {
// only analyze locks on fields of current class
if (caller_method->vertex == NULL) {
caller_method->vertex = new graph_vertex(curr_cls);
}
// get class descriptor of object type of new lock (b)
const char* lock_name =
compound_name(sp->equals->cls->name.as_asciz(), // class name
sp->equals->name.as_asciz()); // field name
class_desc* obj_cls = class_desc::get(utf_string(lock_name));
// get descriptor of dummy method "owner.b.<synch>"
method_desc* method =
obj_cls->get_method(utf_string("<synch>"),
utf_string("()"));
method->attr |= method_desc::m_synchronized;
if (method->vertex == NULL) {
method->vertex = new graph_vertex(obj_cls);
}
obj_cls->source_file = cls->source_file;
// add call from a.<synch> to b.<synch>
int call_attr = 0;
call_attr |= callee_desc::i_synchronized;
if (sp->equals->cls == cls) {
call_attr |= callee_desc::i_self;
}
method->callees =
new callee_desc(obj_cls, method, method->callees,
get_line_number(addr), call_attr);
if (caller_method->attr & m_sync_block) {
method->attr |= m_sync_block;
}
int caller_attr= 0;
caller_attr |= method_desc::m_synchronized;
method->build_call_graph(caller_method, method->callees,
caller_attr);
}
#ifdef DUMP_MONITOR
printf("%s acquires lock on %s.%s (type %s)\n",
cls->name.as_asciz(),
sp->equals->cls->name.as_asciz(),
sp->equals->name.as_asciz(),
((const_utf8*)constant_pool[sp->equals->name_and_type->desc])->as_asciz()
);
#endif
}
}
}
break;
case monitorexit:
{
if (in_monitor > 0) {
in_monitor -= 1;
}
sp -= 1;
if (sp->equals != NULL) {
#ifdef DUMP_MONITOR
printf("%s relinquishes lock on %s.%s\n",
cls->name.as_asciz(),
sp->equals->cls->name.as_asciz(),
sp->equals->name.as_asciz()
);
#endif
cls->locks.release(sp->equals);
}
}
break;
case wide:
switch (*pc++) {
case aload:
{
var_desc* var = &vars[unpack2(pc)];
if (var->type == tp_void) {
var->type = tp_object;
var->min = 0;
var->max = MAX_ARRAY_LENGTH;
var->mask = var_desc::vs_unknown;
}
sp->type = var->type;
sp->min = var->min;
sp->max = var->max;
sp->mask = var->mask;
sp->index = var - vars;
sp->equals = var->equals;
sp += 1;
}
break;
case iload:
{
var_desc* var = &vars[unpack2(pc)];
if (var->type == tp_void) {
var->type = tp_int;
var->min = ranges[tp_int].min;
var->max = ranges[tp_int].max;
var->mask = ALL_BITS;
}
sp->type = var->type;
sp->min = var->min;
sp->max = var->max;
sp->mask = var->mask;
sp->index = var - vars;
sp += 1;
}
break;
case dload:
sp[0].type = sp[1].type = tp_double;
sp[0].index = sp[1].index = NO_ASSOC_VAR;
sp += 2;
break;
case lload:
{
int index = unpack2(pc);
if (vars[index].type == tp_void) {
vars[index].type = tp_long;
vars[index].min = 0x80000000;
vars[index].max = 0x7fffffff;
vars[index].mask = 0xffffffff;
vars[index+1].min = 0x00000000;
vars[index+1].max = 0xffffffff;
vars[index+1].mask = 0xffffffff;
}
sp->type = tp_long;
sp->min = vars[index].min;
sp->max = vars[index].max;
sp->mask = vars[index].mask;
sp->index = index;
sp += 1;
index += 1;
sp->type = tp_long;
sp->min = vars[index].min;
sp->max = vars[index].max;
sp->mask = vars[index].mask;
sp += 1;
}
break;
case fload:
sp->type = tp_float;
sp->index = NO_ASSOC_VAR;
sp += 1;
break;
case istore:
{
int index = unpack2(pc);
var_store_count[index] += 1;
var_desc* var = &vars[index];
sp -= 1;
var->min = sp->min;
var->max = sp->max;
var->mask = sp->mask;
if (var->type == tp_void) {
var->type = tp_int;
}
}
break;
case astore:
{
int index = unpack2(pc);
var_store_count[index] += 1;
sp -= 1;
vars[index].min = sp->min;
vars[index].max = sp->max;
vars[index].mask = sp->mask;
if (vars[index].type == tp_void) {
vars[index].type = tp_object;
}
}
break;
case fstore:
sp -= 1;
break;
case lstore:
{
int index = unpack2(pc);
sp -= 2;
vars[index].type = tp_long;
vars[index].max = sp[0].max;
vars[index].min = sp[0].min;
vars[index].mask = sp[0].mask;
vars[index+1].max = sp[1].max;
vars[index+1].min = sp[1].min;
vars[index+1].mask = sp[1].mask;
var_store_count[index] += 1;
}
break;
case dstore:
sp -= 2;
break;
case ret:
break;
case iinc:
{
int index = unpack2(pc);
var_store_count[index] += 1;
var_desc* var = &vars[index];
int add = (short)unpack2(pc+2);
if (((var->max^add) < 0 || ((var->max+add) ^ add) >= 0)
&& ((var->min^add) < 0 || ((var->min+add) ^ add) >= 0))
{
var->max += add;
var->min += add;
} else {
var->max = ranges[tp_int].max;
var->min = ranges[tp_int].min;
}
var->mask = make_mask(maximum(last_set_bit(var->mask),
last_set_bit(add))+1,
minimum(first_set_bit(var->mask),
first_set_bit(add)));
pc += 2;
}
}
pc += 2;
break;
case multianewarray:
{
int dimensions = pc[2];
sp -= dimensions;
sp->type = tp_int + indirect*dimensions;
sp->index = NO_ASSOC_VAR;
sp->mask = var_desc::vs_new|var_desc::vs_not_null;
for (int j = 0; j < dimensions; j++) {
if (sp[j].min < 0) {
if (sp[j].max < 0) {
message(msg_neg_len, addr, sp[j].min, sp[j].max);
} else if (sp[j].min > -128) {
message(msg_maybe_neg_len, addr,
sp[j].min, sp[j].max);
}
}
}
if (sp->min < 0) {
sp->min = 0;
if (sp->max < 0) {
sp->max = 0;
}
}
sp += 1;
pc += 3;
break;
}
case ifnull:
case ifnonnull:
pc += 2;
sp -= 1;
break;
case jsr_w:
case goto_w:
pc += 4;
break;
default:
assert(false/*Illegal byte code*/);
}
prev_cop = cop;
}
assert(sp == stack_bottom);
// pop-up local variables
for (ctx = context[code_length]; ctx != NULL; ctx = ctx->next) {
sp = ctx->transfer(this, sp, unused, prev_cop);
}
// reset class fields attributes
for (field_desc* field = cls->fields; field != NULL; field = field->next){
field->attr &= ~field_desc::f_used;
}
if (name == "finalize" && !super_finalize) {
message(msg_super_finalize, 0);
}
// cleanup
if (attr & m_synchronized) { // remove "this" from lock set if needed
cls->locks.release(is_this);
}
/*
for (field_desc** fd = equal_descs.begin(); fd != equal_descs.end(); fd++) {
delete *fd;
}
equal_descs.clear();
*/
for (i = code_length; i >= 0; i--) {
local_context* next;
for (ctx = context[i]; ctx != NULL; ctx = next) {
next = ctx->next;
delete ctx;
}
}
delete[] var_store_count;
delete[] line_table;
delete[] context;
}
field_desc* method_desc::getNew() {
char* name = (char *)malloc((size_t)10);
strcpy(name, "<new>");
// assert(equal_descs.size() < 10000); // 4 characters allowed
assert(new_cnt < 10000); // 4 characters allowed
snprintf(name + strlen(name), 4, "%d", new_cnt++);
const char* fd_name = stringPool.add(name);
free(name);
field_desc* fd = new field_desc(utf_string(fd_name), cls, NULL);
// equal_descs.push_back(fd);
return fd;
}
const char* compound_name (const char* first, const char* second) {
char* result =
(char *)malloc((size_t)(strlen(first) +
strlen(second) + 2));
/* Add one byte for '.' and for '\0'. */
strcpy(result, first);
char* tmp = result + strlen(first);
// point to \0
*tmp = '.';
strcpy(++tmp, second);
const char* retval = stringPool.add(result);
free(result);
return retval;
}