/* * 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/systemDictionary.hpp" #include "gc/shared/gcLocker.hpp" #include "interpreter/bytecodeUtils.hpp" #include "memory/resourceArea.hpp" #include "runtime/signature.hpp" #include "runtime/safepointVerifiers.hpp" #include "utilities/events.hpp" #include "utilities/ostream.hpp" class SimulatedOperandStack; class ExceptionMessageBuilder; // The entries of a SimulatedOperandStack. They carry the analysis // information gathered for the slot. class StackSlotAnalysisData { private: friend class SimulatedOperandStack; friend class ExceptionMessageBuilder; unsigned int _bci:17; // The bci of the bytecode that pushed the current value on the operand stack. // INVALID if ambiguous, e.g. after a control flow merge. // 16 bits for bci (max bytecode size) and one for INVALID. unsigned int _type:15; // The BasicType of the value on the operand stack. // Merges this slot data with the given one and returns the result. If // the bcis of the two merged objects are different, the bci of the result // will be undefined. If the types are different, the result type is T_CONFLICT. // (An exception is if one type is an array and the other is object, then // the result type will be T_OBJECT). StackSlotAnalysisData merge(StackSlotAnalysisData other); public: // Creates a new object with an invalid bci and the given type. StackSlotAnalysisData(BasicType type = T_CONFLICT); // Creates a new object with the given bci and type. StackSlotAnalysisData(int bci, BasicType type); enum { // An invalid bytecode index, as > 65535. INVALID = 0x1FFFF }; // Returns the bci. If the bci is invalid, INVALID is returned. unsigned int get_bci(); // Returns true, if the bci is not invalid. bool has_bci() { return get_bci() != INVALID; } // Returns the type of the slot data. BasicType get_type(); }; // A stack consisting of SimulatedOperandStackEntries. // This represents the analysis information for the operand stack // for a given bytecode at a given bci. class SimulatedOperandStack: CHeapObj { private: friend class ExceptionMessageBuilder; friend class StackSlotAnalysisData; // The stack. GrowableArray _stack; SimulatedOperandStack() { }; SimulatedOperandStack(const SimulatedOperandStack ©); // Pushes the given slot data. void push_raw(StackSlotAnalysisData slotData); // Like push_raw, but if the slotData has type long or double, we push two. void push(StackSlotAnalysisData slotData); // Like push(slotData), but using bci/type to create an instance of // StackSlotAnalysisData first. void push(int bci, BasicType type); // Pops the given number of entries. void pop(int slots); // Merges this with the given stack by merging all entries. The // size of the stacks must be the same. void merge(SimulatedOperandStack const& other); public: // Returns the size of the stack. int get_size() const; // Returns the slot data at the given index. Slot 0 is top of stack. StackSlotAnalysisData get_slotData(int slot); }; // Helper class to build internal exception messages for exceptions // that are thrown because prerequisites to execute a bytecode // are not met. // E.g., if a NPE is thrown because an iload can not be executed // by the VM because the reference to load from is null. // // It analyses the bytecode to assemble Java-like message text // to give precise information where in a larger expression the // exception occured. // // To assemble this message text, it is needed to know how // operand stack slot entries were pushed on the operand stack. // This class contains an analysis over the bytecodes to compute // this information. The information is stored in a // SimulatedOperandStack for each bytecode. class ExceptionMessageBuilder : public StackObj { // The stacks for each bytecode. GrowableArray* _stacks; // The method. Method* _method; // The number of entries used (the sum of all entries of all stacks). int _nr_of_entries; // If true, we have added at least one new stack. bool _added_one; // If true, we have processed all bytecodes. bool _all_processed; // The maximum number of entries we want to use. This is used to // limit the amount of memory we waste for insane methods (as they // appear in JCK tests). static const int _max_entries = 1000000; static const int _max_cause_detail = 5; // Merges the stack the the given bci with the given stack. If there // is no stack at the bci, we just put the given stack there. This // method doesn't takes ownership of the stack. void merge(int bci, SimulatedOperandStack* stack); // Processes the instruction at the given bci in the method. Returns // the size of the instruction. int do_instruction(int bci); bool print_NPE_cause0(outputStream *os, int bci, int slot, int max_detail, bool innerExpr = false, const char *prefix = NULL); public: // Creates an ExceptionMessageBuilder object and runs the analysis // building SimulatedOperandStacks for each bytecode in the given // method (the method must be rewritten already). Note that you're // not allowed to use this object when crossing a safepoint! If the // bci is != -1, we only create the stacks as far as needed to get a // stack for the bci. ExceptionMessageBuilder(Method* method, int bci = -1); // Releases the resources. ~ExceptionMessageBuilder(); // Returns the number of stacks (this is the size of the method). int get_size() { return _stacks->length() - 1; } // Assuming that a NullPointerException was thrown at the given bci, // we return the nr of the slot holding the null reference. If this // NPE is created by hand, we return -2 as the slot. If there // cannot be a NullPointerException at the bci, -1 is returned. int get_NPE_null_slot(int bci); // Prints a java-like expression for the bytecode that pushed // the value to the given slot being live at the given bci. // It constructs the expression by recursing backwards over the // bytecode using the results of the analysis done in the // constructor of ExceptionMessageBuilder. // os: The stream to print the message to. // bci: The index of the bytecode that caused the NPE. // slot: The slot on the operand stack that contains null. // The slots are numbered from TOS downwards, i.e., // TOS has the slot number 0, that below 1 and so on. // // Returns false if nothing was printed, else true. bool print_NPE_cause(outputStream *os, int bci, int slot); // Prints a string describing the failed action. void print_NPE_failed_action(outputStream *os, int bci); }; // Prints the name of the method that is described at constant pool // index cp_index in the constant pool of method 'method'. static void print_method_name(outputStream *os, Method* method, int cp_index) { ConstantPool* cp = method->constants(); Symbol* klass = cp->klass_ref_at_noresolve(cp_index); Symbol* name = cp->name_ref_at(cp_index); Symbol* signature = cp->signature_ref_at(cp_index); os->print("%s.%s(", klass->as_klass_external_name(), name->as_C_string()); signature->print_as_signature_external_parameters(os); os->print(")"); } // Prints the name of the field that is described at constant pool // index cp_index in the constant pool of method 'method'. static void print_field_and_class(outputStream *os, Method* method, int cp_index) { ConstantPool* cp = method->constants(); Symbol* klass = cp->klass_ref_at_noresolve(cp_index); Symbol *name = cp->name_ref_at(cp_index); os->print("%s.%s", klass->as_klass_external_name(), name->as_C_string()); } // Returns the name of the field that is described at constant pool // index cp_index in the constant pool of method 'method'. static char const* get_field_name(Method* method, int cp_index) { Symbol* name = method->constants()->name_ref_at(cp_index); return name->as_C_string(); } static void print_local_var(outputStream *os, unsigned int bci, Method* method, int slot) { if (method->has_localvariable_table()) { for (int i = 0; i < method->localvariable_table_length(); i++) { LocalVariableTableElement* elem = method->localvariable_table_start() + i; unsigned int start = elem->start_bci; unsigned int end = start + elem->length; if ((bci >= start) && (bci < end) && (elem->slot == slot)) { ConstantPool* cp = method->constants(); char *var = cp->symbol_at(elem->name_cp_index)->as_C_string(); os->print("%s", var); return; } } } // Handle at least some cases we know. if (!method->is_static() && (slot == 0)) { os->print("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) { os->print("", 1 + param_index); } else { // This is the best we can do. os->print("", slot); } } } StackSlotAnalysisData::StackSlotAnalysisData(BasicType type) : _bci(INVALID), _type(type) {} StackSlotAnalysisData::StackSlotAnalysisData(int bci, BasicType type) : _bci(bci), _type(type) { assert(bci >= 0, "BCI must be >= 0"); assert(bci < 65536, "BCI must be < 65536"); } unsigned int StackSlotAnalysisData::get_bci() { return _bci; } BasicType StackSlotAnalysisData::get_type() { return (BasicType)_type; } StackSlotAnalysisData StackSlotAnalysisData::merge(StackSlotAnalysisData 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 StackSlotAnalysisData(get_bci(), T_OBJECT); } else { return StackSlotAnalysisData(T_OBJECT); } } else { return StackSlotAnalysisData(T_CONFLICT); } } if (get_bci() == other.get_bci()) { return *this; } else { return StackSlotAnalysisData(get_type()); } } SimulatedOperandStack::SimulatedOperandStack(const SimulatedOperandStack ©) { for (int i = 0; i < copy.get_size(); i++) { push_raw(copy._stack.at(i)); } } void SimulatedOperandStack::push_raw(StackSlotAnalysisData slotData) { if (slotData.get_type() == T_VOID) { return; } _stack.push(slotData); } void SimulatedOperandStack::push(StackSlotAnalysisData slotData) { if (type2size[slotData.get_type()] == 2) { push_raw(slotData); push_raw(slotData); } else { push_raw(slotData); } } void SimulatedOperandStack::push(int bci, BasicType type) { push(StackSlotAnalysisData(bci, type)); } void SimulatedOperandStack::pop(int slots) { for (int i = 0; i < slots; ++i) { _stack.pop(); } assert(get_size() >= 0, "Popped too many slots"); } void SimulatedOperandStack::merge(SimulatedOperandStack const& other) { assert(get_size() == other.get_size(), "Stacks not of same size"); for (int i = get_size() - 1; i >= 0; --i) { _stack.at_put(i, _stack.at(i).merge(other._stack.at(i))); } } int SimulatedOperandStack::get_size() const { return _stack.length(); } StackSlotAnalysisData SimulatedOperandStack::get_slotData(int slot) { assert(slot >= 0, "Slot < 0"); assert(slot < get_size(), "Slot >= size"); return _stack.at(get_size() - slot - 1); } ExceptionMessageBuilder::ExceptionMessageBuilder(Method* method, int bci) : _method(method), _nr_of_entries(0), _added_one(true), _all_processed(false) { assert(bci >= 0, "BCI too low"); assert(bci < get_size(), "BCI too large"); ConstMethod* const_method = method->constMethod(); const int len = const_method->code_size(); _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 SimulatedOperandStack()); // And initialize the start of 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 SimulatedOperandStack()); _stacks->at(index)->push(index, T_OBJECT); } } } // Do this until each bytecode has 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; ) { // Analyse bytecode i. Step by size of the analyzed bytecode to next bytecode. i += do_instruction(i); // If we want the data only for a certain bci, we can possibly end early. if ((bci == i) && (_stacks->at(i) != NULL)) { _all_processed = true; break; } if (_nr_of_entries > _max_entries) { return; } } } } ExceptionMessageBuilder::~ExceptionMessageBuilder() { if (_stacks != NULL) { for (int i = 0; i < _stacks->length(); ++i) { delete _stacks->at(i); } } } void ExceptionMessageBuilder::merge(int bci, SimulatedOperandStack* 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(); } // Replace the stack at this bci with a copy of our new merged stack. delete _stacks->at(bci); _stacks->at_put(bci, new SimulatedOperandStack(*stack)); } int ExceptionMessageBuilder::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; } // Make a local copy of the stack for this bci to work on. SimulatedOperandStack* stack = new SimulatedOperandStack(*_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_slotData(0)); break; case Bytecodes::_dup_x1: { StackSlotAnalysisData top1 = stack->get_slotData(0); StackSlotAnalysisData top2 = stack->get_slotData(1); stack->pop(2); stack->push_raw(top1); stack->push_raw(top2); stack->push_raw(top1); break; } case Bytecodes::_dup_x2: { StackSlotAnalysisData top1 = stack->get_slotData(0); StackSlotAnalysisData top2 = stack->get_slotData(1); StackSlotAnalysisData top3 = stack->get_slotData(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_slotData(1)); stack->push_raw(stack->get_slotData(1)); break; case Bytecodes::_dup2_x1: { StackSlotAnalysisData top1 = stack->get_slotData(0); StackSlotAnalysisData top2 = stack->get_slotData(1); StackSlotAnalysisData top3 = stack->get_slotData(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: { StackSlotAnalysisData top1 = stack->get_slotData(0); StackSlotAnalysisData top2 = stack->get_slotData(1); StackSlotAnalysisData top3 = stack->get_slotData(2); StackSlotAnalysisData top4 = stack->get_slotData(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: { StackSlotAnalysisData top1 = stack->get_slotData(0); StackSlotAnalysisData top2 = stack->get_slotData(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: { // Find out the type of the field accessed. 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); // Simulate the bytecode: pop the address, push the 'value' loaded // from the field. 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 it 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; } int ExceptionMessageBuilder::get_NPE_null_slot(int bci) { // 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; // Position of argument of the bytecode. if (code == Bytecodes::_wide) { code = Bytecodes::java_code_at(_method, code_base + bci + 1); pos += 1; } switch (code) { case Bytecodes::_getfield: case Bytecodes::_arraylength: case Bytecodes::_athrow: case Bytecodes::_monitorenter: case Bytecodes::_monitorexit: return 0; case Bytecodes::_iaload: case Bytecodes::_faload: case Bytecodes::_aaload: case Bytecodes::_baload: case Bytecodes::_caload: case Bytecodes::_saload: case Bytecodes::_laload: case Bytecodes::_daload: return 1; case Bytecodes::_iastore: case Bytecodes::_fastore: case Bytecodes::_aastore: case Bytecodes::_bastore: case Bytecodes::_castore: case Bytecodes::_sastore: return 2; case Bytecodes::_lastore: case Bytecodes::_dastore: return 3; 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); return type2size[char2type((char) signature->char_at(0))]; } 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); Symbol* name = cp->symbol_at(name_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 explicitly by new in Java code. if (name != vmSymbols::object_initializer_name()) { int type_index = cp->signature_ref_index_at(name_and_type_index); Symbol* signature = cp->symbol_at(type_index); // The 'this' parameter was null. Return the slot of it. return ArgumentSizeComputer(signature).size(); } else { return -2; } } default: break; } return -1; } bool ExceptionMessageBuilder::print_NPE_cause(outputStream* os, int bci, int slot) { if (print_NPE_cause0(os, bci, slot, _max_cause_detail, false, " because '")) { os->print("' is null."); return true; } return false; } // Recursively print what was null. // // Go to the bytecode that pushed slot 'slot' on the operand stack // at bytecode 'bci'. Compute a message for that bytecode. If // necessary (array, field), recur further. // At most do max_detail recursions. // Prefix is used to print a proper beginning of the whole // sentence. // innerExpr is used to omit some text, like 'static' in // inner expressions like array subscripts. // // Returns true if something was printed. // bool ExceptionMessageBuilder::print_NPE_cause0(outputStream* os, int bci, int slot, int max_detail, bool innerExpr, const char *prefix) { assert(bci >= 0, "BCI too low"); assert(bci < get_size(), "BCI too large"); if (max_detail <= 0) { return false; } if (_stacks->at(bci) == NULL) { return false; } SimulatedOperandStack* stack = _stacks->at(bci); assert(slot >= 0, "Slot nr. too low"); assert(slot < stack->get_size(), "Slot nr. too large"); StackSlotAnalysisData slotData = stack->get_slotData(slot); if (!slotData.has_bci()) { return false; } // Get the bytecode. unsigned int source_bci = slotData.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; } if (max_detail == _max_cause_detail && prefix != NULL && code != Bytecodes::_invokevirtual && code != Bytecodes::_invokespecial && code != Bytecodes::_invokestatic && code != Bytecodes::_invokeinterface) { os->print("%s", prefix); } switch (code) { case Bytecodes::_iload_0: case Bytecodes::_aload_0: print_local_var(os, source_bci, _method, 0); return true; case Bytecodes::_iload_1: case Bytecodes::_aload_1: print_local_var(os, source_bci, _method, 1); return true; case Bytecodes::_iload_2: case Bytecodes::_aload_2: print_local_var(os, source_bci, _method, 2); return true; case Bytecodes::_iload_3: case Bytecodes::_aload_3: print_local_var(os, source_bci, _method, 3); return true; case Bytecodes::_iload: 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); } print_local_var(os, source_bci, _method, index); return true; } case Bytecodes::_aconst_null: os->print("null"); return true; case Bytecodes::_iconst_m1: os->print("-1"); return true; case Bytecodes::_iconst_0: os->print("0"); return true; case Bytecodes::_iconst_1: os->print("1"); return true; case Bytecodes::_iconst_2: os->print("2"); return true; case Bytecodes::_iconst_3: os->print("3"); return true; case Bytecodes::_iconst_4: os->print("4"); return true; case Bytecodes::_iconst_5: os->print("5"); return true; case Bytecodes::_bipush: { jbyte con = *(jbyte*) (code_base + source_bci + 1); os->print("%d", con); return true; } case Bytecodes::_sipush: { u2 con = Bytes::get_Java_u2(code_base + source_bci + 1); os->print("%d", con); return true; } case Bytecodes::_iaload: case Bytecodes::_aaload: { // Print the 'name' of the array. Go back to the bytecode that // pushed the array reference on the operand stack. if (!print_NPE_cause0(os, source_bci, 1, max_detail-1, innerExpr)) { // Returned false. Max recursion depth was reached. Print dummy. os->print(""); } os->print("["); // Print the index expression. Go back to the bytecode that // pushed the index on the operand stack. // innerExpr == true so we don't print unwanted strings // as "The return value of'". And don't decrement max_detail so we always // get a value here and only cancel out on the dereference. if (!print_NPE_cause0(os, source_bci, 0, max_detail, true)) { // Returned false. We don't print complex array index expressions. Print placeholder. os->print("..."); } os->print("]"); return true; } case Bytecodes::_getstatic: { int cp_index = Bytes::get_native_u2(code_base + pos) + ConstantPool::CPCACHE_INDEX_TAG; print_field_and_class(os, _method, cp_index); return true; } case Bytecodes::_getfield: { // Print the sender. Go back to the bytecode that // pushed the sender on the operand stack. if (print_NPE_cause0(os, source_bci, 0, max_detail - 1, innerExpr)) { os->print("."); } int cp_index = Bytes::get_native_u2(code_base + pos) + ConstantPool::CPCACHE_INDEX_TAG; os->print("%s", get_field_name(_method, cp_index)); return true; } 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); if (max_detail == _max_cause_detail && !innerExpr) { os->print(" because the return value of '"); } print_method_name(os, _method, cp_index); return true; } default: break; } return false; } void ExceptionMessageBuilder::print_NPE_failed_action(outputStream *os, int bci) { // If this NPE was created via reflection, we have no real NPE. assert(_method->method_holder() != SystemDictionary::reflect_NativeConstructorAccessorImpl_klass(), "We should have checked for reflection in get_NPE_null_slot()."); // 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; } switch (code) { case Bytecodes::_iaload: os->print("Cannot load from int array"); break; case Bytecodes::_faload: os->print("Cannot load from float array"); break; case Bytecodes::_aaload: os->print("Cannot load from object array"); break; case Bytecodes::_baload: os->print("Cannot load from byte/boolean array"); break; case Bytecodes::_caload: os->print("Cannot load from char array"); break; case Bytecodes::_saload: os->print("Cannot load from short array"); break; case Bytecodes::_laload: os->print("Cannot load from long array"); break; case Bytecodes::_daload: os->print("Cannot load from double array"); break; case Bytecodes::_iastore: os->print("Cannot store to int array"); break; case Bytecodes::_fastore: os->print("Cannot store to float array"); break; case Bytecodes::_aastore: os->print("Cannot store to object array"); break; case Bytecodes::_bastore: os->print("Cannot store to byte/boolean array"); break; case Bytecodes::_castore: os->print("Cannot store to char array"); break; case Bytecodes::_sastore: os->print("Cannot store to short array"); break; case Bytecodes::_lastore: os->print("Cannot store to long array"); break; case Bytecodes::_dastore: os->print("Cannot store to double array"); break; case Bytecodes::_arraylength: os->print("Cannot read the array length"); break; case Bytecodes::_athrow: os->print("Cannot throw exception"); break; case Bytecodes::_monitorenter: os->print("Cannot enter synchronized block"); break; case Bytecodes::_monitorexit: os->print("Cannot exit synchronized block"); break; 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 name_index = cp->name_ref_index_at(name_and_type_index); Symbol* name = cp->symbol_at(name_index); os->print("Cannot read field '%s'", name->as_C_string()); } break; case Bytecodes::_putfield: { int cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); os->print("Cannot assign field '%s'", get_field_name(_method, cp_index)); } 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); os->print("Cannot invoke '"); print_method_name(os, _method, cp_index); os->print("'"); } break; default: assert(0, "We should have checked this bytecode in get_NPE_null_slot()."); break; } } // Main API bool BytecodeUtils::get_NPE_message_at(outputStream* ss, Method* method, int bci) { NoSafepointVerifier _nsv; // Cannot use this object over a safepoint. // If this NPE was created via reflection, we have no real NPE. if (method->method_holder() == SystemDictionary::reflect_NativeConstructorAccessorImpl_klass()) { return false; } // Analyse the bytecodes. ResourceMark rm; ExceptionMessageBuilder emb(method, bci); // The slot of the operand stack that contains the null reference. // Also checks for NPE explicitly constructed and returns -2. int slot = emb.get_NPE_null_slot(bci); // Build the message. if (slot == -2) { // We don't want to print a message. return false; } else if (slot == -1) { // We encountered a bytecode that does not dereference a reference. DEBUG_ONLY(ss->print("There cannot be a NullPointerException at bci %d of method %s", bci, method->external_name())); NOT_DEBUG(return false); } else { // Print string describing which action (bytecode) could not be // performed because of the null reference. emb.print_NPE_failed_action(ss, bci); // Print a description of what is null. if (!emb.print_NPE_cause(ss, bci, slot)) { // Nothing was printed. End the sentence without the 'because' // subordinate sentence. ss->print("."); } } return true; }