aoqi@0: /* aoqi@0: * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: * aoqi@0: */ aoqi@0: aoqi@0: #ifndef SHARE_VM_CI_CISTREAMS_HPP aoqi@0: #define SHARE_VM_CI_CISTREAMS_HPP aoqi@0: aoqi@0: #include "ci/ciClassList.hpp" aoqi@0: #include "ci/ciExceptionHandler.hpp" aoqi@0: #include "ci/ciInstanceKlass.hpp" aoqi@0: #include "ci/ciMethod.hpp" aoqi@0: #include "interpreter/bytecode.hpp" aoqi@0: aoqi@0: // ciBytecodeStream aoqi@0: // aoqi@0: // The class is used to iterate over the bytecodes of a method. aoqi@0: // It hides the details of constant pool structure/access by aoqi@0: // providing accessors for constant pool items. It returns only pure aoqi@0: // Java bytecodes; VM-internal _fast bytecodes are translated back to aoqi@0: // their original form during iteration. aoqi@0: class ciBytecodeStream : StackObj { aoqi@0: private: aoqi@0: // Handling for the weird bytecodes aoqi@0: Bytecodes::Code next_wide_or_table(Bytecodes::Code); // Handle _wide & complicated inline table aoqi@0: aoqi@0: static Bytecodes::Code check_java(Bytecodes::Code c) { aoqi@0: assert(Bytecodes::is_java_code(c), "should not return _fast bytecodes"); aoqi@0: return c; aoqi@0: } aoqi@0: aoqi@0: static Bytecodes::Code check_defined(Bytecodes::Code c) { aoqi@0: assert(Bytecodes::is_defined(c), ""); aoqi@0: return c; aoqi@0: } aoqi@0: aoqi@0: ciMethod* _method; // the method aoqi@0: ciInstanceKlass* _holder; aoqi@0: address _bc_start; // Start of current bytecode for table aoqi@0: address _was_wide; // Address past last wide bytecode aoqi@0: jint* _table_base; // Aligned start of last table or switch aoqi@0: aoqi@0: address _start; // Start of bytecodes aoqi@0: address _end; // Past end of bytecodes aoqi@0: address _pc; // Current PC aoqi@0: Bytecodes::Code _bc; // Current bytecode aoqi@0: Bytecodes::Code _raw_bc; // Current bytecode, raw form aoqi@0: aoqi@0: void reset( address base, unsigned int size ) { aoqi@0: _bc_start =_was_wide = 0; aoqi@0: _start = _pc = base; _end = base + size; aoqi@0: } aoqi@0: aoqi@0: void assert_wide(bool require_wide) const { aoqi@0: if (require_wide) aoqi@0: { assert(is_wide(), "must be a wide instruction"); } aoqi@0: else { assert(!is_wide(), "must not be a wide instruction"); } aoqi@0: } aoqi@0: aoqi@0: Bytecode bytecode() const { return Bytecode(this, _bc_start); } aoqi@0: Bytecode next_bytecode() const { return Bytecode(this, _pc); } aoqi@0: aoqi@0: public: aoqi@0: // End-Of-Bytecodes aoqi@0: static Bytecodes::Code EOBC() { aoqi@0: return Bytecodes::_illegal; aoqi@0: } aoqi@0: aoqi@0: ciBytecodeStream(ciMethod* m) { aoqi@0: reset_to_method(m); aoqi@0: } aoqi@0: aoqi@0: ciBytecodeStream() { aoqi@0: reset_to_method(NULL); aoqi@0: } aoqi@0: aoqi@0: ciMethod* method() const { return _method; } aoqi@0: aoqi@0: void reset_to_method(ciMethod* m) { aoqi@0: _method = m; aoqi@0: if (m == NULL) { aoqi@0: _holder = NULL; aoqi@0: reset(NULL, 0); aoqi@0: } else { aoqi@0: _holder = m->holder(); aoqi@0: reset(m->code(), m->code_size()); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void reset_to_bci( int bci ); aoqi@0: aoqi@0: // Force the iterator to report a certain bci. aoqi@0: void force_bci(int bci); aoqi@0: aoqi@0: void set_max_bci( int max ) { aoqi@0: _end = _start + max; aoqi@0: } aoqi@0: aoqi@0: address cur_bcp() const { return _bc_start; } // Returns bcp to current instruction aoqi@0: int next_bci() const { return _pc - _start; } aoqi@0: int cur_bci() const { return _bc_start - _start; } aoqi@0: int instruction_size() const { return _pc - _bc_start; } aoqi@0: aoqi@0: Bytecodes::Code cur_bc() const{ return check_java(_bc); } aoqi@0: Bytecodes::Code cur_bc_raw() const { return check_defined(_raw_bc); } aoqi@0: Bytecodes::Code next_bc() { return Bytecodes::java_code((Bytecodes::Code)* _pc); } aoqi@0: aoqi@0: // Return current ByteCode and increment PC to next bytecode, skipping all aoqi@0: // intermediate constants. Returns EOBC at end. aoqi@0: // Expected usage: aoqi@0: // ciBytecodeStream iter(m); aoqi@0: // while (iter.next() != ciBytecodeStream::EOBC()) { ... } aoqi@0: Bytecodes::Code next() { aoqi@0: _bc_start = _pc; // Capture start of bc aoqi@0: if( _pc >= _end ) return EOBC(); // End-Of-Bytecodes aoqi@0: aoqi@0: // Fetch Java bytecode aoqi@0: // All rewritten bytecodes maintain the size of original bytecode. aoqi@0: _bc = Bytecodes::java_code(_raw_bc = (Bytecodes::Code)*_pc); aoqi@0: int csize = Bytecodes::length_for(_bc); // Expected size aoqi@0: _pc += csize; // Bump PC past bytecode aoqi@0: if (csize == 0) { aoqi@0: _bc = next_wide_or_table(_bc); aoqi@0: } aoqi@0: return check_java(_bc); aoqi@0: } aoqi@0: aoqi@0: bool is_wide() const { return ( _pc == _was_wide ); } aoqi@0: aoqi@0: // Does this instruction contain an index which refes into the CP cache? aoqi@0: bool has_cache_index() const { return Bytecodes::uses_cp_cache(cur_bc_raw()); } aoqi@0: aoqi@0: bool has_optional_appendix() { return Bytecodes::has_optional_appendix(cur_bc_raw()); } aoqi@0: aoqi@0: int get_index_u1() const { aoqi@0: return bytecode().get_index_u1(cur_bc_raw()); aoqi@0: } aoqi@0: aoqi@0: int get_index_u1_cpcache() const { aoqi@0: return bytecode().get_index_u1_cpcache(cur_bc_raw()); aoqi@0: } aoqi@0: aoqi@0: // Get a byte index following this bytecode. aoqi@0: // If prefixed with a wide bytecode, get a wide index. aoqi@0: int get_index() const { aoqi@0: assert(!has_cache_index(), "else use cpcache variant"); aoqi@0: return (_pc == _was_wide) // was widened? aoqi@0: ? get_index_u2(true) // yes, return wide index aoqi@0: : get_index_u1(); // no, return narrow index aoqi@0: } aoqi@0: aoqi@0: // Get 2-byte index (byte swapping depending on which bytecode) aoqi@0: int get_index_u2(bool is_wide = false) const { aoqi@0: return bytecode().get_index_u2(cur_bc_raw(), is_wide); aoqi@0: } aoqi@0: aoqi@0: // Get 2-byte index in native byte order. (Rewriter::rewrite makes these.) aoqi@0: int get_index_u2_cpcache() const { aoqi@0: return bytecode().get_index_u2_cpcache(cur_bc_raw()); aoqi@0: } aoqi@0: aoqi@0: // Get 4-byte index, for invokedynamic. aoqi@0: int get_index_u4() const { aoqi@0: return bytecode().get_index_u4(cur_bc_raw()); aoqi@0: } aoqi@0: aoqi@0: bool has_index_u4() const { aoqi@0: return bytecode().has_index_u4(cur_bc_raw()); aoqi@0: } aoqi@0: aoqi@0: // Get dimensions byte (multinewarray) aoqi@0: int get_dimensions() const { return *(unsigned char*)(_pc-1); } aoqi@0: aoqi@0: // Sign-extended index byte/short, no widening aoqi@0: int get_constant_u1() const { return bytecode().get_constant_u1(instruction_size()-1, cur_bc_raw()); } aoqi@0: int get_constant_u2(bool is_wide = false) const { return bytecode().get_constant_u2(instruction_size()-2, cur_bc_raw(), is_wide); } aoqi@0: aoqi@0: // Get a byte signed constant for "iinc". Invalid for other bytecodes. aoqi@0: // If prefixed with a wide bytecode, get a wide constant aoqi@0: int get_iinc_con() const {return (_pc==_was_wide) ? (jshort) get_constant_u2(true) : (jbyte) get_constant_u1();} aoqi@0: aoqi@0: // 2-byte branch offset from current pc aoqi@0: int get_dest() const { aoqi@0: return cur_bci() + bytecode().get_offset_s2(cur_bc_raw()); aoqi@0: } aoqi@0: aoqi@0: // 2-byte branch offset from next pc aoqi@0: int next_get_dest() const { aoqi@0: assert(_pc < _end, ""); aoqi@0: return next_bci() + next_bytecode().get_offset_s2(Bytecodes::_ifeq); aoqi@0: } aoqi@0: aoqi@0: // 4-byte branch offset from current pc aoqi@0: int get_far_dest() const { aoqi@0: return cur_bci() + bytecode().get_offset_s4(cur_bc_raw()); aoqi@0: } aoqi@0: aoqi@0: // For a lookup or switch table, return target destination aoqi@0: int get_int_table( int index ) const { aoqi@0: return Bytes::get_Java_u4((address)&_table_base[index]); } aoqi@0: aoqi@0: // For tableswitch - get length of offset part aoqi@0: int get_tableswitch_length() { return get_int_table(2)-get_int_table(1)+1; } aoqi@0: aoqi@0: int get_dest_table( int index ) const { aoqi@0: return cur_bci() + get_int_table(index); } aoqi@0: aoqi@0: // --- Constant pool access --- aoqi@0: int get_constant_raw_index() const; aoqi@0: int get_constant_pool_index() const; aoqi@0: int get_constant_cache_index() const; aoqi@0: int get_field_index(); aoqi@0: int get_method_index(); aoqi@0: aoqi@0: // If this bytecode is a new, newarray, multianewarray, instanceof, aoqi@0: // or checkcast, get the referenced klass. aoqi@0: ciKlass* get_klass(bool& will_link); aoqi@0: int get_klass_index() const; aoqi@0: aoqi@0: // If this bytecode is one of the ldc variants, get the referenced aoqi@0: // constant. Do not attempt to resolve it, since that would require aoqi@0: // execution of Java code. If it is not resolved, return an unloaded aoqi@0: // object (ciConstant.as_object()->is_loaded() == false). aoqi@0: ciConstant get_constant(); aoqi@0: constantTag get_constant_pool_tag(int index) const; aoqi@0: aoqi@0: // True if the klass-using bytecode points to an unresolved klass aoqi@0: bool is_unresolved_klass() const { aoqi@0: constantTag tag = get_constant_pool_tag(get_klass_index()); aoqi@0: return tag.is_unresolved_klass(); aoqi@0: } aoqi@0: aoqi@0: // If this bytecode is one of get_field, get_static, put_field, aoqi@0: // or put_static, get the referenced field. aoqi@0: ciField* get_field(bool& will_link); aoqi@0: aoqi@0: ciInstanceKlass* get_declared_field_holder(); aoqi@0: int get_field_holder_index(); aoqi@0: int get_field_signature_index(); aoqi@0: aoqi@0: ciMethod* get_method(bool& will_link, ciSignature* *declared_signature_result); aoqi@0: bool has_appendix(); aoqi@0: ciObject* get_appendix(); aoqi@0: bool has_method_type(); aoqi@0: ciMethodType* get_method_type(); aoqi@0: ciKlass* get_declared_method_holder(); aoqi@0: int get_method_holder_index(); aoqi@0: int get_method_signature_index(); aoqi@0: aoqi@0: // Get the resolved references arrays from the constant pool aoqi@0: ciObjArray* get_resolved_references(); aoqi@0: }; aoqi@0: aoqi@0: aoqi@0: // ciSignatureStream aoqi@0: // aoqi@0: // The class is used to iterate over the elements of a method signature. aoqi@0: class ciSignatureStream : public StackObj { aoqi@0: private: aoqi@0: ciSignature* _sig; aoqi@0: int _pos; aoqi@0: // holder is a method's holder aoqi@0: ciKlass* _holder; aoqi@0: public: aoqi@0: ciSignatureStream(ciSignature* signature, ciKlass* holder = NULL) { aoqi@0: _sig = signature; aoqi@0: _pos = 0; aoqi@0: _holder = holder; aoqi@0: } aoqi@0: aoqi@0: bool at_return_type() { return _pos == _sig->count(); } aoqi@0: aoqi@0: bool is_done() { return _pos > _sig->count(); } aoqi@0: aoqi@0: void next() { aoqi@0: if (_pos <= _sig->count()) { aoqi@0: _pos++; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: ciType* type() { aoqi@0: if (at_return_type()) { aoqi@0: return _sig->return_type(); aoqi@0: } else { aoqi@0: return _sig->type_at(_pos); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // next klass in the signature aoqi@0: ciKlass* next_klass() { aoqi@0: ciKlass* sig_k; aoqi@0: if (_holder != NULL) { aoqi@0: sig_k = _holder; aoqi@0: _holder = NULL; aoqi@0: } else { aoqi@0: while (!type()->is_klass()) { aoqi@0: next(); aoqi@0: } aoqi@0: assert(!at_return_type(), "passed end of signature"); aoqi@0: sig_k = type()->as_klass(); aoqi@0: next(); aoqi@0: } aoqi@0: return sig_k; aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: aoqi@0: // ciExceptionHandlerStream aoqi@0: // aoqi@0: // The class is used to iterate over the exception handlers of aoqi@0: // a method. aoqi@0: class ciExceptionHandlerStream : public StackObj { aoqi@0: private: aoqi@0: // The method whose handlers we are traversing aoqi@0: ciMethod* _method; aoqi@0: aoqi@0: // Our current position in the list of handlers aoqi@0: int _pos; aoqi@0: int _end; aoqi@0: aoqi@0: ciInstanceKlass* _exception_klass; aoqi@0: int _bci; aoqi@0: bool _is_exact; aoqi@0: aoqi@0: public: aoqi@0: ciExceptionHandlerStream(ciMethod* method) { aoqi@0: _method = method; aoqi@0: aoqi@0: // Force loading of method code and handlers. aoqi@0: _method->code(); aoqi@0: aoqi@0: _pos = 0; aoqi@0: _end = _method->_handler_count; aoqi@0: _exception_klass = NULL; aoqi@0: _bci = -1; aoqi@0: _is_exact = false; aoqi@0: } aoqi@0: aoqi@0: ciExceptionHandlerStream(ciMethod* method, int bci, aoqi@0: ciInstanceKlass* exception_klass = NULL, aoqi@0: bool is_exact = false) { aoqi@0: _method = method; aoqi@0: aoqi@0: // Force loading of method code and handlers. aoqi@0: _method->code(); aoqi@0: aoqi@0: _pos = -1; aoqi@0: _end = _method->_handler_count + 1; // include the rethrow handler aoqi@0: _exception_klass = (exception_klass != NULL && exception_klass->is_loaded() aoqi@0: ? exception_klass aoqi@0: : NULL); aoqi@0: _bci = bci; aoqi@0: assert(_bci >= 0, "bci out of range"); aoqi@0: _is_exact = is_exact; aoqi@0: next(); aoqi@0: } aoqi@0: aoqi@0: // These methods are currently implemented in an odd way. aoqi@0: // Count the number of handlers the iterator has ever produced aoqi@0: // or will ever produce. Do not include the final rethrow handler. aoqi@0: // That is, a trivial exception handler stream will have a count aoqi@0: // of zero and produce just the rethrow handler. aoqi@0: int count(); aoqi@0: aoqi@0: // Count the number of handlers this stream will produce from now on. aoqi@0: // Include the current handler, and the final rethrow handler. aoqi@0: // The remaining count will be zero iff is_done() is true, aoqi@0: int count_remaining(); aoqi@0: aoqi@0: bool is_done() { aoqi@0: return (_pos >= _end); aoqi@0: } aoqi@0: aoqi@0: void next() { aoqi@0: _pos++; aoqi@0: if (_bci != -1) { aoqi@0: // We are not iterating over all handlers... aoqi@0: while (!is_done()) { aoqi@0: ciExceptionHandler* handler = _method->_exception_handlers[_pos]; aoqi@0: if (handler->is_in_range(_bci)) { aoqi@0: if (handler->is_catch_all()) { aoqi@0: // Found final active catch block. aoqi@0: _end = _pos+1; aoqi@0: return; aoqi@0: } else if (_exception_klass == NULL || !handler->catch_klass()->is_loaded()) { aoqi@0: // We cannot do any type analysis here. Must conservatively assume aoqi@0: // catch block is reachable. aoqi@0: return; aoqi@0: } else if (_exception_klass->is_subtype_of(handler->catch_klass())) { aoqi@0: // This catch clause will definitely catch the exception. aoqi@0: // Final candidate. aoqi@0: _end = _pos+1; aoqi@0: return; aoqi@0: } else if (!_is_exact && aoqi@0: handler->catch_klass()->is_subtype_of(_exception_klass)) { aoqi@0: // This catch block may be reachable. aoqi@0: return; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // The catch block was not pertinent. Go on. aoqi@0: _pos++; aoqi@0: } aoqi@0: } else { aoqi@0: // This is an iteration over all handlers. aoqi@0: return; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: ciExceptionHandler* handler() { aoqi@0: return _method->_exception_handlers[_pos]; aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: aoqi@0: aoqi@0: // Implementation for declarations in bytecode.hpp aoqi@0: Bytecode::Bytecode(const ciBytecodeStream* stream, address bcp): _bcp(bcp != NULL ? bcp : stream->cur_bcp()), _code(Bytecodes::code_at(NULL, addr_at(0))) {} aoqi@0: Bytecode_lookupswitch::Bytecode_lookupswitch(const ciBytecodeStream* stream): Bytecode(stream) { verify(); } aoqi@0: Bytecode_tableswitch::Bytecode_tableswitch(const ciBytecodeStream* stream): Bytecode(stream) { verify(); } aoqi@0: aoqi@0: #endif // SHARE_VM_CI_CISTREAMS_HPP