From 6d8b12448d9bbe96517f2145a93f4589e947edb5 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 10 Dec 2001 01:18:30 +0000 Subject: 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 --- libjava/ChangeLog | 18 ++++++++ libjava/defineclass.cc | 11 +---- libjava/resolve.cc | 4 +- libjava/verify.cc | 111 ++++++++++++++++++++++++++++++++++++------------- 4 files changed, 105 insertions(+), 39 deletions(-) diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 4004083..c9d99f5 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,5 +1,23 @@ 2001-12-09 Tom Tromey + * 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'. diff --git a/libjava/defineclass.cc b/libjava/defineclass.cc index 2901527..df2da09 100644 --- a/libjava/defineclass.cc +++ b/libjava/defineclass.cc @@ -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); - } } } } diff --git a/libjava/resolve.cc b/libjava/resolve.cc index 2b25b57..ba397c9 100644 --- a/libjava/resolve.cc +++ b/libjava/resolve.cc @@ -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 (); } } diff --git a/libjava/verify.cc b/libjava/verify.cc index 74499ed..8627de5 100644 --- a/libjava/verify.cc +++ b/libjava/verify.cc @@ -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; -- cgit v1.1