resolve.cc (_Jv_PrepareClass): Verify method here...
* resolve.cc (_Jv_PrepareClass): Verify method here... * defineclass.cc (handleMethodsEnd): ... not here. * verify.cc (_Jv_BytecodeVerifier::initialize_stack): New method. (_Jv_BytecodeVerifier::verify_instructions_0) [op_return]: Ensure there are no uninitialized objects. (_Jv_BytecodeVerifier::state::this_type): New field. (_Jv_BytecodeVerifier::state::state): Initialize this_type. (_Jv_BytecodeVerifier::state::copy): Copy this_type. (_Jv_BytecodeVerifier::state::merge): Merge this_type. (_Jv_BytecodeVerifier::state::check_no_uninitialized_objects): Handle this_type. (_Jv_BytecodeVerifier::state::check_this_initialized): New method. (_Jv_BytecodeVerifier::state::set_initialized): Handle this_type. (_Jv_BytecodeVerifier::state::set_this_type): New method. (_Jv_BytecodeVerifier::verify_instructions_0) [op_putfield]: Allow assignment to fields of `this' before another initializer is run. From-SVN: r47826
This commit is contained in:
parent
ec10f7c703
commit
6d8b12448d
@ -1,5 +1,23 @@
|
||||
2001-12-09 Tom Tromey <tromey@redhat.com>
|
||||
|
||||
* resolve.cc (_Jv_PrepareClass): Verify method here...
|
||||
* defineclass.cc (handleMethodsEnd): ... not here.
|
||||
* verify.cc (_Jv_BytecodeVerifier::initialize_stack): New method.
|
||||
(_Jv_BytecodeVerifier::verify_instructions_0) [op_return]: Ensure
|
||||
there are no uninitialized objects.
|
||||
(_Jv_BytecodeVerifier::state::this_type): New field.
|
||||
(_Jv_BytecodeVerifier::state::state): Initialize this_type.
|
||||
(_Jv_BytecodeVerifier::state::copy): Copy this_type.
|
||||
(_Jv_BytecodeVerifier::state::merge): Merge this_type.
|
||||
(_Jv_BytecodeVerifier::state::check_no_uninitialized_objects):
|
||||
Handle this_type.
|
||||
(_Jv_BytecodeVerifier::state::check_this_initialized): New
|
||||
method.
|
||||
(_Jv_BytecodeVerifier::state::set_initialized): Handle this_type.
|
||||
(_Jv_BytecodeVerifier::state::set_this_type): New method.
|
||||
(_Jv_BytecodeVerifier::verify_instructions_0) [op_putfield]: Allow
|
||||
assignment to fields of `this' before another initializer is run.
|
||||
|
||||
* Makefile.in: Rebuilt.
|
||||
* Makefile.am (gnu/gcj/runtime/VMClassLoader.h): Use `::java'.
|
||||
|
||||
|
@ -940,7 +940,7 @@ _Jv_ClassReader::handleClassBegin
|
||||
pool_data[super_class].clazz = the_super;
|
||||
pool_tags[super_class] = JV_CONSTANT_ResolvedClass;
|
||||
}
|
||||
|
||||
|
||||
// now we've come past the circularity problem, we can
|
||||
// now say that we're loading...
|
||||
|
||||
@ -1315,15 +1315,6 @@ void _Jv_ClassReader::handleMethodsEnd ()
|
||||
{
|
||||
if (def->interpreted_methods[i] == 0)
|
||||
throw_class_format_error ("method with no code");
|
||||
|
||||
if (verify)
|
||||
{
|
||||
_Jv_InterpMethod *m;
|
||||
m = (reinterpret_cast<_Jv_InterpMethod *>
|
||||
(def->interpreted_methods[i]));
|
||||
// FIXME: enable once verifier is more fully tested.
|
||||
// _Jv_VerifyMethod (m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -578,7 +578,7 @@ _Jv_PrepareClass(jclass klass)
|
||||
|
||||
// set the instance size for the class
|
||||
clz->size_in_bytes = instance_size;
|
||||
|
||||
|
||||
// allocate static memory
|
||||
if (static_size != 0)
|
||||
{
|
||||
@ -628,6 +628,8 @@ _Jv_PrepareClass(jclass klass)
|
||||
else if (imeth != 0) // it could be abstract
|
||||
{
|
||||
_Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (imeth);
|
||||
// FIXME: enable once verifier is more fully tested.
|
||||
// _Jv_VerifyMethod (im);
|
||||
clz->methods[i].ncode = im->ncode ();
|
||||
}
|
||||
}
|
||||
|
@ -764,6 +764,13 @@ private:
|
||||
// This is used to keep a linked list of all the states which
|
||||
// require re-verification. We use the PC to keep track.
|
||||
int next;
|
||||
// We keep track of the type of `this' specially. This is used to
|
||||
// ensure that an instance initializer invokes another initializer
|
||||
// on `this' before returning. We must keep track of this
|
||||
// specially because otherwise we might be confused by code which
|
||||
// assigns to locals[0] (overwriting `this') and then returns
|
||||
// without really initializing.
|
||||
type this_type;
|
||||
|
||||
// INVALID marks a state which is not on the linked list of states
|
||||
// requiring reverification.
|
||||
@ -772,6 +779,7 @@ private:
|
||||
static const int NO_NEXT = -2;
|
||||
|
||||
state ()
|
||||
: this_type ()
|
||||
{
|
||||
stack = NULL;
|
||||
locals = NULL;
|
||||
@ -779,6 +787,7 @@ private:
|
||||
}
|
||||
|
||||
state (int max_stack, int max_locals)
|
||||
: this_type ()
|
||||
{
|
||||
stacktop = 0;
|
||||
stackdepth = 0;
|
||||
@ -855,6 +864,7 @@ private:
|
||||
locals[i] = copy->locals[i];
|
||||
local_changed[i] = copy->local_changed[i];
|
||||
}
|
||||
this_type = copy->this_type;
|
||||
// Don't modify `next'.
|
||||
}
|
||||
|
||||
@ -870,14 +880,19 @@ private:
|
||||
// FIXME: subroutine handling?
|
||||
}
|
||||
|
||||
// Merge STATE into this state. Destructively modifies this state.
|
||||
// Returns true if the new state was in fact changed. Will throw an
|
||||
// exception if the states are not mergeable.
|
||||
// Merge STATE_OLD into this state. Destructively modifies this
|
||||
// state. Returns true if the new state was in fact changed.
|
||||
// Will throw an exception if the states are not mergeable.
|
||||
bool merge (state *state_old, bool ret_semantics,
|
||||
int max_locals)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
// Special handling for `this'. If one or the other is
|
||||
// uninitialized, then the merge is uninitialized.
|
||||
if (this_type.isinitialized ())
|
||||
this_type = state_old->this_type;
|
||||
|
||||
// Merge subroutine states. *THIS and *STATE_OLD must be in the
|
||||
// same subroutine. Also, recursive subroutine calls must be
|
||||
// avoided.
|
||||
@ -940,6 +955,21 @@ private:
|
||||
for (int i = 0; i < max_locals; ++i)
|
||||
if (locals[i].isreference () && ! locals[i].isinitialized ())
|
||||
verify_fail ("uninitialized object in local variable");
|
||||
|
||||
check_this_initialized ();
|
||||
}
|
||||
|
||||
// Ensure that `this' has been initialized.
|
||||
void check_this_initialized ()
|
||||
{
|
||||
if (this_type.isreference () && ! this_type.isinitialized ())
|
||||
verify_fail ("`this' is uninitialized");
|
||||
}
|
||||
|
||||
// Set type of `this'.
|
||||
void set_this_type (const type &k)
|
||||
{
|
||||
this_type = k;
|
||||
}
|
||||
|
||||
// Note that a local variable was modified.
|
||||
@ -957,6 +987,7 @@ private:
|
||||
stack[i].set_initialized (pc);
|
||||
for (int i = 0; i < max_locals; ++i)
|
||||
locals[i].set_initialized (pc);
|
||||
this_type.set_initialized (pc);
|
||||
}
|
||||
|
||||
// Return true if this state is the unmerged result of a `ret'.
|
||||
@ -1870,6 +1901,42 @@ private:
|
||||
verify_fail ("incompatible return type", start_PC);
|
||||
}
|
||||
|
||||
// Initialize the stack for the new method. Returns true if this
|
||||
// method is an instance initializer.
|
||||
bool initialize_stack ()
|
||||
{
|
||||
int var = 0;
|
||||
bool is_init = false;
|
||||
|
||||
using namespace java::lang::reflect;
|
||||
if (! Modifier::isStatic (current_method->self->accflags))
|
||||
{
|
||||
type kurr (current_class);
|
||||
if (_Jv_equalUtf8Consts (current_method->self->name, gcj::init_name))
|
||||
{
|
||||
kurr.set_uninitialized (type::SELF);
|
||||
is_init = true;
|
||||
}
|
||||
set_variable (0, kurr);
|
||||
current_state->set_this_type (kurr);
|
||||
++var;
|
||||
}
|
||||
|
||||
// We have to handle wide arguments specially here.
|
||||
int arg_count = _Jv_count_arguments (current_method->self->signature);
|
||||
type arg_types[arg_count];
|
||||
compute_argument_types (current_method->self->signature, arg_types);
|
||||
for (int i = 0; i < arg_count; ++i)
|
||||
{
|
||||
set_variable (var, arg_types[i]);
|
||||
++var;
|
||||
if (arg_types[i].iswide ())
|
||||
++var;
|
||||
}
|
||||
|
||||
return is_init;
|
||||
}
|
||||
|
||||
void verify_instructions_0 ()
|
||||
{
|
||||
current_state = new state (current_method->max_stack,
|
||||
@ -1878,31 +1945,8 @@ private:
|
||||
PC = 0;
|
||||
start_PC = 0;
|
||||
|
||||
{
|
||||
int var = 0;
|
||||
|
||||
using namespace java::lang::reflect;
|
||||
if (! Modifier::isStatic (current_method->self->accflags))
|
||||
{
|
||||
type kurr (current_class);
|
||||
if (_Jv_equalUtf8Consts (current_method->self->name, gcj::init_name))
|
||||
kurr.set_uninitialized (type::SELF);
|
||||
set_variable (0, kurr);
|
||||
++var;
|
||||
}
|
||||
|
||||
// We have to handle wide arguments specially here.
|
||||
int arg_count = _Jv_count_arguments (current_method->self->signature);
|
||||
type arg_types[arg_count];
|
||||
compute_argument_types (current_method->self->signature, arg_types);
|
||||
for (int i = 0; i < arg_count; ++i)
|
||||
{
|
||||
set_variable (var, arg_types[i]);
|
||||
++var;
|
||||
if (arg_types[i].iswide ())
|
||||
++var;
|
||||
}
|
||||
}
|
||||
// True if we are verifying an instance initializer.
|
||||
bool this_is_init = initialize_stack ();
|
||||
|
||||
states = (state **) _Jv_Malloc (sizeof (state *)
|
||||
* current_method->code_length);
|
||||
@ -2561,6 +2605,10 @@ private:
|
||||
invalidate_pc ();
|
||||
break;
|
||||
case op_return:
|
||||
// We only need to check this when the return type is
|
||||
// void, because all instance initializers return void.
|
||||
if (this_is_init)
|
||||
current_state->check_this_initialized ();
|
||||
check_return_type (void_type);
|
||||
invalidate_pc ();
|
||||
break;
|
||||
@ -2583,6 +2631,13 @@ private:
|
||||
type klass;
|
||||
type field = check_field_constant (get_ushort (), &klass);
|
||||
pop_type (field);
|
||||
|
||||
// We have an obscure special case here: we can use
|
||||
// `putfield' on a field declared in this class, even if
|
||||
// `this' has not yet been initialized.
|
||||
if (! current_state->this_type.isinitialized ()
|
||||
&& current_state->this_type.pc == type::SELF)
|
||||
klass.set_uninitialized (type::SELF);
|
||||
pop_type (klass);
|
||||
}
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user