/* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ #include "precompiled.hpp" #include "classfile/bytecodeUtils.hpp" #include "classfile/systemDictionary.hpp" #include "gc/shared/gcLocker.hpp" #include "memory/resourceArea.hpp" #include "runtime/signature.hpp" #include "utilities/events.hpp" int MethodBytecodePrinter::get_s8(Method* method, int* pos) { signed char* bcp = *pos + (signed char*) (method->constMethod()->code_base()); *pos += 1; return *bcp; } int MethodBytecodePrinter::get_s16(Method* method, int* pos) { int16_t result = (int16_t) Bytes::get_Java_u2(method->constMethod()->code_base() + *pos); *pos += 2; return result; } int MethodBytecodePrinter::get_s32(Method* method, int* pos) { int32_t result = (int32_t) Bytes::get_Java_u4(method->constMethod()->code_base() + *pos); *pos += 4; return result; } int MethodBytecodePrinter::get_u8(Method* method, int* pos) { uint8_t* bcp = *pos + (uint8_t*) (method->constMethod()->code_base()); *pos += 1; return *bcp; } int MethodBytecodePrinter::get_u16(Method* method, int* pos) { uint16_t result = Bytes::get_Java_u2(method->constMethod()->code_base() + *pos); *pos += 2; return result; } int MethodBytecodePrinter::get_u16_native(Method* method, int* pos) { uint16_t result = Bytes::get_native_u2(method->constMethod()->code_base() + *pos); *pos += 2; return result; } void MethodBytecodePrinter::print_branch_target(outputStream& stream, Method* method, int target_bci) { int line_nr = method->line_number_from_bci(target_bci); if (line_nr != -1) { stream.print(" -> %d (", line_nr); } else { stream.print(" -> "); } stream.print("0x%04x", target_bci); if (line_nr != -1) { stream.print(")"); } } void MethodBytecodePrinter::print_branch_target_16(outputStream& stream, Method* method, int bci, int* pos) { int target_bci = bci + get_s16(method, pos); print_branch_target(stream, method, target_bci); } void MethodBytecodePrinter::print_branch_target_32(outputStream& stream, Method* method, int bci, int* pos) { int target_bci = bci + get_s32(method, pos); print_branch_target(stream, method, target_bci); } void MethodBytecodePrinter::print_cp_entry(outputStream& stream, Method* method, int cp_index) { ConstantPool* cp = method->constants(); constantTag tag = cp->tag_at(cp_index); // Handle only that types which are possible here. Note that methods and // fields are handeled in print_native_cp_entry_16() instead. if (tag.is_klass() || tag.is_unresolved_klass()) { stream.print(" %s", cp->klass_name_at(cp_index)->as_C_string()); } else if (tag.is_string()) { stream.print(" '%s'", cp->string_at_noresolve(cp_index)); } else if (tag.is_int()) { stream.print(" #%d", cp->int_at(cp_index)); } else if (tag.is_float()) { stream.print(" #%f", cp->float_at(cp_index)); } else if (tag.is_long()) { stream.print(" #"); stream.print_jlong(cp->long_at(cp_index)); } else if (tag.is_double()) { stream.print(" #%f", cp->double_at(cp_index)); } else if (tag.is_utf8()) { stream.print(" %s", cp->symbol_at(cp_index)->as_C_string()); } else if (tag.is_string()) { stream.print(" \"%s\"", cp->string_at_noresolve(cp_index)); } else { stream.print(" unknown cp type %d", tag.value()); } } void MethodBytecodePrinter::print_cp_entry_8(outputStream& stream, Method* method, int* pos) { int cp_index = get_u8(method, pos); print_cp_entry(stream, method, cp_index); } void MethodBytecodePrinter::print_cp_entry_16(outputStream& stream, Method* method, int* pos) { int cp_index = get_u16(method, pos); print_cp_entry(stream, method, cp_index); } void MethodBytecodePrinter::print_obj_entry_8(outputStream& stream, Method* method, int* pos) { int cp_index = method->constants()->object_to_cp_index(get_u8(method, pos)); print_cp_entry(stream, method, cp_index); } void MethodBytecodePrinter::print_obj_entry_16(outputStream& stream, Method* method, int* pos) { int cp_index = method->constants()->object_to_cp_index(get_u16_native(method, pos)); print_cp_entry(stream, method, cp_index); } void MethodBytecodePrinter::print_native_cp_entry_16(outputStream& stream, Method* method, int* pos) { ConstantPool* cp = method->constants(); int raw_index = get_u16_native(method, pos); int cp_index = cp->cache()->entry_at(raw_index)->constant_pool_index(); int class_index = cp->uncached_klass_ref_index_at(cp_index); char *klass_name = cp->klass_name_at(class_index)->as_C_string(); char *name = cp->uncached_name_ref_at(cp_index)->as_C_string(); char *type = cp->uncached_signature_ref_at(cp_index)->as_C_string(); if (type[0] == '(') { // Method stream.print(" %s.%s%s", klass_name, name, type); } else { // Field stream.print(" %s.%s", klass_name, name); } } void MethodBytecodePrinter::print_local_var(outputStream& stream, Method* method, int bci, int var_index) { ConstMethod* const_method = method->constMethod(); if (const_method->has_localvariable_table()) { int len = const_method->localvariable_table_length(); // Find the local variable entry for this slot and bci. for (int i = 0; i < len; ++i) { LocalVariableTableElement* elem = const_method->localvariable_table_start() + i; if ((elem->start_bci <= bci) && (elem->start_bci + elem->length > bci)) { if (elem->slot == var_index) { print_cp_entry(stream, method, elem->name_cp_index); } } } } else { stream.print(" ", var_index); } } void MethodBytecodePrinter::print_local_var_8(outputStream& stream, Method* method, int bci, int* pos) { int var_index = get_u8(method, pos); print_local_var(stream, method, bci, var_index); } void MethodBytecodePrinter::print_local_var_16(outputStream& stream, Method* method, int bci, int* pos) { int var_index = get_u16(method, pos); print_local_var(stream, method, bci, var_index); } void MethodBytecodePrinter::print_signed_8(outputStream& stream, Method* method, int* pos) { int val = get_s8(method, pos); stream.print("%s%d", " #", val); } void MethodBytecodePrinter::print_signed_16(outputStream& stream, Method* method, int* pos) { int val = get_s16(method, pos); stream.print("%s%d", " #", val); } void MethodBytecodePrinter::print_unsigned_8(outputStream& stream, Method* method, int* pos) { int val = get_u8(method, pos); stream.print("%s%d", " #", val); } void MethodBytecodePrinter::print_unsigned_16(outputStream& stream, Method* method, int* pos) { int val = get_u16(method, pos); stream.print("%s%d", " #", val); } void print_stacks(Method* method); void MethodBytecodePrinter::print(outputStream& stream, Method* method, int start_bci, int end_bci, bool add_line_nr, bool add_bci, int marked_bci, char const* prefix) { assert(start_bci <= end_bci, "Invalid range"); ConstMethod* const_method = method->constMethod(); address base = method->code_base(); int code_size = const_method->code_size(); int len; int last_line = -1; for (int bci = 0; bci < code_size; bci += len) { len = Bytecodes::java_length_at(method, base + bci); // Don't write bytecodes in front of the range. if (bci < start_bci) { continue; } // Don't write bytecodes after the range. if (bci >= end_bci) { return; } // If this is a wide prefix, remember that and get the real bytecode. bool is_wide = false; // Get the bytecode (and let java_code_at() handle breapoints). Bytecodes::Code code = Bytecodes::java_code_at(method, base + bci); Bytecodes::Code raw_code = Bytecodes::code_at(method, base + bci); if (code == Bytecodes::_wide) { is_wide = true; code = Bytecodes::java_code_at(method, base + bci + 1); raw_code = Bytecodes::code_at(method, base + bci + 1); } // Add prefix stream.print("%s", prefix); // Get the format specified and the name of that bytecode. For the // format we have to use the real code. char const* name = Bytecodes::name(code); // Add java line-nr if we have reached a new one. if (add_line_nr) { int line_nr = method->line_number_from_bci(bci); if (line_nr != -1) { if (last_line != line_nr) { last_line = line_nr; stream.print("%5d ", line_nr); } else { stream.print(" "); } } else { stream.print(" "); } } // Print bci if needed. if (add_bci) { stream.print("0x%04x ", bci); } // Print mark if this is the bytecode to be marked. if (bci == marked_bci) { stream.print("--> "); } else { stream.print(" "); } // Print the bytecode name. stream.print("%s", name); // Print the bytecode arguments. int pos = bci + 1; // If wide, get the format string after the first 'w'. if (is_wide) { pos += 1; // Skip 'wide' prefix. } int offset = add_bci ? 8 : 0; offset += add_line_nr ? 7 : 0; offset += 4; // Marker // Handle tableswitch differently. if (code == Bytecodes::_tableswitch) { // The index table is aligned to 4 bytes. pos = (pos + 3) & ~3; int default_target = bci + get_s32(method, &pos); int low = get_s32(method, &pos); int high = get_s32(method, &pos); int indent = offset + 4; stream.print("\n"); for (int64_t i = low; i <= high; ++i) { int target = bci + get_s32(method, &pos); stream.print("%*scase " INT64_FORMAT ": goto", indent, "", i); print_branch_target(stream, method, target); stream.print("\n"); } stream.print("%*sdefault: goto", indent, ""); print_branch_target(stream, method, default_target); stream.print("\n"); continue; } // Handle lookupswitch differently. if (code == Bytecodes::_lookupswitch) { // The index table is aligned to 4 bytes. pos = (pos + 3) & ~3; int default_target = bci + get_s32(method, &pos); int cases = get_s32(method, &pos); int indent = offset + 4; stream.print("\n"); for (int i = 0; i < cases; ++i) { int value = get_s32(method, &pos); int target = bci + get_s32(method, &pos); stream.print("%*scase %d: goto", indent, "", value); print_branch_target(stream, method, target); stream.print("\n"); } stream.print("%*sdefault: goto", indent, ""); print_branch_target(stream, method, default_target); stream.print("\n"); continue; } stream.print("\n"); } } char const* MethodBytecodePrinter::get_bytecodes(Method* method, int start_bci, int end_bci, bool add_line_nr, bool add_bci, int marked_bci, char const* prefix) { stringStream stream; print(stream, method, start_bci, end_bci, add_line_nr, add_bci, marked_bci, prefix); return stream.as_string(); } char const* MethodBytecodePrinter::get_klass_name(Method* method, int cp_index) { ConstantPool* cp = method->constants(); int class_index = cp->klass_ref_index_at(cp_index); Symbol* clazz = cp->klass_at_noresolve(class_index); char* result = clazz->as_C_string(); char* pos = result; while (*pos) { if (*pos == '/') { *pos = '.'; } ++pos; } return result; } char const* MethodBytecodePrinter::get_method_name(Method* method, int cp_index) { char const* class_name = get_klass_name(method, cp_index); ConstantPool* cp = method->constants(); int name_and_type_index = cp->name_and_type_ref_index_at(cp_index); int name_index = cp->name_ref_index_at(name_and_type_index); int type_index = cp->signature_ref_index_at(name_and_type_index); Symbol* name = cp->symbol_at(name_index); Symbol* signature = cp->symbol_at(type_index); stringStream ss; ss.print("%s%s%s%s%s%s", get_klass_name(method, cp_index), ".", name->as_C_string(), "(", signature->as_C_string(), ")"); return ss.as_string(); } char const* MethodBytecodePrinter::get_field_and_class(Method* method, int cp_index) { char const* class_name = get_klass_name(method, cp_index); ConstantPool* cp = method->constants(); int name_and_type_index = cp->name_and_type_ref_index_at(cp_index); int name_index = cp->name_ref_index_at(name_and_type_index); Symbol* name = cp->symbol_at(name_index); stringStream ss; ss.print("%s%s%s", get_klass_name(method, cp_index), ".", name->as_C_string()); return ss.as_string(); } TrackingStackEntry::TrackingStackEntry(BasicType type) : _entry(INVALID + type * SCALE) { } TrackingStackEntry::TrackingStackEntry(int bci, BasicType type) : _entry(bci + type * SCALE) { assert(bci >= 0, "BCI must be >= 0"); assert(bci < 65536, "BCI must be < 65536"); } int TrackingStackEntry::get_bci() { return _entry % SCALE; } BasicType TrackingStackEntry::get_type() { return BasicType (_entry / SCALE); } TrackingStackEntry TrackingStackEntry::merge(TrackingStackEntry other) { if (get_type() != other.get_type()) { if (((get_type() == T_OBJECT) || (get_type() == T_ARRAY)) && ((other.get_type() == T_OBJECT) || (other.get_type() == T_ARRAY))) { if (get_bci() == other.get_bci()) { return TrackingStackEntry(get_bci(), T_OBJECT); } else { return TrackingStackEntry(T_OBJECT); } } else { return TrackingStackEntry(T_CONFLICT); } } if (get_bci() == other.get_bci()) { return *this; } else { return TrackingStackEntry(get_type()); } } TrackingStack::TrackingStack(const TrackingStack ©) { for (int i = 0; i < copy.get_size(); i++) { push_raw(copy._stack.at(i)); } } void TrackingStack::push_raw(TrackingStackEntry entry) { if (entry.get_type() == T_VOID) { return; } _stack.push(entry); } void TrackingStack::push(TrackingStackEntry entry) { if (type2size[entry.get_type()] == 2) { push_raw(entry); push_raw(entry); } else { push_raw(entry); } } void TrackingStack::push(int bci, BasicType type) { push(TrackingStackEntry(bci, type)); } void TrackingStack::pop(int slots) { for (int i = 0; i < slots; ++i) { _stack.pop(); } assert(get_size() >= 0, "Popped too many slots"); } void TrackingStack::merge(TrackingStack const& other) { assert(get_size() == other.get_size(), "Stacks not of same size"); for (int i = get_size() - 1; i >= 0; --i) { _stack.at(i).merge(other._stack.at(i)); } } int TrackingStack::get_size() const { return _stack.length(); } TrackingStackEntry TrackingStack::get_entry(int slot) { assert(slot >= 0, "Slot < 0"); assert(slot < get_size(), "Slot >= size"); return _stack.at(get_size() - slot - 1); } static TrackingStackSource createInvalidSource(int bci) { return TrackingStackSource(TrackingStackSource::INVALID, bci, "invalid"); } static TrackingStackSource createLocalVarSource(int bci, Method* method, int slot) { // We assume outermost caller has ResourceMark. stringStream reason; if (method->has_localvariable_table()) { for (int i = 0; i < method->localvariable_table_length(); i++) { LocalVariableTableElement* elem = method->localvariable_table_start() + i; int start = elem->start_bci; int end = start + elem->length; if ((bci >= start) && (bci < end) && (elem->slot == slot)) { ConstantPool* cp = method->constants(); reason.print("loaded from local variable '%s'", cp->symbol_at(elem->name_cp_index)->as_C_string()); return TrackingStackSource(TrackingStackSource::LOCAL_VAR, bci, reason.as_string()); } } } // Handle at least some cases we know. if (!method->is_static() && (slot == 0)) { reason.print("loaded from local variable 'this'"); } else { int curr = method->is_static() ? 0 : 1; SignatureStream ss(method->signature()); int param_index = 0; bool found = false; for (SignatureStream ss(method->signature()); !ss.is_done(); ss.next()) { if (ss.at_return_type()) { continue; } int size = type2size[ss.type()]; if ((slot >= curr) && (slot < curr + size)) { found = true; break; } param_index += 1; curr += size; } if (found) { if (param_index < 5) { static char const* s[] = {"first", "second", "third", "forth", "fifth"}; reason.print("loaded from the %s parameter of the method", s[param_index]); } else { reason.print("loaded from the parameter nr. %d of the method", 1 + param_index); } } else { // This is the best we can do. reason.print("loaded from a local variable at slot %d", slot); } } return TrackingStackSource(TrackingStackSource::LOCAL_VAR, bci, reason.as_string()); } static TrackingStackSource createMethodSource(int bci, Method* method, int cp_index) { // We assume outermost caller has ResourceMark. stringStream reason; reason.print("returned from %s", MethodBytecodePrinter::get_method_name(method, cp_index)); return TrackingStackSource(TrackingStackSource::METHOD, bci, reason.as_string()); } static TrackingStackSource createConstantSource(int bci) { return TrackingStackSource(TrackingStackSource::CONSTANT, bci, "loaded from a constant"); } static TrackingStackSource createArraySource(int bci, TrackingStackSource const& array_source, TrackingStackSource const& index_source) { // We assume outermost caller has ResourceMark. stringStream reason; if (array_source.get_type() != TrackingStackSource::INVALID) { if (index_source.get_type() != TrackingStackSource::INVALID) { reason.print("loaded from an array (which itself was %s) with an index %s", array_source.as_string(), index_source.as_string()); } else { reason.print("loaded from an array (which itself was %s)", array_source.as_string()); } } else { if (index_source.get_type() != TrackingStackSource::INVALID) { reason.print("loaded from an array with an index %s", index_source.as_string()); } else { reason.print("loaded from an array"); } } return TrackingStackSource(TrackingStackSource::ARRAY_ELEM, bci, reason.as_string()); } static TrackingStackSource createFieldSource(int bci, Method* method, int cp_index, TrackingStackSource const& object_source) { // We assume outermost caller has ResourceMark. stringStream reason; if (object_source.get_type() != TrackingStackSource::INVALID) { reason.print("loaded from field %s of an object %s", MethodBytecodePrinter::get_field_and_class(method, cp_index), object_source.as_string()); } else { reason.print("loaded from field %s of an object", MethodBytecodePrinter::get_field_and_class(method, cp_index)); } return TrackingStackSource(TrackingStackSource::FIELD_ELEM, bci, reason.as_string()); } static TrackingStackSource createStaticFieldSource(int bci, Method* method, int cp_index) { // We assume outermost caller has ResourceMark. stringStream reason; reason.print("loaded from static field %s", MethodBytecodePrinter::get_field_and_class(method, cp_index)); return TrackingStackSource(TrackingStackSource::FIELD_ELEM, bci, reason.as_string()); } TrackingStackCreator::TrackingStackCreator(Method* method, int bci) : _method(method) { ConstMethod* const_method = method->constMethod(); int len = const_method->code_size(); _nr_of_entries = 0; _max_entries = 1000000; _stacks = new GrowableArray (len+1); for (int i = 0; i <= len; ++i) { _stacks->push(NULL); } // Initialize stack a bci 0. _stacks->at_put(0, new TrackingStack()); // And initialize for all exception handlers. if (const_method->has_exception_handler()) { ExceptionTableElement *et = const_method->exception_table_start(); for (int i = 0; i < const_method->exception_table_length(); ++i) { u2 index = et[i].handler_pc; if (_stacks->at(index) == NULL) { _stacks->at_put(index, new TrackingStack()); _stacks->at(index)->push(index, T_OBJECT); } } } _all_processed = false; _added_one = true; // Do this until each bytecode hash a stack or we haven't // added a new stack in one iteration. while (!_all_processed && _added_one) { _all_processed = true; _added_one = false; for (int i = 0; i < len; ) { i += do_instruction(i); // If we want the data only for a bci, we can possibly end early. if ((bci == i) && (_stacks->at(i) != NULL)) { _all_processed = true; break; } if (_nr_of_entries > _max_entries) { return; } } } } TrackingStackCreator::~TrackingStackCreator() { for (int i = 0; i < _stacks->length(); ++i) { delete _stacks->at(i); } } void TrackingStackCreator::merge(int bci, TrackingStack* stack) { assert(stack != _stacks->at(bci), "Cannot merge itself"); if (_stacks->at(bci) != NULL) { stack->merge(*_stacks->at(bci)); } else { // Got a new stack, so count the entries. _nr_of_entries += stack->get_size(); } delete _stacks->at(bci); _stacks->at_put(bci, new TrackingStack(*stack)); } int TrackingStackCreator::do_instruction(int bci) { ConstMethod* const_method = _method->constMethod(); address code_base = _method->constMethod()->code_base(); // We use the java code, since we don't want to cope with all the fast variants. int len = Bytecodes::java_length_at(_method, code_base + bci); // If we have no stack for this bci, we cannot process the bytecode now. if (_stacks->at(bci) == NULL) { _all_processed = false; return len; } TrackingStack* stack = new TrackingStack(*_stacks->at(bci)); // dest_bci is != -1 if we branch. int dest_bci = -1; // This is for table and lookup switch. static const int initial_length = 2; GrowableArray dests(initial_length); bool flow_ended = false; // Get the bytecode. bool is_wide = false; Bytecodes::Code raw_code = Bytecodes::code_at(_method, code_base + bci); Bytecodes::Code code = Bytecodes::java_code_at(_method, code_base + bci); int pos = bci + 1; if (code == Bytecodes::_wide) { is_wide = true; code = Bytecodes::java_code_at(_method, code_base + bci + 1); pos += 1; } // Now simulate the action of each bytecode. switch (code) { case Bytecodes::_nop: case Bytecodes::_aconst_null: case Bytecodes::_iconst_m1: case Bytecodes::_iconst_0: case Bytecodes::_iconst_1: case Bytecodes::_iconst_2: case Bytecodes::_iconst_3: case Bytecodes::_iconst_4: case Bytecodes::_iconst_5: case Bytecodes::_lconst_0: case Bytecodes::_lconst_1: case Bytecodes::_fconst_0: case Bytecodes::_fconst_1: case Bytecodes::_fconst_2: case Bytecodes::_dconst_0: case Bytecodes::_dconst_1: case Bytecodes::_bipush: case Bytecodes::_sipush: case Bytecodes::_iload: case Bytecodes::_lload: case Bytecodes::_fload: case Bytecodes::_dload: case Bytecodes::_aload: case Bytecodes::_iload_0: case Bytecodes::_iload_1: case Bytecodes::_iload_2: case Bytecodes::_iload_3: case Bytecodes::_lload_0: case Bytecodes::_lload_1: case Bytecodes::_lload_2: case Bytecodes::_lload_3: case Bytecodes::_fload_0: case Bytecodes::_fload_1: case Bytecodes::_fload_2: case Bytecodes::_fload_3: case Bytecodes::_dload_0: case Bytecodes::_dload_1: case Bytecodes::_dload_2: case Bytecodes::_dload_3: case Bytecodes::_aload_0: case Bytecodes::_aload_1: case Bytecodes::_aload_2: case Bytecodes::_aload_3: case Bytecodes::_iinc: case Bytecodes::_new: stack->push(bci, Bytecodes::result_type(code)); break; case Bytecodes::_ldc: case Bytecodes::_ldc_w: case Bytecodes::_ldc2_w: { int cp_index; ConstantPool* cp = _method->constants(); if (code == Bytecodes::_ldc) { cp_index = *(uint8_t*) (code_base + pos); if (raw_code == Bytecodes::_fast_aldc) { cp_index = cp->object_to_cp_index(cp_index); } } else { if (raw_code == Bytecodes::_fast_aldc_w) { cp_index = Bytes::get_native_u2(code_base + pos); cp_index = cp->object_to_cp_index(cp_index); } else { cp_index = Bytes::get_Java_u2(code_base + pos); } } constantTag tag = cp->tag_at(cp_index); if (tag.is_klass() || tag.is_unresolved_klass() || tag.is_method() || tag.is_interface_method() || tag.is_field() || tag.is_string()) { stack->push(bci, T_OBJECT); } else if (tag.is_int()) { stack->push(bci, T_INT); } else if (tag.is_long()) { stack->push(bci, T_LONG); } else if (tag.is_float()) { stack->push(bci, T_FLOAT); } else if (tag.is_double()) { stack->push(bci, T_DOUBLE); } else { assert(false, "Unexpected tag"); } break; } case Bytecodes::_iaload: case Bytecodes::_faload: case Bytecodes::_aaload: case Bytecodes::_baload: case Bytecodes::_caload: case Bytecodes::_saload: case Bytecodes::_laload: case Bytecodes::_daload: stack->pop(2); stack->push(bci, Bytecodes::result_type(code)); break; case Bytecodes::_istore: case Bytecodes::_lstore: case Bytecodes::_fstore: case Bytecodes::_dstore: case Bytecodes::_astore: case Bytecodes::_istore_0: case Bytecodes::_istore_1: case Bytecodes::_istore_2: case Bytecodes::_istore_3: case Bytecodes::_lstore_0: case Bytecodes::_lstore_1: case Bytecodes::_lstore_2: case Bytecodes::_lstore_3: case Bytecodes::_fstore_0: case Bytecodes::_fstore_1: case Bytecodes::_fstore_2: case Bytecodes::_fstore_3: case Bytecodes::_dstore_0: case Bytecodes::_dstore_1: case Bytecodes::_dstore_2: case Bytecodes::_dstore_3: case Bytecodes::_astore_0: case Bytecodes::_astore_1: case Bytecodes::_astore_2: case Bytecodes::_astore_3: case Bytecodes::_iastore: case Bytecodes::_lastore: case Bytecodes::_fastore: case Bytecodes::_dastore: case Bytecodes::_aastore: case Bytecodes::_bastore: case Bytecodes::_castore: case Bytecodes::_sastore: case Bytecodes::_pop: case Bytecodes::_pop2: case Bytecodes::_monitorenter: case Bytecodes::_monitorexit: case Bytecodes::_breakpoint: stack->pop(-Bytecodes::depth(code)); break; case Bytecodes::_dup: stack->push_raw(stack->get_entry(0)); break; case Bytecodes::_dup_x1: { TrackingStackEntry top1 = stack->get_entry(0); TrackingStackEntry top2 = stack->get_entry(1); stack->pop(2); stack->push_raw(top1); stack->push_raw(top2); stack->push_raw(top1); break; } case Bytecodes::_dup_x2: { TrackingStackEntry top1 = stack->get_entry(0); TrackingStackEntry top2 = stack->get_entry(1); TrackingStackEntry top3 = stack->get_entry(2); stack->pop(3); stack->push_raw(top1); stack->push_raw(top3); stack->push_raw(top2); stack->push_raw(top1); break; } case Bytecodes::_dup2: stack->push_raw(stack->get_entry(1)); stack->push_raw(stack->get_entry(1)); break; case Bytecodes::_dup2_x1: { TrackingStackEntry top1 = stack->get_entry(0); TrackingStackEntry top2 = stack->get_entry(1); TrackingStackEntry top3 = stack->get_entry(2); stack->pop(3); stack->push_raw(top2); stack->push_raw(top1); stack->push_raw(top3); stack->push_raw(top2); stack->push_raw(top1); break; } case Bytecodes::_dup2_x2: { TrackingStackEntry top1 = stack->get_entry(0); TrackingStackEntry top2 = stack->get_entry(1); TrackingStackEntry top3 = stack->get_entry(2); TrackingStackEntry top4 = stack->get_entry(3); stack->pop(4); stack->push_raw(top2); stack->push_raw(top1); stack->push_raw(top4); stack->push_raw(top3); stack->push_raw(top2); stack->push_raw(top1); break; } case Bytecodes::_swap: { TrackingStackEntry top1 = stack->get_entry(0); TrackingStackEntry top2 = stack->get_entry(1); stack->pop(2); stack->push(top1); stack->push(top2); break; } case Bytecodes::_iadd: case Bytecodes::_ladd: case Bytecodes::_fadd: case Bytecodes::_dadd: case Bytecodes::_isub: case Bytecodes::_lsub: case Bytecodes::_fsub: case Bytecodes::_dsub: case Bytecodes::_imul: case Bytecodes::_lmul: case Bytecodes::_fmul: case Bytecodes::_dmul: case Bytecodes::_idiv: case Bytecodes::_ldiv: case Bytecodes::_fdiv: case Bytecodes::_ddiv: case Bytecodes::_irem: case Bytecodes::_lrem: case Bytecodes::_frem: case Bytecodes::_drem: case Bytecodes::_iand: case Bytecodes::_land: case Bytecodes::_ior: case Bytecodes::_lor: case Bytecodes::_ixor: case Bytecodes::_lxor: stack->pop(2 * type2size[Bytecodes::result_type(code)]); stack->push(bci, Bytecodes::result_type(code)); break; case Bytecodes::_ineg: case Bytecodes::_lneg: case Bytecodes::_fneg: case Bytecodes::_dneg: stack->pop(type2size[Bytecodes::result_type(code)]); stack->push(bci, Bytecodes::result_type(code)); break; case Bytecodes::_ishl: case Bytecodes::_lshl: case Bytecodes::_ishr: case Bytecodes::_lshr: case Bytecodes::_iushr: case Bytecodes::_lushr: stack->pop(1 + type2size[Bytecodes::result_type(code)]); stack->push(bci, Bytecodes::result_type(code)); break; case Bytecodes::_i2l: case Bytecodes::_i2f: case Bytecodes::_i2d: case Bytecodes::_f2i: case Bytecodes::_f2l: case Bytecodes::_f2d: case Bytecodes::_i2b: case Bytecodes::_i2c: case Bytecodes::_i2s: stack->pop(1); stack->push(bci, Bytecodes::result_type(code)); break; case Bytecodes::_l2i: case Bytecodes::_l2f: case Bytecodes::_l2d: case Bytecodes::_d2i: case Bytecodes::_d2l: case Bytecodes::_d2f: stack->pop(2); stack->push(bci, Bytecodes::result_type(code)); break; case Bytecodes::_lcmp: case Bytecodes::_fcmpl: case Bytecodes::_fcmpg: case Bytecodes::_dcmpl: case Bytecodes::_dcmpg: stack->pop(1 - Bytecodes::depth(code)); stack->push(bci, T_INT); break; case Bytecodes::_ifeq: case Bytecodes::_ifne: case Bytecodes::_iflt: case Bytecodes::_ifge: case Bytecodes::_ifgt: case Bytecodes::_ifle: case Bytecodes::_if_icmpeq: case Bytecodes::_if_icmpne: case Bytecodes::_if_icmplt: case Bytecodes::_if_icmpge: case Bytecodes::_if_icmpgt: case Bytecodes::_if_icmple: case Bytecodes::_if_acmpeq: case Bytecodes::_if_acmpne: case Bytecodes::_ifnull: case Bytecodes::_ifnonnull: stack->pop(-Bytecodes::depth(code)); dest_bci = bci + (int16_t) Bytes::get_Java_u2(code_base + pos); break; case Bytecodes::_jsr: // NOTE: Bytecodes has wrong depth for jsr. stack->push(bci, T_ADDRESS); dest_bci = bci + (int16_t) Bytes::get_Java_u2(code_base + pos); flow_ended = true; break; case Bytecodes::_jsr_w: { // NOTE: Bytecodes has wrong depth for jsr. stack->push(bci, T_ADDRESS); dest_bci = bci + (int32_t) Bytes::get_Java_u4(code_base + pos); flow_ended = true; break; } case Bytecodes::_ret: // We don't track local variables, so we cannot know were we // return. This makes the stacks imprecise, but we have to // live with that. flow_ended = true; break; case Bytecodes::_tableswitch: { stack->pop(1); pos = (pos + 3) & ~3; dest_bci = bci + (int32_t) Bytes::get_Java_u4(code_base + pos); int low = (int32_t) Bytes::get_Java_u4(code_base + pos + 4); int high = (int32_t) Bytes::get_Java_u4(code_base + pos + 8); for (int64_t i = low; i <= high; ++i) { dests.push(bci + (int32_t) Bytes::get_Java_u4(code_base + pos + 12 + 4 * (i - low))); } break; } case Bytecodes::_lookupswitch: { stack->pop(1); pos = (pos + 3) & ~3; dest_bci = bci + (int32_t) Bytes::get_Java_u4(code_base + pos); int nr_of_dests = (int32_t) Bytes::get_Java_u4(code_base + pos + 4); for (int i = 0; i < nr_of_dests; ++i) { dests.push(bci + (int32_t) Bytes::get_Java_u4(code_base + pos + 12 + 8 * i)); } break; } case Bytecodes::_ireturn: case Bytecodes::_lreturn: case Bytecodes::_freturn: case Bytecodes::_dreturn: case Bytecodes::_areturn: case Bytecodes::_return: case Bytecodes::_athrow: stack->pop(-Bytecodes::depth(code)); flow_ended = true; break; case Bytecodes::_getstatic: case Bytecodes::_getfield: { int cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); ConstantPool* cp = _method->constants(); int name_and_type_index = cp->name_and_type_ref_index_at(cp_index); int type_index = cp->signature_ref_index_at(name_and_type_index); Symbol* signature = cp->symbol_at(type_index); stack->pop(1 - Bytecodes::depth(code)); stack->push(bci, char2type((char) signature->char_at(0))); break; } case Bytecodes::_putstatic: case Bytecodes::_putfield: { int cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); ConstantPool* cp = _method->constants(); int name_and_type_index = cp->name_and_type_ref_index_at(cp_index); int type_index = cp->signature_ref_index_at(name_and_type_index); Symbol* signature = cp->symbol_at(type_index); ResultTypeFinder result_type(signature); stack->pop(type2size[char2type((char) signature->char_at(0))] - Bytecodes::depth(code) - 1); break; } case Bytecodes::_invokevirtual: case Bytecodes::_invokespecial: case Bytecodes::_invokestatic: case Bytecodes::_invokeinterface: case Bytecodes::_invokedynamic: { ConstantPool* cp = _method->constants(); int cp_index; if (code == Bytecodes::_invokedynamic) { cp_index = ((int) Bytes::get_native_u4(code_base + pos)); } else { cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); } int name_and_type_index = cp->name_and_type_ref_index_at(cp_index); int type_index = cp->signature_ref_index_at(name_and_type_index); Symbol* signature = cp->symbol_at(type_index); if ((code != Bytecodes::_invokestatic) && (code != Bytecodes::_invokedynamic)) { // Pop class. stack->pop(1); } stack->pop(ArgumentSizeComputer(signature).size()); ResultTypeFinder result_type(signature); stack->push(bci, result_type.type()); break; } case Bytecodes::_newarray: case Bytecodes::_anewarray: case Bytecodes::_instanceof: stack->pop(1); stack->push(bci, Bytecodes::result_type(code)); break; case Bytecodes::_arraylength: // The return type of arraylength is wrong in the bytecodes table (T_VOID). stack->pop(1); stack->push(bci, T_INT); break; case Bytecodes::_checkcast: break; case Bytecodes::_multianewarray: stack->pop(*(uint8_t*) (code_base + pos + 2)); stack->push(bci, T_OBJECT); break; case Bytecodes::_goto: stack->pop(-Bytecodes::depth(code)); dest_bci = bci + (int16_t) Bytes::get_Java_u2(code_base + pos); flow_ended = true; break; case Bytecodes::_goto_w: stack->pop(-Bytecodes::depth(code)); dest_bci = bci + (int32_t) Bytes::get_Java_u4(code_base + pos); flow_ended = true; break; default: // Allow at least the bcis which have stack info to work. _all_processed = false; _added_one = false; delete stack; return len; } // Put new stack to the next instruction, if we might reach if from // this bci. if (!flow_ended) { if (_stacks->at(bci + len) == NULL) { _added_one = true; } merge(bci + len, stack); } // Put the stack to the branch target too. if (dest_bci != -1) { if (_stacks->at(dest_bci) == NULL) { _added_one = true; } merge(dest_bci, stack); } // If we have more than one branch target, process these too. for (int64_t i = 0; i < dests.length(); ++i) { if (_stacks->at(dests.at(i)) == NULL) { _added_one = true; } merge(dests.at(i), stack); } delete stack; return len; } TrackingStackSource TrackingStackCreator::get_source(int bci, int slot, int max_detail) { assert(bci >= 0, "BCI too low"); assert(bci < get_size(), "BCI to large"); if (max_detail <= 0) { return createInvalidSource(bci); } if (_stacks->at(bci) == NULL) { return createInvalidSource(bci); } TrackingStack* stack = _stacks->at(bci); assert(slot >= 0, "Slot nr. too low"); assert(slot < stack->get_size(), "Slot nr. too large"); TrackingStackEntry entry = stack->get_entry(slot); if (!entry.has_bci()) { return createInvalidSource(bci); } // Get the bytecode. int source_bci = entry.get_bci(); address code_base = _method->constMethod()->code_base(); Bytecodes::Code code = Bytecodes::java_code_at(_method, code_base + source_bci); bool is_wide = false; int pos = source_bci + 1; if (code == Bytecodes::_wide) { is_wide = true; code = Bytecodes::java_code_at(_method, code_base + source_bci + 1); pos += 1; } switch (code) { case Bytecodes::_iload: case Bytecodes::_lload: case Bytecodes::_fload: case Bytecodes::_dload: case Bytecodes::_aload: { int index; if (is_wide) { index = Bytes::get_Java_u2(code_base + source_bci + 2); } else { index = *(uint8_t*) (code_base + source_bci + 1); } return createLocalVarSource(source_bci, _method, index); } case Bytecodes::_iload_0: case Bytecodes::_lload_0: case Bytecodes::_fload_0: case Bytecodes::_dload_0: case Bytecodes::_aload_0: return createLocalVarSource(source_bci, _method, 0); case Bytecodes::_iload_1: case Bytecodes::_lload_1: case Bytecodes::_fload_1: case Bytecodes::_dload_1: case Bytecodes::_aload_1: return createLocalVarSource(source_bci, _method, 1); case Bytecodes::_iload_2: case Bytecodes::_lload_2: case Bytecodes::_fload_2: case Bytecodes::_dload_2: case Bytecodes::_aload_2: return createLocalVarSource(source_bci, _method, 2); case Bytecodes::_lload_3: case Bytecodes::_iload_3: case Bytecodes::_fload_3: case Bytecodes::_dload_3: case Bytecodes::_aload_3: return createLocalVarSource(source_bci, _method, 3); case Bytecodes::_aconst_null: case Bytecodes::_iconst_m1: case Bytecodes::_iconst_0: case Bytecodes::_iconst_1: case Bytecodes::_iconst_2: case Bytecodes::_iconst_3: case Bytecodes::_iconst_4: case Bytecodes::_iconst_5: case Bytecodes::_lconst_0: case Bytecodes::_lconst_1: case Bytecodes::_fconst_0: case Bytecodes::_fconst_1: case Bytecodes::_fconst_2: case Bytecodes::_dconst_0: case Bytecodes::_dconst_1: case Bytecodes::_bipush: case Bytecodes::_sipush: return createConstantSource(source_bci); case Bytecodes::_iaload: case Bytecodes::_faload: case Bytecodes::_aaload: case Bytecodes::_baload: case Bytecodes::_caload: case Bytecodes::_saload: case Bytecodes::_laload: case Bytecodes::_daload: { TrackingStackSource array_source = get_source(source_bci, 1, max_detail - 1); TrackingStackSource index_source = get_source(source_bci, 0, max_detail - 1); return createArraySource(source_bci, array_source, index_source); } case Bytecodes::_invokevirtual: case Bytecodes::_invokespecial: case Bytecodes::_invokestatic: case Bytecodes::_invokeinterface: { int cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); return createMethodSource(source_bci, _method, cp_index); } case Bytecodes::_getstatic: return createStaticFieldSource(source_bci, _method, Bytes::get_native_u2(code_base + pos) + ConstantPool::CPCACHE_INDEX_TAG); case Bytecodes::_getfield: { int cp_index = Bytes::get_native_u2(code_base + pos) + ConstantPool::CPCACHE_INDEX_TAG; TrackingStackSource object_source = get_source(source_bci, 0, max_detail - 1); return createFieldSource(source_bci, _method, cp_index, object_source); } default: return createInvalidSource(bci); } } int TrackingStackCreator::get_null_pointer_slot(int bci, char const** reason) { // If this NPE was created via reflection, we have no real NPE. if (_method->method_holder() == SystemDictionary::reflect_NativeConstructorAccessorImpl_klass()) { return -2; } // Get the bytecode. address code_base = _method->constMethod()->code_base(); Bytecodes::Code code = Bytecodes::java_code_at(_method, code_base + bci); int pos = bci + 1; if (code == Bytecodes::_wide) { code = Bytecodes::java_code_at(_method, code_base + bci + 1); pos += 1; } int result = -1; switch (code) { case Bytecodes::_iaload: if (reason != NULL) { *reason = "while trying to load from a null int array"; } result = 1; break; case Bytecodes::_faload: if (reason != NULL) { *reason = "while trying to load from a null float array"; } result = 1; break; case Bytecodes::_aaload: if (reason != NULL) { *reason = "while trying to load from a null object array"; } result = 1; break; case Bytecodes::_baload: if (reason != NULL) { *reason = "while trying to load from a null byte (or boolean) array"; } result = 1; break; case Bytecodes::_caload: if (reason != NULL) { *reason = "while trying to load from a null char array"; } result = 1; break; case Bytecodes::_saload: if (reason != NULL) { *reason = "while trying to load from a null short array"; } result = 1; break; case Bytecodes::_laload: if (reason != NULL) { *reason = "while trying to load from a null long array"; } result = 1; break; case Bytecodes::_daload: if (reason != NULL) { *reason = "while trying to load from a null double array"; } result = 1; break; case Bytecodes::_iastore: if (reason != NULL) { *reason = "while trying to store to a null int array"; } result = 2; break; case Bytecodes::_lastore: if (reason != NULL) { *reason = "while trying to store to a null long array"; } result = 3; break; case Bytecodes::_fastore: if (reason != NULL) { *reason = "while trying to store to a null float array"; } result = 2; break; case Bytecodes::_dastore: if (reason != NULL) { *reason = "while trying to store to a null double array"; } result = 3; break; case Bytecodes::_aastore: if (reason != NULL) { *reason = "while trying to store to a null object array"; } result = 2; break; case Bytecodes::_bastore: if (reason != NULL) { *reason = "while trying to store to a null byte (or boolean) array"; } result = 2; break; case Bytecodes::_castore: if (reason != NULL) { *reason = "while trying to store to a null char array"; } result = 2; break; case Bytecodes::_sastore: if (reason != NULL) { *reason = "while trying to store to a null short array"; } result = 2; break; case Bytecodes::_getfield: { if (reason != NULL) { int cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); ConstantPool* cp = _method->constants(); int name_and_type_index = cp->name_and_type_ref_index_at(cp_index); int name_index = cp->name_ref_index_at(name_and_type_index); Symbol* name = cp->symbol_at(name_index); stringStream ss; ss.print("while trying to read the field '%s' of a null object", name->as_C_string()); *reason = ss.as_string(); } result = 0; } break; case Bytecodes::_arraylength: if (reason != NULL) { *reason = "while trying to get the length of a null array"; } result = 0; break; case Bytecodes::_athrow: if (reason != NULL) { *reason = "while trying to throw a null exception object"; } result = 0; break; case Bytecodes::_monitorenter: if (reason != NULL) { *reason = "while trying to enter a null monitor"; } result = 0; break; case Bytecodes::_monitorexit: if (reason != NULL) { *reason = "while trying to exit a null monitor"; } result = 0; break; case Bytecodes::_putfield: { int cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); ConstantPool* cp = _method->constants(); int name_and_type_index = cp->name_and_type_ref_index_at(cp_index); int type_index = cp->signature_ref_index_at(name_and_type_index); Symbol* signature = cp->symbol_at(type_index); if (reason != NULL) { stringStream ss; ss.print("while trying to write the field %s of a null object", MethodBytecodePrinter::get_field_and_class(_method, cp_index)); *reason = ss.as_string(); } result = type2size[char2type((char) signature->char_at(0))]; } break; case Bytecodes::_invokevirtual: case Bytecodes::_invokespecial: case Bytecodes::_invokeinterface: { int cp_index = Bytes::get_native_u2(code_base+ pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); ConstantPool* cp = _method->constants(); int name_and_type_index = cp->name_and_type_ref_index_at(cp_index); int name_index = cp->name_ref_index_at(name_and_type_index); int type_index = cp->signature_ref_index_at(name_and_type_index); Symbol* name = cp->symbol_at(name_index); Symbol* signature = cp->symbol_at(type_index); // Assume the the call of a constructor can never cause a NullPointerException // (which is true in Java). This is mainly used to avoid generating wrong // messages for NullPointerExceptions created explicitely by new in Java code. if (name != vmSymbols::object_initializer_name()) { if (reason != NULL) { stringStream ss; ss.print("while trying to invoke the method %s on a null reference", MethodBytecodePrinter::get_method_name(_method, cp_index)); *reason = ss.as_string(); } result = ArgumentSizeComputer(signature).size(); } else { result = -2; } } break; default: break; } return result; }