#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(" := %x %s\n", (int)sp->equals, sp->equals == NULL ? "" : 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(" := %x %s\n", (int)sp->equals, sp->equals == NULL ? "" : 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(" := %x %s\n", i, (int)vars[i].equals, vars[i].equals == NULL ? "" : 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(" := %x %s\n", i, (int)vars[i].equals, vars[i].equals == NULL ? "" : 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(" := %x %s\n", (int)field, field == NULL ? "" : 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 != "") && (name != "") && (! (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->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 ? "" : 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(" = %s\n", sp[-fp].equals == NULL ? "" : 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() : ""), &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 != "") && (method->name != "") ) { // 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(""), 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. -> 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(constant_pool[curr->name_and_type->desc]); */ // if method is synchronized, use method as caller - otherwise, // use pseudo method OWNER.var_name. 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(""), 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." method_desc* method = obj_cls->get_method(utf_string(""), 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. to b. 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, ""); // 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; }