duke@435: /* duke@435: * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. duke@435: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@435: * duke@435: * This code is free software; you can redistribute it and/or modify it duke@435: * under the terms of the GNU General Public License version 2 only, as duke@435: * published by the Free Software Foundation. duke@435: * duke@435: * This code is distributed in the hope that it will be useful, but WITHOUT duke@435: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@435: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@435: * version 2 for more details (a copy is included in the LICENSE file that duke@435: * accompanied this code). duke@435: * duke@435: * You should have received a copy of the GNU General Public License version duke@435: * 2 along with this work; if not, write to the Free Software Foundation, duke@435: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@435: * duke@435: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@435: * CA 95054 USA or visit www.sun.com if you need additional information or duke@435: * have any questions. duke@435: * duke@435: */ duke@435: duke@435: // ciBytecodeStream duke@435: // duke@435: // The class is used to iterate over the bytecodes of a method. duke@435: // It hides the details of constant pool structure/access by duke@435: // providing accessors for constant pool items. It returns only pure duke@435: // Java bytecodes; VM-internal _fast bytecodes are translated back to duke@435: // their original form during iteration. duke@435: class ciBytecodeStream : StackObj { duke@435: private: duke@435: // Handling for the weird bytecodes duke@435: Bytecodes::Code wide(); // Handle wide bytecode duke@435: Bytecodes::Code table(Bytecodes::Code); // Handle complicated inline table duke@435: duke@435: static Bytecodes::Code check_java(Bytecodes::Code c) { duke@435: assert(Bytecodes::is_java_code(c), "should not return _fast bytecodes"); duke@435: return c; duke@435: } duke@435: duke@435: ciMethod* _method; // the method duke@435: ciInstanceKlass* _holder; duke@435: address _bc_start; // Start of current bytecode for table duke@435: address _was_wide; // Address past last wide bytecode duke@435: jint* _table_base; // Aligned start of last table or switch duke@435: duke@435: address _start; // Start of bytecodes duke@435: address _end; // Past end of bytecodes duke@435: address _pc; // Current PC duke@435: Bytecodes::Code _bc; // Current bytecode duke@435: duke@435: void reset( address base, unsigned int size ) { duke@435: _bc_start =_was_wide = 0; duke@435: _start = _pc = base; _end = base + size; } duke@435: duke@435: public: duke@435: // End-Of-Bytecodes duke@435: static Bytecodes::Code EOBC() { duke@435: return Bytecodes::_illegal; duke@435: } duke@435: duke@435: ciBytecodeStream(ciMethod* m) { duke@435: reset_to_method(m); duke@435: } duke@435: duke@435: ciBytecodeStream() { duke@435: reset_to_method(NULL); duke@435: } duke@435: duke@435: ciMethod* method() const { return _method; } duke@435: duke@435: void reset_to_method(ciMethod* m) { duke@435: _method = m; duke@435: if (m == NULL) { duke@435: _holder = NULL; duke@435: reset(NULL, 0); duke@435: } else { duke@435: _holder = m->holder(); duke@435: reset(m->code(), m->code_size()); duke@435: } duke@435: } duke@435: duke@435: void reset_to_bci( int bci ); duke@435: duke@435: // Force the iterator to report a certain bci. duke@435: void force_bci(int bci); duke@435: duke@435: void set_max_bci( int max ) { duke@435: _end = _start + max; duke@435: } duke@435: duke@435: address cur_bcp() { return _bc_start; } // Returns bcp to current instruction duke@435: int next_bci() const { return _pc -_start; } duke@435: int cur_bci() const { return _bc_start - _start; } duke@435: duke@435: Bytecodes::Code cur_bc() const{ return check_java(_bc); } duke@435: Bytecodes::Code next_bc() { return Bytecodes::java_code((Bytecodes::Code)* _pc); } duke@435: duke@435: // Return current ByteCode and increment PC to next bytecode, skipping all duke@435: // intermediate constants. Returns EOBC at end. duke@435: // Expected usage: duke@435: // while( (bc = iter.next()) != EOBC() ) { ... } duke@435: Bytecodes::Code next() { duke@435: _bc_start = _pc; // Capture start of bc duke@435: if( _pc >= _end ) return EOBC(); // End-Of-Bytecodes duke@435: duke@435: // Fetch Java bytecode duke@435: // All rewritten bytecodes maintain the size of original bytecode. duke@435: _bc = Bytecodes::java_code((Bytecodes::Code)*_pc); duke@435: int csize = Bytecodes::length_for(_bc); // Expected size duke@435: duke@435: if( _bc == Bytecodes::_wide ) { duke@435: _bc=wide(); // Handle wide bytecode duke@435: } else if( csize == 0 ) { duke@435: _bc=table(_bc); // Handle inline tables duke@435: } else { duke@435: _pc += csize; // Bump PC past bytecode duke@435: } duke@435: return check_java(_bc); duke@435: } duke@435: duke@435: bool is_wide() { return ( _pc == _was_wide ); } duke@435: duke@435: // Get a byte index following this bytecode. duke@435: // If prefixed with a wide bytecode, get a wide index. duke@435: int get_index() const { duke@435: return (_pc == _was_wide) // was widened? duke@435: ? Bytes::get_Java_u2(_bc_start+2) // yes, return wide index duke@435: : _bc_start[1]; // no, return narrow index duke@435: } duke@435: duke@435: // Set a byte index following this bytecode. duke@435: // If prefixed with a wide bytecode, get a wide index. duke@435: void put_index(int idx) { duke@435: if (_pc == _was_wide) // was widened? duke@435: Bytes::put_Java_u2(_bc_start+2,idx); // yes, set wide index duke@435: else duke@435: _bc_start[1]=idx; // no, set narrow index duke@435: } duke@435: duke@435: // Get 2-byte index (getfield/putstatic/etc) duke@435: int get_index_big() const { return Bytes::get_Java_u2(_bc_start+1); } duke@435: duke@435: // Get dimensions byte (multinewarray) duke@435: int get_dimensions() const { return *(unsigned char*)(_pc-1); } duke@435: duke@435: // Get unsigned index fast duke@435: int get_index_fast() const { return Bytes::get_native_u2(_pc-2); } duke@435: duke@435: // Sign-extended index byte/short, no widening duke@435: int get_byte() const { return (int8_t)(_pc[-1]); } duke@435: int get_short() const { return (int16_t)Bytes::get_Java_u2(_pc-2); } duke@435: int get_long() const { return (int32_t)Bytes::get_Java_u4(_pc-4); } duke@435: duke@435: // Get a byte signed constant for "iinc". Invalid for other bytecodes. duke@435: // If prefixed with a wide bytecode, get a wide constant duke@435: int get_iinc_con() const {return (_pc==_was_wide) ? get_short() :get_byte();} duke@435: duke@435: // 2-byte branch offset from current pc duke@435: int get_dest( ) const { duke@435: assert( Bytecodes::length_at(_bc_start) == sizeof(jshort)+1, "get_dest called with bad bytecode" ); duke@435: return _bc_start-_start + (short)Bytes::get_Java_u2(_pc-2); duke@435: } duke@435: duke@435: // 2-byte branch offset from next pc duke@435: int next_get_dest( ) const { duke@435: address next_bc_start = _pc; duke@435: assert( _pc < _end, "" ); duke@435: Bytecodes::Code next_bc = (Bytecodes::Code)*_pc; duke@435: assert( next_bc != Bytecodes::_wide, ""); duke@435: int next_csize = Bytecodes::length_for(next_bc); duke@435: assert( next_csize != 0, "" ); duke@435: assert( next_bc <= Bytecodes::_jsr_w, ""); duke@435: address next_pc = _pc + next_csize; duke@435: assert( Bytecodes::length_at(next_bc_start) == sizeof(jshort)+1, "next_get_dest called with bad bytecode" ); duke@435: return next_bc_start-_start + (short)Bytes::get_Java_u2(next_pc-2); duke@435: } duke@435: duke@435: // 4-byte branch offset from current pc duke@435: int get_far_dest( ) const { duke@435: assert( Bytecodes::length_at(_bc_start) == sizeof(jint)+1, "dest4 called with bad bytecode" ); duke@435: return _bc_start-_start + (int)Bytes::get_Java_u4(_pc-4); duke@435: } duke@435: duke@435: // For a lookup or switch table, return target destination duke@435: int get_int_table( int index ) const { duke@435: return Bytes::get_Java_u4((address)&_table_base[index]); } duke@435: duke@435: // For tableswitch - get length of offset part duke@435: int get_tableswitch_length() { return get_int_table(2)-get_int_table(1)+1; } duke@435: duke@435: int get_dest_table( int index ) const { duke@435: return cur_bci() + get_int_table(index); } duke@435: duke@435: // --- Constant pool access --- duke@435: int get_constant_index() const; duke@435: int get_field_index(); duke@435: int get_method_index(); duke@435: duke@435: // If this bytecode is a new, newarray, multianewarray, instanceof, duke@435: // or checkcast, get the referenced klass. duke@435: ciKlass* get_klass(bool& will_link); duke@435: int get_klass_index() const; duke@435: duke@435: // If this bytecode is one of the ldc variants, get the referenced duke@435: // constant duke@435: ciConstant get_constant(); duke@435: // True if the ldc variant points to an unresolved string duke@435: bool is_unresolved_string() const; duke@435: // True if the ldc variant points to an unresolved klass duke@435: bool is_unresolved_klass() const; duke@435: duke@435: // If this bytecode is one of get_field, get_static, put_field, duke@435: // or put_static, get the referenced field. duke@435: ciField* get_field(bool& will_link); duke@435: duke@435: ciInstanceKlass* get_declared_field_holder(); duke@435: int get_field_holder_index(); duke@435: int get_field_signature_index(); duke@435: duke@435: // If this is a method invocation bytecode, get the invoked method. duke@435: ciMethod* get_method(bool& will_link); duke@435: ciKlass* get_declared_method_holder(); duke@435: int get_method_holder_index(); duke@435: int get_method_signature_index(); duke@435: }; duke@435: duke@435: duke@435: // ciSignatureStream duke@435: // duke@435: // The class is used to iterate over the elements of a method signature. duke@435: class ciSignatureStream : public StackObj { duke@435: private: duke@435: ciSignature* _sig; duke@435: int _pos; duke@435: public: duke@435: ciSignatureStream(ciSignature* signature) { duke@435: _sig = signature; duke@435: _pos = 0; duke@435: } duke@435: duke@435: bool at_return_type() { return _pos == _sig->count(); } duke@435: duke@435: bool is_done() { return _pos > _sig->count(); } duke@435: duke@435: void next() { duke@435: if (_pos <= _sig->count()) { duke@435: _pos++; duke@435: } duke@435: } duke@435: duke@435: ciType* type() { duke@435: if (at_return_type()) { duke@435: return _sig->return_type(); duke@435: } else { duke@435: return _sig->type_at(_pos); duke@435: } duke@435: } duke@435: }; duke@435: duke@435: duke@435: // ciExceptionHandlerStream duke@435: // duke@435: // The class is used to iterate over the exception handlers of duke@435: // a method. duke@435: class ciExceptionHandlerStream : public StackObj { duke@435: private: duke@435: // The method whose handlers we are traversing duke@435: ciMethod* _method; duke@435: duke@435: // Our current position in the list of handlers duke@435: int _pos; duke@435: int _end; duke@435: duke@435: ciInstanceKlass* _exception_klass; duke@435: int _bci; duke@435: bool _is_exact; duke@435: duke@435: public: duke@435: ciExceptionHandlerStream(ciMethod* method) { duke@435: _method = method; duke@435: duke@435: // Force loading of method code and handlers. duke@435: _method->code(); duke@435: duke@435: _pos = 0; duke@435: _end = _method->_handler_count; duke@435: _exception_klass = NULL; duke@435: _bci = -1; duke@435: _is_exact = false; duke@435: } duke@435: duke@435: ciExceptionHandlerStream(ciMethod* method, int bci, duke@435: ciInstanceKlass* exception_klass = NULL, duke@435: bool is_exact = false) { duke@435: _method = method; duke@435: duke@435: // Force loading of method code and handlers. duke@435: _method->code(); duke@435: duke@435: _pos = -1; duke@435: _end = _method->_handler_count + 1; // include the rethrow handler duke@435: _exception_klass = (exception_klass != NULL && exception_klass->is_loaded() duke@435: ? exception_klass duke@435: : NULL); duke@435: _bci = bci; duke@435: assert(_bci >= 0, "bci out of range"); duke@435: _is_exact = is_exact; duke@435: next(); duke@435: } duke@435: duke@435: // These methods are currently implemented in an odd way. duke@435: // Count the number of handlers the iterator has ever produced duke@435: // or will ever produce. Do not include the final rethrow handler. duke@435: // That is, a trivial exception handler stream will have a count duke@435: // of zero and produce just the rethrow handler. duke@435: int count(); duke@435: duke@435: // Count the number of handlers this stream will produce from now on. duke@435: // Include the current handler, and the final rethrow handler. duke@435: // The remaining count will be zero iff is_done() is true, duke@435: int count_remaining(); duke@435: duke@435: bool is_done() { duke@435: return (_pos >= _end); duke@435: } duke@435: duke@435: void next() { duke@435: _pos++; duke@435: if (_bci != -1) { duke@435: // We are not iterating over all handlers... duke@435: while (!is_done()) { duke@435: ciExceptionHandler* handler = _method->_exception_handlers[_pos]; duke@435: if (handler->is_in_range(_bci)) { duke@435: if (handler->is_catch_all()) { duke@435: // Found final active catch block. duke@435: _end = _pos+1; duke@435: return; duke@435: } else if (_exception_klass == NULL || !handler->catch_klass()->is_loaded()) { duke@435: // We cannot do any type analysis here. Must conservatively assume duke@435: // catch block is reachable. duke@435: return; duke@435: } else if (_exception_klass->is_subtype_of(handler->catch_klass())) { duke@435: // This catch clause will definitely catch the exception. duke@435: // Final candidate. duke@435: _end = _pos+1; duke@435: return; duke@435: } else if (!_is_exact && duke@435: handler->catch_klass()->is_subtype_of(_exception_klass)) { duke@435: // This catch block may be reachable. duke@435: return; duke@435: } duke@435: } duke@435: duke@435: // The catch block was not pertinent. Go on. duke@435: _pos++; duke@435: } duke@435: } else { duke@435: // This is an iteration over all handlers. duke@435: return; duke@435: } duke@435: } duke@435: duke@435: ciExceptionHandler* handler() { duke@435: return _method->_exception_handlers[_pos]; duke@435: } duke@435: };