#include "local_context.hh"
/*
** every subclass of local_context has a transfer method which keeps track
** of the variable states during branching. These transfer methods are
** called in method_desc::parse_code(
** constant** constant_pool, const field_desc* is_this)
** for every bytecode and at the end after parsing all the bytecode of the method
** before cleanup to pop up all the local variables.
*/
vbm_operand* ctx_push_var::transfer(method_desc* method, vbm_operand* sp,
byte, byte&)
{
var_desc* var = &method->vars[var_index];
if (var->type == tp_void) {
if (IS_INT_TYPE(var_type)) {
var->min = ranges[var_type].min;
var->max = ranges[var_type].max;
var->mask = var->min|var->max;
} else if (var_type == tp_long) {
var[0].min = 0x80000000;
var[0].max = 0x7fffffff;
var[0].mask = 0xffffffff;
var[1].min = 0;
var[1].max = 0xffffffff;
var[1].mask = 0xffffffff;
} else {
var->mask = (var->type == tp_self)
? var_desc::vs_not_null : var_desc::vs_unknown;
var->min = 0;
var->max = MAX_ARRAY_LENGTH;
}
}
var->type = var_type;
var->name = *var_name;
var->start_pc = var_start_pc;
return sp;
}
vbm_operand* ctx_pop_var::transfer(method_desc* method, vbm_operand* sp,
byte, byte&)
{
var_desc* var = &method->vars[var_index];
for (field_desc* field = method->cls->fields;
field != NULL;
field = field->next)
{
// Do not produce message about shadowing of class camponent by local
// variable in case when local variable is formal parameter of the
// method and programmer explicitly refer class object component
// by "this": this.x = x;
if (field->name == var->name
&& (!(field->attr & field_desc::f_used)
|| var->start_pc != 0 /* not formal parameter*/))
{
method->message(msg_shadow_local, var->start_pc,
&var->name, method->cls);
break;
}
}
var->type = tp_void;
var->name = utf_string("???");
return sp;
}
vbm_operand* ctx_split::transfer(method_desc* method, vbm_operand* sp,
byte cop, byte& prev_cop)
{
if (n_branches > 0 && cmd == cmd_save_ctx) {
vars = new var_desc[method->n_vars];
memcpy(vars, method->vars, method->n_vars*sizeof(var_desc));
} else {
vars = NULL;
}
switch_var_index = -1;
in_monitor = method->in_monitor;
vbm_operand* left_op = sp-2;
vbm_operand* right_op = sp-1;
switch (cop) {
case jsr:
case jsr_w:
sp->type = tp_object;
sp->mask = var_desc::vs_not_null;
sp->min = 0;
sp->max = MAX_ARRAY_LENGTH;
stack_pointer = sp+1;
break;
case tableswitch:
case lookupswitch:
stack_pointer = right_op;
switch_var_index = right_op->index;
break;
case ifeq:
if (vars != NULL && right_op->index >= 0) {
// state at the branch address
var_desc* var = &vars[right_op->index];
if (prev_cop == iand) {
// Operation of form (x & const) == 0
if (IS_INT_TYPE(var->type)) {
var->mask &= ~right_op->mask;
}
} else {
var->max = var->min = 0;
if (IS_INT_TYPE(var->type)) {
var->mask = 0;
}
}
}
stack_pointer = right_op;
break;
case ifne:
if (right_op->index >= 0) { // value of local var. was pushed on stack
// state after if
var_desc* var = &method->vars[right_op->index];
if (prev_cop == iand) {
// Operation of form (x & const) != 0
if (IS_INT_TYPE(var->type)) {
var->mask &= ~right_op->mask;
}
} else {
var->max = var->min = 0;
if (IS_INT_TYPE(var->type)) {
var->mask = 0;
}
}
}
stack_pointer = right_op;
break;
case iflt:
if (right_op->index >= 0) {
// state after if
var_desc* var = &method->vars[right_op->index];
if (var->min < 0) var->min = 0;
if (var->max < 0) var->max = 0;
if (IS_INT_TYPE(var->type)) {
var->mask &= ~SIGN_BIT;
}
if (vars != NULL) { // forward branch
// state at the branch address
var = &vars[right_op->index];
if (var->min >= 0) var->min = -1;
if (var->max >= 0) var->max = -1;
}
}
stack_pointer = right_op;
break;
case ifge:
if (right_op->index >= 0) {
// state after if
var_desc* var = &method->vars[right_op->index];
if (var->min >= 0) var->min = -1;
if (var->max >= 0) var->max = -1;
if (vars != NULL) { // forward branch
// state at the branch address
var = &vars[right_op->index];
if (var->min < 0) var->min = 0;
if (var->max < 0) var->max = 0;
if (IS_INT_TYPE(var->type)) {
var->mask &= ~SIGN_BIT;
}
}
}
stack_pointer = right_op;
break;
case ifgt:
if (right_op->index >= 0) {
// state after if
var_desc* var = &method->vars[right_op->index];
if (var->min > 0) var->min = 0;
if (var->max > 0) var->max = 0;
if (vars != NULL) { // forward branch
// state at the branch address
var = &vars[right_op->index];
if (var->min <= 0) var->min = 1;
if (var->max <= 0) var->max = 1;
if (IS_INT_TYPE(var->type)) {
var->mask &= ~SIGN_BIT;
}
}
}
stack_pointer = right_op;
break;
case ifle:
if (right_op->index >= 0) {
// state after if
var_desc* var = &method->vars[right_op->index];
if (var->min <= 0) var->min = 1;
if (var->max <= 0) var->max = 1;
if (IS_INT_TYPE(var->type)) {
var->mask &= ~SIGN_BIT;
}
if (vars != NULL) { // forward branch
// state at the branch address
var = &vars[right_op->index];
if (var->min > 0) var->min = 0;
if (var->max > 0) var->max = 0;
}
}
stack_pointer = right_op;
break;
case if_icmpeq:
if (vars != NULL) {
// state at the branch address
if (right_op->index >= 0) {
var_desc* var = &vars[right_op->index];
if (var->min < left_op->min) var->min = left_op->min;
if (var->max > left_op->max) var->max = left_op->max;
if (var->min > var->max) var->min = var->max; // recovery
if (IS_INT_TYPE(var->type)) {
var->mask &= left_op->mask;
}
}
if (left_op->index >= 0) {
var_desc* var = &vars[left_op->index];
if (var->min < right_op->min) var->min = right_op->min;
if (var->max > right_op->max) var->max = right_op->max;
if (var->min > var->max) var->min = var->max; // recovery
if (IS_INT_TYPE(var->type)) {
var->mask &= right_op->mask;
}
}
}
stack_pointer = left_op;
break;
case if_icmpne:
if (right_op->index >= 0) {
// state after if
var_desc* var = &method->vars[right_op->index];
if (var->min < left_op->min) var->min = left_op->min;
if (var->max > left_op->max) var->max = left_op->max;
if (var->min > var->max) var->min = var->max; // recovery
if (IS_INT_TYPE(var->type)) {
var->mask &= left_op->mask;
}
}
if (left_op->index >= 0) {
var_desc* var = &method->vars[left_op->index];
if (var->min < right_op->min) var->min = right_op->min;
if (var->max > right_op->max) var->max = right_op->max;
if (var->min > var->max) var->min = var->max; // recovery
if (IS_INT_TYPE(var->type)) {
var->mask &= right_op->mask;
}
}
stack_pointer = left_op;
break;
case if_icmplt:
if (right_op->index >= 0) {
// left >= right
var_desc* var = &method->vars[right_op->index];
if (var->max > left_op->max) {
var->max = left_op->max;
if (var->min > var->max) var->min = var->max;
}
if (vars != NULL) {
// left < right
var = &vars[right_op->index];
if (var->min <= left_op->min) {
var->min = left_op->min == ranges[tp_int].max
? left_op->min : left_op->min+1;
if (var->min > var->max) var->max = var->min;
}
}
}
if (left_op->index >= 0) {
// left >= right
var_desc* var = &method->vars[left_op->index];
if (var->min < right_op->min) {
var->min = right_op->min;
if (var->min > var->max) var->max = var->min;
}
if (vars != NULL) {
// left < right
var = &vars[left_op->index];
if (var->max >= right_op->max) {
var->max = right_op->max == ranges[tp_int].min
? right_op->max : right_op->max-1;
if (var->min > var->max) var->min = var->max;
}
}
}
stack_pointer = left_op;
break;
case if_icmple:
if (right_op->index >= 0) {
// left > right
var_desc* var = &method->vars[right_op->index];
if (var->max >= left_op->max) {
var->max = left_op->max == ranges[tp_int].min
? left_op->max : left_op->max-1;
if (var->min > var->max) var->min = var->max;
}
if (vars != NULL) {
// left <= right
var = &vars[right_op->index];
if (var->min < left_op->min) {
var->min = left_op->min;
if (var->min > var->max) var->max = var->min;
}
}
}
if (left_op->index >= 0) {
// left > right
var_desc* var = &method->vars[left_op->index];
if (var->min <= right_op->min) {
var->min = right_op->min == ranges[tp_int].max
? right_op->min : right_op->min+1;
if (var->min > var->max) var->max = var->min;
}
if (vars != NULL) {
// left <= right
var = &vars[left_op->index];
if (var->max > right_op->max) {
var->max = right_op->max;
if (var->min > var->max) var->min = var->max;
}
}
}
stack_pointer = left_op;
break;
case if_icmpgt:
if (right_op->index >= 0) {
// left <= right
var_desc* var = &method->vars[right_op->index];
if (var->min < left_op->min) {
var->min = left_op->min;
if (var->min > var->max) var->max = var->min;
}
if (vars != NULL) {
// left > right
var = &vars[right_op->index];
if (var->max >= left_op->max) {
var->max = left_op->max == ranges[tp_int].min
? left_op->max : left_op->max-1;
if (var->min > var->max) var->min = var->max;
}
}
}
if (left_op->index >= 0) {
// left <= right
var_desc* var = &method->vars[left_op->index];
if (var->max > right_op->max) {
var->max = right_op->max;
if (var->min > var->max) var->min = var->max;
}
if (vars != NULL) {
// left > right
var = &vars[left_op->index];
if (var->min <= right_op->min) {
var->min = right_op->min == ranges[tp_int].max
? right_op->min : right_op->min+1;
if (var->min > var->max) var->max = var->min;
}
}
}
stack_pointer = left_op;
break;
case if_icmpge:
if (right_op->index >= 0) {
// left < right
var_desc* var = &method->vars[right_op->index];
if (var->min <= left_op->min) {
var->min = left_op->min == ranges[tp_int].max
? left_op->min : left_op->min+1;
if (var->min > var->max) var->max = var->min;
}
if (vars != NULL) {
// left >= right
var = &vars[right_op->index];
if (var->max > left_op->max) {
var->max = left_op->max;
if (var->min > var->max) var->min = var->max;
}
}
}
if (left_op->index >= 0) {
// left < right
var_desc* var = &method->vars[left_op->index];
if (var->max >= right_op->max) {
var->max = right_op->max == ranges[tp_int].min
? right_op->max : right_op->max-1;
if (var->min > var->max) var->min = var->max;
}
if (vars != NULL) {
// left >= right
var = &vars[left_op->index];
if (var->min < right_op->min) {
var->min = right_op->min;
if (var->min > var->max) var->max = var->min;
}
}
}
stack_pointer = left_op;
break;
case if_acmpeq:
if (vars != NULL) {
if (right_op->index >= 0) {
vars[right_op->index].mask &= left_op->mask;
}
if (left_op->index >= 0) {
vars[left_op->index].mask &= right_op->mask;
}
}
stack_pointer = left_op;
break;
case if_acmpne:
if (right_op->index >= 0) {
method->vars[right_op->index].mask &= left_op->mask;
}
if (left_op->index >= 0) {
method->vars[left_op->index].mask &= right_op->mask;
}
stack_pointer = left_op;
break;
case ifnull:
if (right_op->index >= 0) {
method->vars[right_op->index].mask |= var_desc::vs_not_null;
if (vars != NULL) {
vars[right_op->index].mask &= ~var_desc::vs_not_null;
}
}
stack_pointer = right_op;
break;
case ifnonnull:
if (right_op->index >= 0) {
method->vars[right_op->index].mask &= ~var_desc::vs_not_null;
if (vars != NULL) {
vars[right_op->index].mask |= var_desc::vs_not_null;
}
}
stack_pointer = right_op;
break;
default:
stack_pointer = sp;
}
stack_top[1] = stack_pointer[-1];
stack_top[0] = stack_pointer[-2];
return sp;
}
/*
** merge states after split
*/
vbm_operand* ctx_merge::transfer(method_desc* method, vbm_operand* sp,
byte, byte& prev_cop)
{
var_desc save_var;
method->in_monitor = come_from->in_monitor;
if (cmd == cmd_case_ctx && come_from->switch_var_index >= 0) {
// If branch is part of switch and switch expression is local variable,
// then we know value of this variable if this branch takes place
var_desc* var = &come_from->vars[come_from->switch_var_index];
save_var = *var;
var->max = var->min = var->mask = case_value;
}
var_desc* v0 = method->vars;
var_desc* v1 = come_from->vars;
if (prev_cop == goto_near || prev_cop == goto_w
|| prev_cop == ret || prev_cop == athrow
|| prev_cop == lookupswitch || prev_cop == tableswitch
|| unsigned(prev_cop - ireturn) <= unsigned(vreturn-ireturn))
{
// Control can be passed to this point only by branch:
// no need to merge states
for (int i = method->n_vars; --i >= 0; v0++, v1++) {
if (v0->type == v1->type) {
v0->min = v1->min;
v0->max = v1->max;
v0->mask = v1->mask;
} else if (v1->type == tp_void) {
v0->type = tp_void;
}
}
sp = come_from->stack_pointer;
sp[-1] = come_from->stack_top[1];
sp[-2] = come_from->stack_top[0];
// all successive ctx_merge::transfer should merge variables properties
prev_cop = nop;
} else {
// merge states
for (int i = method->n_vars; --i >= 0; v0++, v1++) {
if (v0->type == v1->type) {
if (IS_INT_TYPE(v0->type)) {
if (v0->min > v1->min) v0->min = v1->min;
if (v0->max < v1->max) v0->max = v1->max;
v0->mask |= v1->mask;
#ifdef INT8_DEFINED
} else if (v0->type == tp_long) {
int8 min0 = LOAD_INT8(v0,min);
int8 max0 = LOAD_INT8(v0,max);
int8 mask0 = LOAD_INT8(v0,mask);
int8 min1 = LOAD_INT8(v1,min);
int8 max1 = LOAD_INT8(v1,max);
int8 mask1 = LOAD_INT8(v1,mask);
if (min0 > min1) {
STORE_INT8(v0, min, min1);
}
if (max0 < max1) {
STORE_INT8(v0, max, max1);
}
mask0 |= mask1;
STORE_INT8(v0, mask, mask0);
v0 += 1;
v1 += 1;
i -= 1;
assert(i >= 0);
#endif
} else {
if (v0->min > v1->min) v0->min = v1->min;
if (v0->max < v1->max) v0->max = v1->max;
v0->mask &= v1->mask;
}
} else if (v0->type != tp_void && v1->type == tp_void) {
if (IS_INT_TYPE(v0->type)) {
v0->min = ranges[tp_int].min;
v0->max = ranges[tp_int].max;
v0->mask = ALL_BITS;
} else if (v0->type == tp_long) {
v0[0].min = 0x80000000;
v0[0].max = 0x7fffffff;
v0[0].mask = 0xffffffff;
v0[1].min = 0x00000000;
v0[1].max = 0xffffffff;
v0[1].mask = 0xffffffff;
} else {
v0->min = 0;
v0->max = MAX_ARRAY_LENGTH;
}
}
}
assert(sp == come_from->stack_pointer);
if (IS_INT_TYPE(come_from->stack_top[1].type)) {
if (sp[-1].min > come_from->stack_top[1].min) {
sp[-1].min = come_from->stack_top[1].min;
}
if (sp[-1].max < come_from->stack_top[1].max) {
sp[-1].max = come_from->stack_top[1].max;
}
sp[-1].mask |= come_from->stack_top[1].mask;
#ifdef INT8_DEFINED
} else if (come_from->stack_top[1].type == tp_long) {
int8 min0 = LOAD_INT8(sp-2,min);
int8 max0 = LOAD_INT8(sp-2,max);
int8 mask0 = LOAD_INT8(sp-2,mask);
int8 min1 = LOAD_INT8(come_from->stack_top,min);
int8 max1 = LOAD_INT8(come_from->stack_top,max);
int8 mask1 = LOAD_INT8(come_from->stack_top,mask);
if (min0 > min1) {
STORE_INT8(sp-2, min, min1);
}
if (max0 < max1) {
STORE_INT8(sp-2, max, max1);
}
mask0 |= mask1;
STORE_INT8(sp-2, mask, mask0);
#endif
} else {
//raphy
sp[-1].min = 0;
sp[-1].max = 0;
come_from->stack_top[1].min = 0;
come_from->stack_top[1].max = 0;
//raphy end
if (sp[-1].min > come_from->stack_top[1].min) {
sp[-1].min = come_from->stack_top[1].min;
}
if (sp[-1].max < come_from->stack_top[1].max) {
sp[-1].max = come_from->stack_top[1].max;
}
sp[-1].mask &= come_from->stack_top[1].mask;
}
}
if (--come_from->n_branches == 0) {
delete[] come_from->vars;
} else if (cmd == cmd_case_ctx && come_from->switch_var_index >= 0) {
// restore state of switch expression varaible,
// because it can be used in other branches
come_from->vars[come_from->switch_var_index] = save_var;
}
return sp;
}
vbm_operand* ctx_entry_point::transfer(method_desc* method, vbm_operand* sp,
byte, byte&)
{
#if 1
//
// As far as state of variable is not followed correctly in case of
// subroutine execution or catching exception, the obvious approach is
// to reset state of all local variables. But in this case we will loose
// useful information, so I decide to keep variables state,
// hoping that it will not cause confusing Jlint messages.
//
var_desc* var = method->vars;
for (int i = method->n_vars; --i >= 0; var++) {
int type = var->type;
if (IS_INT_TYPE(type)) {
var->min = ranges[type].min;
var->max = ranges[type].max;
var->mask = var->min | var->max;
} else if (type == tp_long) {
var->min = 0x80000000;
var->max = 0x7fffffff;
var->mask = 0xffffffff;
var += 1;
var->min = 0x00000000;
var->max = 0xffffffff;
var->mask = 0xffffffff;
i -= 1;
assert(i >= 0);
} else {
var->min = 0;
var->max = MAX_ARRAY_LENGTH;
var->mask = var_desc::vs_unknown;
}
}
#endif
sp->type = tp_object;
sp->mask = var_desc::vs_not_null;
sp->min = 0;
sp->max = MAX_ARRAY_LENGTH;
return sp+1; // exception object is pushed on stack
}
vbm_operand* ctx_reset::transfer(method_desc* method, vbm_operand* sp,
byte, byte&)
{
var_desc* var = method->vars;
for (int n = method->n_vars, i = 0; i < n; i++, var++) {
//
// Reset vaules of local variables which were modified in region of
// code between backward jump label and backward jump intruction
//
if (method->var_store_count[i] != var_store_count[i]) {
int type = var->type;
if (IS_INT_TYPE(type)) {
var->min = ranges[type].min;
var->max = ranges[type].max;
var->mask = var->min | var->max;
} else if (type == tp_long) {
var->min = 0x80000000;
var->max = 0x7fffffff;
var->mask = 0xffffffff;
var += 1;
var->min = 0x00000000;
var->max = 0xffffffff;
var->mask = 0xffffffff;
i += 1;
assert(i < n);
} else {
var->mask = var_desc::vs_unknown;
var->min = 0;
var->max = MAX_ARRAY_LENGTH;
}
}
}
delete[] var_store_count;
return sp;
}