8218628: Add detailed message to NullPointerException describing what is null.
--- a/make/hotspot/symbols/symbols-unix Tue Mar 19 17:03:18 2019 +0800
+++ b/make/hotspot/symbols/symbols-unix Fri Feb 08 14:15:05 2019 +0100
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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
@@ -97,6 +97,7 @@
JVM_GetDeclaredClasses
JVM_GetDeclaringClass
JVM_GetEnclosingMethodInfo
+JVM_GetExtendedNPEMessage
JVM_GetFieldIxModifiers
JVM_GetFieldTypeAnnotations
JVM_GetInheritedAccessControlContext
--- a/src/hotspot/share/classfile/javaClasses.cpp Tue Mar 19 17:03:18 2019 +0800
+++ b/src/hotspot/share/classfile/javaClasses.cpp Fri Feb 08 14:15:05 2019 +0100
@@ -1516,7 +1516,7 @@
int java_lang_Class::classRedefinedCount_offset = -1;
#define CLASS_FIELDS_DO(macro) \
- macro(classRedefinedCount_offset, k, "classRedefinedCount", int_signature, false) ; \
+ macro(classRedefinedCount_offset, k, "classRedefinedCount", int_signature, false); \
macro(_class_loader_offset, k, "classLoader", classloader_signature, false); \
macro(_component_mirror_offset, k, "componentType", class_signature, false); \
macro(_module_offset, k, "module", module_signature, false); \
@@ -1961,6 +1961,38 @@
return method != NULL && (method->constants()->version() == version);
}
+bool java_lang_Throwable::get_method_and_bci(oop throwable, Method** method, int* bci) {
+ objArrayOop bt = (objArrayOop)backtrace(throwable);
+
+ if (bt == NULL) {
+ return false;
+ }
+
+ oop ms = bt->obj_at(trace_methods_offset);
+ typeArrayOop methods = typeArrayOop(ms);
+ typeArrayOop bcis = (typeArrayOop)bt->obj_at(trace_bcis_offset);
+ objArrayOop mirrors = (objArrayOop)bt->obj_at(trace_mirrors_offset);
+ if ((methods == NULL) || (bcis == NULL) || (mirrors == NULL)) {
+ return false;
+ }
+
+ oop m = mirrors->obj_at(0);
+ if (m == NULL) {
+ return false;
+ }
+
+ InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(m));
+ int method_id = methods->short_at(0);
+ Method* act_method = holder->method_with_idnum(method_id);
+ if (act_method == NULL) {
+ return false;
+ }
+
+ int act_bci = Backtrace::bci_at(bcis->int_at(0));
+ *method = act_method;
+ *bci = act_bci;
+ return true;
+}
// This class provides a simple wrapper over the internal structure of
// exception backtrace to insulate users of the backtrace from needing
--- a/src/hotspot/share/classfile/javaClasses.hpp Tue Mar 19 17:03:18 2019 +0800
+++ b/src/hotspot/share/classfile/javaClasses.hpp Fri Feb 08 14:15:05 2019 +0100
@@ -572,6 +572,8 @@
static void java_printStackTrace(Handle throwable, TRAPS);
// Debugging
friend class JavaClasses;
+ // Gets the method and bci of the top frame (TOS). Returns false if this failed.
+ static bool get_method_and_bci(oop throwable, Method** method, int* bci);
};
--- a/src/hotspot/share/classfile/systemDictionary.hpp Tue Mar 19 17:03:18 2019 +0800
+++ b/src/hotspot/share/classfile/systemDictionary.hpp Fri Feb 08 14:15:05 2019 +0100
@@ -155,6 +155,7 @@
do_klass(reflect_ConstantPool_klass, reflect_ConstantPool ) \
do_klass(reflect_UnsafeStaticFieldAccessorImpl_klass, reflect_UnsafeStaticFieldAccessorImpl ) \
do_klass(reflect_CallerSensitive_klass, reflect_CallerSensitive ) \
+ do_klass(reflect_NativeConstructorAccessorImpl_klass, reflect_NativeConstructorAccessorImpl ) \
\
/* support for dynamic typing; it's OK if these are NULL in earlier JDKs */ \
do_klass(DirectMethodHandle_klass, java_lang_invoke_DirectMethodHandle ) \
--- a/src/hotspot/share/classfile/vmSymbols.hpp Tue Mar 19 17:03:18 2019 +0800
+++ b/src/hotspot/share/classfile/vmSymbols.hpp Fri Feb 08 14:15:05 2019 +0100
@@ -243,6 +243,7 @@
template(reflect_Reflection, "jdk/internal/reflect/Reflection") \
template(reflect_CallerSensitive, "jdk/internal/reflect/CallerSensitive") \
template(reflect_CallerSensitive_signature, "Ljdk/internal/reflect/CallerSensitive;") \
+ template(reflect_NativeConstructorAccessorImpl, "jdk/internal/reflect/NativeConstructorAccessorImpl")\
template(checkedExceptions_name, "checkedExceptions") \
template(clazz_name, "clazz") \
template(exceptionTypes_name, "exceptionTypes") \
--- a/src/hotspot/share/include/jvm.h Tue Mar 19 17:03:18 2019 +0800
+++ b/src/hotspot/share/include/jvm.h Fri Feb 08 14:15:05 2019 +0100
@@ -192,6 +192,13 @@
JVM_InitStackTraceElement(JNIEnv* env, jobject element, jobject stackFrameInfo);
/*
+ * java.lang.NullPointerException
+ */
+
+JNIEXPORT jstring JNICALL
+JVM_GetExtendedNPEMessage(JNIEnv *env, jthrowable throwable);
+
+/*
* java.lang.StackWalker
*/
enum {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/interpreter/bytecodeUtils.cpp Fri Feb 08 14:15:05 2019 +0100
@@ -0,0 +1,1317 @@
+/*
+ * 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 "utilities/events.hpp"
+
+/*
+ * Returns the name of the klass that is described at constant pool
+ * index cp_index in the constant pool of method 'method'.
+ */
+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* klass = cp->klass_at_noresolve(class_index);
+
+ return klass->as_klass_external_name();
+}
+
+/*
+ * Returns the name of the method that is described at constant pool
+ * index cp_index in the constant pool of method 'method'.
+ */
+char const* MethodBytecodePrinter::get_method_name(Method* method, int cp_index) {
+ ConstantPool* cp = method->constants();
+ int class_index = cp->klass_ref_index_at(cp_index);
+ Symbol* klass = cp->klass_at_noresolve(class_index);
+
+ 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", klass->as_klass_external_name(), name->as_C_string(), signature->as_C_string());
+ return ss.as_string();
+}
+
+/*
+ * Returns the name of the field that is described at constant pool
+ * index cp_index in the constant pool of method 'method'.
+ */
+char const* MethodBytecodePrinter::get_field_and_class(Method* method, int cp_index) {
+ ConstantPool* cp = method->constants();
+ int class_index = cp->klass_ref_index_at(cp_index);
+ Symbol* klass = cp->klass_at_noresolve(class_index);
+
+ 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", klass->as_klass_external_name(), name->as_C_string());
+ return ss.as_string();
+}
+
+/*
+ * Returns the name of the field that is described at constant pool
+ * index cp_index in the constant pool of method 'method'.
+ */
+char const* MethodBytecodePrinter::get_field_name(Method* method, int 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);
+ return name->as_C_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_put(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();
+ char *var = cp->symbol_at(elem->name_cp_index)->as_C_string();
+ if (strlen(var) == 4 && strcmp(var, "this") == 0) {
+ reason.print("loaded from 'this'");
+ } else {
+ reason.print("loaded from local variable '%s'", var);
+ }
+
+ 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 '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) {
+ 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<TrackingStack*> (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 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 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; ) {
+ // 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;
+ }
+ }
+ }
+}
+
+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<int> 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: {
+ // 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 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 explicitly 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;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/interpreter/bytecodeUtils.hpp Fri Feb 08 14:15:05 2019 +0100
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_CLASSFILE_BYTECODEUTILS_HPP
+#define SHARE_CLASSFILE_BYTECODEUTILS_HPP
+
+#include "memory/allocation.hpp"
+#include "oops/method.hpp"
+#include "utilities/growableArray.hpp"
+
+
+// A class which can be used to print out the bytecode
+// of a method.
+// NOTE: The method must already be rewritten.
+class MethodBytecodePrinter : public AllStatic {
+ public:
+
+ // Returns the external (using '.') name of the class at the given cp index in an
+ // area allocated string.
+ static char const* get_klass_name(Method* method, int cp_index);
+
+ // Returns the name of the method(including signature, but without
+ // the return type) at the given cp index in a resource area
+ // allocated string.
+ static char const* get_method_name(Method* method, int cp_index);
+
+ // Returns the name of the field at the given cp index in
+ // a resource area allocated string.
+ static char const *get_field_name(Method* method, int cp_index);
+
+ // Returns the name and class of the field at the given cp index in
+ // a resource area allocated string.
+ static char const* get_field_and_class(Method* method, int cp_index);
+};
+
+class TrackingStack;
+class TrackingStackCreator;
+
+// The entry of TrackingStack.
+class TrackingStackEntry {
+ private:
+
+ friend class TrackingStack;
+ friend class TrackingStackCreator;
+
+ // The raw entry composed of the type and the bci.
+ int _entry;
+
+ enum {
+ SCALE = 1024 * 1024
+ };
+
+ // Merges this entry with the given one and returns the result. If
+ // the bcis of the entry 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).
+ TrackingStackEntry merge(TrackingStackEntry other);
+public:
+ // Creates a new entry with an invalid bci and the given type.
+ TrackingStackEntry(BasicType type = T_CONFLICT);
+
+ // Creates a new entry with the given bci and type.
+ TrackingStackEntry(int bci, BasicType type);
+
+ public:
+
+ enum {
+ INVALID = SCALE - 1
+ };
+
+ // Returns the bci. If the bci is invalid, INVALID is returned.
+ int get_bci();
+
+ // Returns true, if the bci is not invalid.
+ bool has_bci() { return get_bci() != INVALID; }
+
+ // Returns the type of the entry.
+ BasicType get_type();
+};
+
+// A stack consisting of TrackingStackEntries.
+class TrackingStack: CHeapObj<mtInternal> {
+
+ private:
+
+ friend class TrackingStackCreator;
+ friend class TrackingStackEntry;
+
+ // The stack.
+ GrowableArray<TrackingStackEntry> _stack;
+
+ TrackingStack() { };
+ TrackingStack(const TrackingStack ©);
+
+ // Pushes the given entry.
+ void push_raw(TrackingStackEntry entry);
+
+ // Like push_raw, but if the entry is long or double, we push two.
+ void push(TrackingStackEntry entry);
+
+ // Like push(entry), but using bci/type instead of entry.
+ 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(TrackingStack const& other);
+
+ public:
+
+ // Returns the size of the stack.
+ int get_size() const;
+
+ // Returns the entry with the given index. Top of stack is at index 0.
+ TrackingStackEntry get_entry(int slot);
+};
+
+// Defines a source of a slot of the operand stack.
+class TrackingStackSource {
+
+ public:
+
+ enum Type {
+ // If the value was loaded from a local variable.
+ LOCAL_VAR,
+
+ // If the value was returned from a method.
+ METHOD,
+
+ // If the value was loaded from an array.
+ ARRAY_ELEM,
+
+ // If the value was loaded from a field.
+ FIELD_ELEM,
+
+ // If the value was from a constant.
+ CONSTANT,
+
+ // If the source is invalid.
+ INVALID
+ };
+
+ private:
+
+ const char *_reason;
+
+ Type _type;
+ int _bci;
+
+ public:
+
+ TrackingStackSource(Type type, int bci, const char * reason) : _reason(reason), _type(type), _bci(bci) { }
+
+ // Returns the type.
+ Type get_type() const {
+ return _type;
+ }
+
+ // Returns a human readable string describing the source.
+ char const* as_string() const {
+ return _reason;
+ }
+};
+
+// Analyses the bytecodes of a method and tries to create a tracking
+// stack for each bci. The tracking stack holds the bci and type of
+// the objec on the stack. The bci (if valid) holds the bci of the
+// instruction, which put the entry on the stack.
+class TrackingStackCreator {
+
+ // The stacks.
+ GrowableArray<TrackingStack*>* _stacks;
+
+ // The method.
+ Method* _method;
+
+ // 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).
+ int _max_entries;
+
+ // 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;
+
+ // 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, TrackingStack* stack);
+
+ // Processes the instruction at the given bci in the method. Returns
+ // the size of the instruction.
+ int do_instruction(int bci);
+
+ public:
+
+ // Creates tracking stacks for 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.
+ TrackingStackCreator(Method* method, int bci = -1);
+
+ // Releases the resources.
+ ~TrackingStackCreator();
+
+ // Returns the number of stacks (this is the size of the method).
+ int get_size() { return _stacks->length() - 1; }
+
+ // Returns the source of the value in the given slot at the given bci.
+ // The TOS has the slot number 0, that below 1 and so on. You have to
+ // delete the returned object via 'delete'. 'max_detail' is the number
+ // of levels for which we include sources recursively (e.g. for a source
+ // which was from an array and the array was loaded from field of an
+ // object which ...). The larger the value, the more detailed the source.
+ TrackingStackSource get_source(int bci, int slot, int max_detail);
+
+ // 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. If
+ // 'reason' is != NULL, a description is stored, which is allocated
+ // statically or in the resource area.
+ int get_null_pointer_slot(int bci, char const** reason);
+
+};
+
+#endif // SHARE_CLASSFILE_BYTECODEUTILS_HPP
--- a/src/hotspot/share/prims/jvm.cpp Tue Mar 19 17:03:18 2019 +0800
+++ b/src/hotspot/share/prims/jvm.cpp Fri Feb 08 14:15:05 2019 +0100
@@ -37,6 +37,7 @@
#include "classfile/vmSymbols.hpp"
#include "gc/shared/collectedHeap.inline.hpp"
#include "interpreter/bytecode.hpp"
+#include "interpreter/bytecodeUtils.hpp"
#include "jfr/jfrEvents.hpp"
#include "logging/log.hpp"
#include "memory/heapShared.hpp"
@@ -534,13 +535,52 @@
// java.lang.Throwable //////////////////////////////////////////////////////
-
JVM_ENTRY(void, JVM_FillInStackTrace(JNIEnv *env, jobject receiver))
JVMWrapper("JVM_FillInStackTrace");
Handle exception(thread, JNIHandles::resolve_non_null(receiver));
java_lang_Throwable::fill_in_stack_trace(exception);
JVM_END
+// java.lang.NullPointerException ///////////////////////////////////////////
+
+JVM_ENTRY(jstring, JVM_GetExtendedNPEMessage(JNIEnv *env, jthrowable throwable))
+ oop exc = JNIHandles::resolve_non_null(throwable);
+
+ Method* method;
+ int bci;
+ if (!java_lang_Throwable::get_method_and_bci(exc, &method, &bci)) {
+ return NULL;
+ }
+ if (method->is_native()) {
+ return NULL;
+ }
+
+ ResourceMark rm(THREAD);
+ TrackingStackCreator stc(method, bci);
+ char const* reason;
+ int slot = stc.get_null_pointer_slot(bci, &reason);
+
+ // Build the message.
+ stringStream ss;
+ if (slot == -2) {
+ return NULL;
+ } else if (slot == -1) {
+ ss.print("There cannot be a NullPointerException at bci %d of method %s",
+ bci, method->name_and_sig_as_C_string());
+ } else if (reason == NULL) {
+ ss.print("Cannot get the reason for the NullPointerException at bci %d of method %s",
+ bci, method->name_and_sig_as_C_string());
+ } else {
+ TrackingStackSource source = stc.get_source(bci, slot, 2);
+ ss.print("%s", reason);
+ if (source.get_type() != TrackingStackSource::INVALID) {
+ ss.print(" %s", source.as_string());
+ }
+ }
+
+ oop result = java_lang_String::create_oop_from_str(ss.as_string(), CHECK_0);
+ return (jstring) JNIHandles::make_local(env, result);
+JVM_END
// java.lang.StackTraceElement //////////////////////////////////////////////
--- a/src/java.base/share/classes/java/lang/NullPointerException.java Tue Mar 19 17:03:18 2019 +0800
+++ b/src/java.base/share/classes/java/lang/NullPointerException.java Fri Feb 08 14:15:05 2019 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, Oracle and/or its affiliates. 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
@@ -69,4 +69,29 @@
public NullPointerException(String s) {
super(s);
}
+
+ /**
+ * Returns the detail message string of this throwable.
+ *
+ * If no explicit message was passed to the constructor, and as
+ * long as certain internal information is available, a verbose
+ * description of the null entity is returned. After releasing the
+ * internal information, e.g., after serialization, null will be
+ * returned in this case.
+ *
+ * @return the detail message string of this {@code NullPointerException} instance
+ * (which may be {@code null}).
+ */
+ public String getMessage() {
+ String message = super.getMessage();
+ if (message == null) {
+ return getExtendedNPEMessage();
+ }
+ return message;
+ }
+
+ // Get an extended exception message. This returns a string describing
+ // the location and cause of the exception. It returns null for
+ // exceptions where this is not applicable.
+ private native String getExtendedNPEMessage();
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/native/libjava/NullPointerException.c Fri Feb 08 14:15:05 2019 +0100
@@ -0,0 +1,36 @@
+/*
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 "jni.h"
+#include "jvm.h"
+
+#include "java_lang_NullPointerException.h"
+
+JNIEXPORT jstring JNICALL
+Java_java_lang_NullPointerException_getExtendedNPEMessage(JNIEnv *env, jobject throwable)
+{
+ return JVM_GetExtendedNPEMessage(env, throwable);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java Fri Feb 08 14:15:05 2019 +0100
@@ -0,0 +1,1236 @@
+/*
+ * 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.
+ */
+
+/**
+ * @test
+ * @summary Test extended NullPointerException message for class
+ * files generated without debugging information. The message lists
+ * detailed information about the entity that is null.
+ * @library /test/lib
+ * @compile NullPointerExceptionTest.java
+ * @run main NullPointerExceptionTest
+ */
+/**
+ * @test
+ * @summary Test extended NullPointerException message for
+ * classfiles generated with debug information. In this case the name
+ * of the variable containing the array is printed.
+ * @library /test/lib
+ * @compile -g NullPointerExceptionTest.java
+ * @run main/othervm -XX:+WizardMode -DhasDebugInfo NullPointerExceptionTest
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+
+import jdk.test.lib.Asserts;
+
+/**
+ * Tests NullPointerExceptions
+ */
+public class NullPointerExceptionTest {
+ // Some fields used in the test.
+ static Object nullStaticField;
+ NullPointerExceptionTest nullInstanceField;
+ static int[][][][] staticArray;
+ static long[][] staticLongArray = new long[1000][];
+ DoubleArrayGen dag;
+ ArrayList<String> names = new ArrayList<>();
+ ArrayList<String> curr;
+ static boolean hasDebugInfo = true;
+
+ static {
+ try {
+ hasDebugInfo = System.getProperty("hasDebugInfo") != null;
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+
+ staticArray = new int[1][][][];
+ staticArray[0] = new int[1][][];
+ staticArray[0][0] = new int[1][];
+ }
+
+ public static void checkMessage(String expression,
+ String obtainedMsg, String expectedMsg) {
+ System.out.println();
+ System.out.println(" source code: " + expression);
+ System.out.println(" thrown msg: " + obtainedMsg);
+ System.out.println("expected msg: " + expectedMsg);
+ Asserts.assertEquals(obtainedMsg, expectedMsg);
+ }
+
+ public static void main(String[] args) throws Exception {
+ NullPointerExceptionTest t = new NullPointerExceptionTest();
+ t.testPointerChasing();
+ t.testArrayChasing();
+ t.testMethodChasing();
+ t.testMixedChasing();
+ t.testSameMessage();
+ t.testCreationViaNew();
+ t.testCreationViaReflection();
+ t.testCreationViaSerialization();
+ t.testLoadedFromLocalVariable1();
+ t.testLoadedFromLocalVariable2();
+ t.testLoadedFromLocalVariable3();
+ t.testLoadedFromLocalVariable4();
+ t.testLoadedFromLocalVariable5();
+ t.testLoadedFromLocalVariable6();
+ t.testLoadedFromLocalVariable7();
+ t.testLoadedFromMethod1();
+ t.testLoadedFromMethod2();
+ t.testLoadedFromMethod3();
+ t.testLoadedFromMethod4();
+ t.testLoadedFromMethod5();
+ t.testLoadedFromMethod6();
+ t.testLoadedFromMethod7();
+ t.testLoadedFromStaticField1();
+ t.testLoadedFromStaticField2();
+ t.testLoadedFromStaticField3();
+ t.testLoadedFromStaticField4(0, 0);
+ t.testLoadedFromStaticField5();
+ t.testLoadedFromStaticField5a();
+ t.testLoadedFromStaticField5b();
+ t.testLoadedFromStaticField6();
+ t.testLoadedFromInstanceField1();
+ t.testLoadedFromInstanceField2();
+ t.testLoadedFromInstanceField3();
+ t.testLoadedFromInstanceField4();
+ t.testLoadedFromInstanceField5();
+ t.testLoadedFromInstanceField6();
+ t.testInNative();
+ t.testMissingLocalVariableTable();
+ t.testNullMessages();
+ }
+
+ class A {
+ public B to_b;
+ public B getB() { return to_b; }
+ }
+
+ class B {
+ public C to_c;
+ public B to_b;
+ public C getC() { return to_c; }
+ public B getBfromB() { return to_b; }
+ }
+
+ class C {
+ public D to_d;
+ public D getD() { return to_d; }
+ }
+
+ class D {
+ public int num;
+ public int[][] ar;
+ }
+
+ public void testPointerChasing() {
+ A a = null;
+ try {
+ a.to_b.to_c.to_d.num = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.to_b.to_c.to_d.num = 99 // a is null", e.getMessage(),
+ "while trying to read the field 'to_b' of a null object loaded from " +
+ (hasDebugInfo ? "local variable 'a'" : "a local variable at slot 1"));
+ }
+ a = new A();
+ try {
+ a.to_b.to_c.to_d.num = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.to_b.to_c.to_d.num = 99 // a.to_b is null", e.getMessage(),
+ "while trying to read the field 'to_c' of a null object loaded from field 'NullPointerExceptionTest$A.to_b' of an object loaded from " +
+ (hasDebugInfo ? "local variable 'a'" : "a local variable at slot 1"));
+ }
+ a.to_b = new B();
+ try {
+ a.to_b.to_c.to_d.num = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.to_b.to_c.to_d.num = 99 // a.to_b.to_c is null", e.getMessage(),
+ "while trying to read the field 'to_d' of a null object loaded from field 'NullPointerExceptionTest$B.to_c' of an object loaded from field 'NullPointerExceptionTest$A.to_b' of an object");
+ }
+ a.to_b.to_c = new C();
+ try {
+ a.to_b.to_c.to_d.num = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.to_b.to_c.to_d.num = 99 // a.to_b.to_c.to_d is null", e.getMessage(),
+ "while trying to write the field 'NullPointerExceptionTest$D.num' of a null object loaded from field 'NullPointerExceptionTest$C.to_d' of an object loaded from field 'NullPointerExceptionTest$B.to_c' of an object");
+ }
+ }
+
+ public void testArrayChasing() {
+ int[][][][][] a = null;
+ try {
+ a[0][0][0][0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("int[0][0][0][0][0] = 99 // a is null", e.getMessage(),
+ "while trying to load from a null object array loaded from " +
+ (hasDebugInfo ? "local variable 'a'" : "a local variable at slot 1"));
+ }
+ a = new int[1][][][][];
+ try {
+ a[0][0][0][0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("int[0][0][0][0][0] = 99 // a[0] is null", e.getMessage(),
+ "while trying to load from a null object array loaded from an array (which itself was loaded from " +
+ (hasDebugInfo ? "local variable 'a'" : "a local variable at slot 1") +
+ ") with an index loaded from a constant");
+ }
+ a[0] = new int[1][][][];
+ try {
+ a[0][0][0][0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("int[0][0][0][0][0] = 99 // a[0][0] is null", e.getMessage(),
+ "while trying to load from a null object array loaded from an array (which itself was loaded from an array) with an index loaded from a constant");
+ }
+ a[0][0] = new int[1][][];
+ try {
+ a[0][0][0][0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("int[0][0][0][0][0] = 99 // a[0][0][0] is null", e.getMessage(),
+ "while trying to load from a null object array loaded from an array (which itself was loaded from an array) with an index loaded from a constant");
+ }
+ a[0][0][0] = new int[1][];
+ try {
+ a[0][0][0][0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("int[0][0][0][0][0] = 99 // a[0][0][0][0] is null", e.getMessage(),
+ "while trying to store to a null int array loaded from an array (which itself was loaded from an array) with an index loaded from a constant");
+ }
+ a[0][0][0][0] = new int[1];
+ try {
+ a[0][0][0][0][0] = 99;
+ } catch (NullPointerException e) {
+ Asserts.fail();
+ }
+ }
+
+ public void testMethodChasing() {
+ A a = null;
+ try {
+ a.getB().getBfromB().getC().getD().num = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().getC().getD().num = 99 // a is null", e.getMessage(),
+ "while trying to invoke the method 'NullPointerExceptionTest$A.getB()LNullPointerExceptionTest$B;' on a null reference loaded from " +
+ (hasDebugInfo ? "local variable 'a'" : "a local variable at slot 1"));
+ }
+ a = new A();
+ try {
+ a.getB().getBfromB().getC().getD().num = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().getC().getD().num = 99 // a.getB() is null", e.getMessage(),
+ "while trying to invoke the method 'NullPointerExceptionTest$B.getBfromB()LNullPointerExceptionTest$B;' on a null reference returned from 'NullPointerExceptionTest$A.getB()LNullPointerExceptionTest$B;'");
+ }
+ a.to_b = new B();
+ try {
+ a.getB().getBfromB().getC().getD().num = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().getC().getD().num = 99 // a.getB().getBfromB() is null", e.getMessage(),
+ "while trying to invoke the method 'NullPointerExceptionTest$B.getC()LNullPointerExceptionTest$C;' on a null reference returned from 'NullPointerExceptionTest$B.getBfromB()LNullPointerExceptionTest$B;'");
+ }
+ a.to_b.to_b = new B();
+ try {
+ a.getB().getBfromB().getC().getD().num = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().getC().getD().num = 99 // a.getB().getBfromB().getC() is null", e.getMessage(),
+ "while trying to invoke the method 'NullPointerExceptionTest$C.getD()LNullPointerExceptionTest$D;' on a null reference returned from 'NullPointerExceptionTest$B.getC()LNullPointerExceptionTest$C;'");
+ }
+ a.to_b.to_b.to_c = new C();
+ try {
+ a.getB().getBfromB().getC().getD().num = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().getC().getD().num = 99 // a.getB().getBfromB().getC().getD() is null", e.getMessage(),
+ "while trying to write the field 'NullPointerExceptionTest$D.num' of a null object returned from 'NullPointerExceptionTest$C.getD()LNullPointerExceptionTest$D;'");
+ }
+ }
+
+ public void testMixedChasing() {
+ A a = null;
+ try {
+ a.getB().getBfromB().to_c.to_d.ar[0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a is null", e.getMessage(),
+ "while trying to invoke the method 'NullPointerExceptionTest$A.getB()LNullPointerExceptionTest$B;' on a null reference loaded from " +
+ (hasDebugInfo ? "local variable 'a'" : "a local variable at slot 1"));
+ }
+ a = new A();
+ try {
+ a.getB().getBfromB().to_c.to_d.ar[0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB() is null", e.getMessage(),
+ "while trying to invoke the method 'NullPointerExceptionTest$B.getBfromB()LNullPointerExceptionTest$B;' on a null reference returned from 'NullPointerExceptionTest$A.getB()LNullPointerExceptionTest$B;'");
+ }
+ a.to_b = new B();
+ try {
+ a.getB().getBfromB().to_c.to_d.ar[0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB().getBfromB() is null", e.getMessage(),
+ "while trying to read the field 'to_c' of a null object returned from 'NullPointerExceptionTest$B.getBfromB()LNullPointerExceptionTest$B;'");
+ }
+ a.to_b.to_b = new B();
+ try {
+ a.getB().getBfromB().to_c.to_d.ar[0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB().getBfromB().to_c is null", e.getMessage(),
+ "while trying to read the field 'to_d' of a null object loaded from field 'NullPointerExceptionTest$B.to_c' of an object returned from 'NullPointerExceptionTest$B.getBfromB()LNullPointerExceptionTest$B;'");
+ }
+ a.to_b.to_b.to_c = new C();
+ try {
+ a.getB().getBfromB().to_c.to_d.ar[0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB().getBfromB().to_c.to_d is null", e.getMessage(),
+ "while trying to read the field 'ar' of a null object loaded from field 'NullPointerExceptionTest$C.to_d' of an object loaded from field 'NullPointerExceptionTest$B.to_c' of an object");
+ }
+ a.to_b.to_b.to_c.to_d = new D();
+ try {
+ a.getB().getBfromB().to_c.to_d.ar[0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB().getBfromB().to_c.to_d.ar is null", e.getMessage(),
+ "while trying to load from a null object array loaded from field 'NullPointerExceptionTest$D.ar' of an object loaded from field 'NullPointerExceptionTest$C.to_d' of an object");
+ }
+ try {
+ a.getB().getBfromB().getC().getD().ar[0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().getC().getD().ar[0][0] = 99; // a.getB().getBfromB().getC().getD().ar is null", e.getMessage(),
+ "while trying to load from a null object array loaded from field 'NullPointerExceptionTest$D.ar' of an object returned from 'NullPointerExceptionTest$C.getD()LNullPointerExceptionTest$D;'");
+ }
+ a.to_b.to_b.to_c.to_d.ar = new int[1][];
+ try {
+ a.getB().getBfromB().to_c.to_d.ar[0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB().getBfromB().to_c.to_d.ar[0] is null", e.getMessage(),
+ "while trying to store to a null int array loaded from an array (which itself was loaded from field 'NullPointerExceptionTest$D.ar' of an object) with an index loaded from a constant");
+ }
+ try {
+ a.getB().getBfromB().getC().getD().ar[0][0] = 99;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("a.getB().getBfromB().getC().getD().ar[0][0] = 99; // a.getB().getBfromB().getC().getD().ar[0] is null", e.getMessage(),
+ "while trying to store to a null int array loaded from an array (which itself was loaded from field 'NullPointerExceptionTest$D.ar' of an object) with an index loaded from a constant");
+ }
+ }
+
+
+ // Test we get the same message calling npe.getMessage() twice.
+ public void testSameMessage() throws Exception {
+ Object null_o = null;
+ String expectedMsg =
+ "while trying to invoke the method 'java.lang.Object.hashCode()I'" +
+ " on a null reference loaded from " +
+ (hasDebugInfo ? "local variable 'null_o'" : "a local variable at slot 1");
+
+ try {
+ null_o.hashCode();
+ Asserts.fail();
+ } catch (NullPointerException npe) {
+ String msg1 = npe.getMessage();
+ checkMessage("null_o.hashCode()", msg1, expectedMsg);
+ String msg2 = npe.getMessage();
+ Asserts.assertTrue(msg1.equals(msg2));
+ // It was decided that getMessage should generate the
+ // message anew on every call, so this does not hold any more.
+ Asserts.assertFalse(msg1 == msg2);
+ }
+ }
+
+ /**
+ *
+ */
+ public void testCreationViaNew() {
+ Asserts.assertNull(new NullPointerException().getMessage());
+ }
+
+ /**
+ * @throws Exception
+ */
+ public void testCreationViaReflection() throws Exception {
+ Exception ex = NullPointerException.class.getDeclaredConstructor().newInstance();
+ Asserts.assertNull(ex.getMessage());
+ }
+
+ /**
+ * @throws Exception
+ */
+ public void testCreationViaSerialization() throws Exception {
+ // NPE without message.
+ Object o1 = new NullPointerException();
+ ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
+ ObjectOutputStream oos1 = new ObjectOutputStream(bos1);
+ oos1.writeObject(o1);
+ ByteArrayInputStream bis1 = new ByteArrayInputStream(bos1.toByteArray());
+ ObjectInputStream ois1 = new ObjectInputStream(bis1);
+ Exception ex1 = (Exception) ois1.readObject();
+ Asserts.assertNull(ex1.getMessage());
+
+ // NPE with custom message.
+ String msg2 = "A useless message";
+ Object o2 = new NullPointerException(msg2);
+ ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
+ ObjectOutputStream oos2 = new ObjectOutputStream(bos2);
+ oos2.writeObject(o2);
+ ByteArrayInputStream bis2 = new ByteArrayInputStream(bos2.toByteArray());
+ ObjectInputStream ois2 = new ObjectInputStream(bis2);
+ Exception ex2 = (Exception) ois2.readObject();
+ Asserts.assertEquals(ex2.getMessage(), msg2);
+
+ // NPE with generated message.
+ Object null_o3 = null;
+ Object o3 = null;
+ String msg3 = null;
+ try {
+ null_o3.hashCode();
+ Asserts.fail();
+ } catch (NullPointerException npe3) {
+ o3 = npe3;
+ msg3 = npe3.getMessage();
+ checkMessage("null_o3.hashCode()", msg3, "while trying to invoke the method 'java.lang.Object.hashCode()I'" +
+ " on a null reference loaded from " +
+ (hasDebugInfo ? "local variable 'null_o3'" : "a local variable at slot 14"));
+ }
+ ByteArrayOutputStream bos3 = new ByteArrayOutputStream();
+ ObjectOutputStream oos3 = new ObjectOutputStream(bos3);
+ oos3.writeObject(o3);
+ ByteArrayInputStream bis3 = new ByteArrayInputStream(bos3.toByteArray());
+ ObjectInputStream ois3 = new ObjectInputStream(bis3);
+ Exception ex3 = (Exception) ois3.readObject();
+ // It was decided that getMessage should not store the
+ // message in Throwable.detailMessage, thus it can not
+ // be recovered by serialization.
+ //Asserts.assertEquals(ex3.getMessage(), msg3);
+ Asserts.assertEquals(ex3.getMessage(), null);
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("null")
+ public void testLoadedFromLocalVariable1() {
+ Object o = null;
+
+ try {
+ o.hashCode();
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("o.hashCode()", e.getMessage(), "while trying to invoke the method 'java.lang.Object.hashCode()I' on a null reference loaded from " + (hasDebugInfo ? "local variable 'o'" : "a local variable at slot 1"));
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("null")
+ public void testLoadedFromLocalVariable2() {
+ Exception[] myVariable = null;
+
+ try {
+ Asserts.assertNull(myVariable[0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("myVariable[0]", e.getMessage(), "while trying to load from a null object array loaded from " + (hasDebugInfo ? "local variable 'myVariable'" : "a local variable at slot 1"));
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("null")
+ public void testLoadedFromLocalVariable3() {
+ Exception[] myVariable = null;
+
+ try {
+ myVariable[0] = null;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("myVariable[0] = null", e.getMessage(), "while trying to store to a null object array loaded from " + (hasDebugInfo ? "local variable 'myVariable'" : "a local variable at slot 1"));
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("null")
+ public void testLoadedFromLocalVariable4() {
+ Exception[] myVariable\u0096 = null;
+
+ try {
+ Asserts.assertTrue(myVariable\u0096.length == 0);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("myVariable\u0096.length", e.getMessage(), "while trying to get the length of a null array loaded from " + (hasDebugInfo ? "local variable 'myVariable'" : "a local variable at slot 1"));
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ @SuppressWarnings("null")
+ public void testLoadedFromLocalVariable5() throws Exception {
+ Exception myException = null;
+
+ try {
+ throw myException;
+ } catch (NullPointerException e) {
+ checkMessage("throw myException", e.getMessage(), "while trying to throw a null exception object loaded from " + (hasDebugInfo ? "local variable 'myException'" : "a local variable at slot 1"));
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("null")
+ public void testLoadedFromLocalVariable6() {
+ byte[] myVariable = null;
+ int my_index = 1;
+
+ try {
+ Asserts.assertTrue(myVariable[my_index] == 0);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("myVariable[my_index]", e.getMessage(), "while trying to load from a null byte (or boolean) array loaded from " + (hasDebugInfo ? "local variable 'myVariable'" : "a local variable at slot 1"));
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("null")
+ public void testLoadedFromLocalVariable7() {
+ byte[] myVariable = null;
+
+ try {
+ myVariable[System.out.hashCode()] = (byte) 0;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("myVariable[System.out.hashCode()]", e.getMessage(), "while trying to store to a null byte (or boolean) array loaded from " + (hasDebugInfo ? "local variable 'myVariable'" : "a local variable at slot 1"));
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromMethod1() {
+ try {
+ Asserts.assertTrue(((char[]) NullPointerGenerator.nullReturner(false))[0] == 'A');
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("((char[]) NullPointerGenerator.nullReturner(false))[0]", e.getMessage(), "while trying to load from a null char array returned from 'NullPointerExceptionTest$NullPointerGenerator.nullReturner(Z)Ljava/lang/Object;'");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromMethod2() {
+ try {
+ Asserts.assertTrue(((char[]) (new NullPointerGenerator().returnMyNull(1, 1, (short) 1)))[0] == 'a');
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("((char[]) (new NullPointerGenerator().returnMyNull(1, 1, (short) 1)))[0]", e.getMessage(), "while trying to load from a null char array returned from 'NullPointerExceptionTest$NullPointerGenerator.returnMyNull(DJS)Ljava/lang/Object;'");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromMethod3() {
+ try {
+ Asserts.assertTrue(((double[]) returnNull(null, null, 1f))[0] == 1.0);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("((double[]) returnNull(null, null, 1f))[0] ", e.getMessage(), "while trying to load from a null double array returned from 'NullPointerExceptionTest.returnNull([[Ljava/lang/String;[[[IF)Ljava/lang/Object;'");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromMethod4() {
+ ImplTestLoadedFromMethod4(new DoubleArrayGenImpl());
+ }
+
+ /**
+ * @param gen
+ */
+ public void ImplTestLoadedFromMethod4(DoubleArrayGen gen) {
+ try {
+ (gen.getArray())[0] = 1.0;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("(gen.getArray())[0]", e.getMessage(), "while trying to store to a null double array returned from 'NullPointerExceptionTest$DoubleArrayGen.getArray()[D'");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromMethod5() {
+ try {
+ returnMeAsNull(null, 1, 'A').dag = null;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("returnMeAsNull(null, 1, 'A').dag = null", e.getMessage(), "while trying to write the field 'NullPointerExceptionTest.dag' of a null object returned from 'NullPointerExceptionTest.returnMeAsNull(Ljava/lang/Throwable;IC)LNullPointerExceptionTest;'");
+ }
+ /*
+ try {
+ returnMeAsNull(null, 1, 'A').dag.dag = null;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("returnMeAsNull(null, 1, 'A').dag.dag = null", e.getMessage(), "while trying to write the field 'NullPointerExceptionTest.dag' of a null object returned from 'NullPointerExceptionTest.returnMeAsNull(Ljava/lang/Throwable;IC)LNullPointerExceptionTest;'");
+ }
+ */
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("null")
+ public void testLoadedFromMethod6() {
+ short[] sa = null;
+
+ try {
+ Asserts.assertTrue(sa[0] == (short) 1);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("sa[0]", e.getMessage(), "while trying to load from a null short array loaded from " + (hasDebugInfo ? "local variable 'sa'" : "a local variable at slot 1"));
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("null")
+ public void testLoadedFromMethod7() {
+ short[] sa = null;
+
+ try {
+ sa[0] = 1;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("sa[0] = 1", e.getMessage(), "while trying to store to a null short array loaded from " + (hasDebugInfo ? "local variable 'sa'" : "a local variable at slot 1"));
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromStaticField1() {
+ try {
+ Asserts.assertTrue(((float[]) nullStaticField)[0] == 1.0f);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("((float[]) nullStaticField)[0]", e.getMessage(), "while trying to load from a null float array loaded from static field 'NullPointerExceptionTest.nullStaticField'");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromStaticField2() {
+ try {
+ ((float[]) nullStaticField)[0] = 1.0f;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("((float[]) nullStaticField)[0] = 1.0f", e.getMessage(), "while trying to store to a null float array loaded from static field 'NullPointerExceptionTest.nullStaticField'");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromStaticField3() {
+ try {
+ Asserts.assertTrue(staticArray[0][0][0][0] == 1);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("staticArray[0][0][0][0] // staticArray[0][0][0] is null.", e.getMessage(), "while trying to load from a null int array loaded from an array (which itself was loaded from an array) with an index loaded from a constant");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromStaticField4(int myIdx, int pos) {
+ try {
+ staticArray[0][0][pos][myIdx] = 2;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage(" staticArray[0][0][pos][myIdx] = 2", e.getMessage(), "while trying to store to a null int array loaded from an array (which itself was loaded from an array) with an index loaded from " + (hasDebugInfo ? "local variable 'pos'" : "the parameter nr. 2 of the method"));
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromStaticField5() {
+ try {
+ Asserts.assertTrue(staticLongArray[0][0] == 1L);
+ } catch (NullPointerException e) {
+ checkMessage("staticLongArray[0][0]", e.getMessage(), "while trying to load from a null long array loaded from an array (which itself was loaded from static field 'NullPointerExceptionTest.staticLongArray') with an index loaded from a constant");
+ }
+ }
+
+ /**
+ * Test bipush for index.
+ */
+ public void testLoadedFromStaticField5a() {
+ try {
+ Asserts.assertTrue(staticLongArray[139 /*0x77*/][0] == 1L);
+ } catch (NullPointerException e) {
+ checkMessage("staticLongArray[139][0]", e.getMessage(), "while trying to load from a null long array loaded from an array (which itself was loaded from static field 'NullPointerExceptionTest.staticLongArray') with an index loaded from a constant");
+ }
+ }
+
+ /**
+ * Test sipush for index.
+ */
+ public void testLoadedFromStaticField5b() {
+ try {
+ Asserts.assertTrue(staticLongArray[819 /*0x333*/][0] == 1L);
+ } catch (NullPointerException e) {
+ checkMessage("staticLongArray[819][0]", e.getMessage(), "while trying to load from a null long array loaded from an array (which itself was loaded from static field 'NullPointerExceptionTest.staticLongArray') with an index loaded from a constant");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromStaticField6() {
+ try {
+ staticLongArray[0][0] = 2L;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("staticLongArray[0][0] = 2L", e.getMessage(), "while trying to store to a null long array loaded from an array (which itself was loaded from static field 'NullPointerExceptionTest.staticLongArray') with an index loaded from a constant");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromInstanceField1() {
+ try {
+ Asserts.assertTrue(this.nullInstanceField.nullInstanceField == null);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("this.nullInstanceField.nullInstanceField", e.getMessage(), "while trying to read the field 'nullInstanceField' of a null object loaded from field 'NullPointerExceptionTest.nullInstanceField' of an object loaded from 'this'");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromInstanceField2() {
+ try {
+ this.nullInstanceField.nullInstanceField = null;
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("this.nullInstanceField.nullInstanceField = null", e.getMessage(), "while trying to write the field 'NullPointerExceptionTest.nullInstanceField' of a null object loaded from field 'NullPointerExceptionTest.nullInstanceField' of an object loaded from 'this'");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromInstanceField3() {
+ NullPointerExceptionTest obj = this;
+
+ try {
+ Asserts.assertNull(obj.dag.getArray().clone());
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("obj.dag.getArray().clone()", e.getMessage(), "while trying to invoke the method 'NullPointerExceptionTest$DoubleArrayGen.getArray()[D' on a null reference loaded from field 'NullPointerExceptionTest.dag' of an object loaded from " + (hasDebugInfo ? "local variable 'obj'" : "a local variable at slot 1"));
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromInstanceField4() {
+ int indexes[] = new int[1];
+
+ NullPointerExceptionTest[] objs = new NullPointerExceptionTest[] {this};
+
+ try {
+ Asserts.assertNull(objs[indexes[0]].nullInstanceField.returnNull(null, null, 1f));
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("objs[indexes[0]].nullInstanceField.returnNull(null, null, 1f", e.getMessage(), "while trying to invoke the method 'NullPointerExceptionTest.returnNull([[Ljava/lang/String;[[[IF)Ljava/lang/Object;' on a null reference loaded from field 'NullPointerExceptionTest.nullInstanceField' of an object loaded from an array");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromInstanceField5() {
+ int indexes[] = new int[1];
+
+ NullPointerExceptionTest[] objs = new NullPointerExceptionTest[] {this};
+
+ try {
+ Asserts.assertNull(objs[indexes[0]].nullInstanceField.toString().toCharArray().clone());
+ } catch (NullPointerException e) {
+ checkMessage("objs[indexes[0]].nullInstanceField.toString().toCharArray().clone()", e.getMessage(), "while trying to invoke the method 'java.lang.Object.toString()Ljava/lang/String;' on a null reference loaded from field 'NullPointerExceptionTest.nullInstanceField' of an object loaded from an array");
+ }
+ }
+
+ /**
+ *
+ */
+ public void testLoadedFromInstanceField6() {
+ int indexes[] = new int[1];
+
+ NullPointerExceptionTest[][] objs =
+ new NullPointerExceptionTest[][] {new NullPointerExceptionTest[] {this}};
+
+ try {
+ // Check monitorenter only, since we cannot probe monitorexit from Java.
+ synchronized (objs[indexes[0]][0].nullInstanceField) {
+ Asserts.fail();
+ }
+ } catch (NullPointerException e) {
+ checkMessage("synchronized (objs[indexes[0]][0].nullInstanceField)", e.getMessage(), "while trying to enter a null monitor loaded from field 'NullPointerExceptionTest.nullInstanceField' of an object loaded from an array");
+ }
+ }
+
+ /**
+ * @throws ClassNotFoundException
+ */
+ public void testInNative() throws ClassNotFoundException {
+ try {
+ Class.forName(null);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ Asserts.assertNull(e.getMessage());
+ }
+ }
+
+ private Object returnNull(String[][] dummy1, int[][][] dummy2, float dummy3) {
+ return null;
+ }
+
+ private NullPointerExceptionTest returnMeAsNull(Throwable dummy1, int dummy2, char dummy3){
+ return null;
+ }
+
+ static interface DoubleArrayGen {
+ public double[] getArray();
+ }
+
+ static class DoubleArrayGenImpl implements DoubleArrayGen {
+ @Override
+ public double[] getArray() {
+ return null;
+ }
+ }
+
+ static class NullPointerGenerator {
+ public static Object nullReturner(boolean dummy1) {
+ return null;
+ }
+
+ public Object returnMyNull(double dummy1, long dummy2, short dummy3) {
+ return null;
+ }
+ }
+
+ /**
+ *
+ */
+ public void testMissingLocalVariableTable() {
+ doTestMissingLocalVariableTable(names);
+
+ String[] expectedHasDebugInfoGoodNames = new String[] {
+ "while trying to invoke the method 'java.lang.Object.hashCode()I' on a null reference " +
+ "loaded from field 'NullPointerExceptionTest.nullInstanceField' " +
+ "of an object loaded from 'this'",
+ "while trying to invoke the method 'java.lang.Object.hashCode()I' on a null reference loaded from " +
+ "local variable 'a1'",
+ "while trying to invoke the method 'java.lang.Object.hashCode()I' on a null reference loaded from " +
+ "local variable 'o1'",
+ "while trying to invoke the method 'java.lang.Object.hashCode()I' on a null reference loaded from " +
+ "local variable 'aa1'"
+ };
+
+ String[] expectedNoDebugInfoGoodNames = new String[] {
+ "while trying to invoke the method 'java.lang.Object.hashCode()I' on a null reference " +
+ "loaded from field 'NullPointerExceptionTest.nullInstanceField' " +
+ "of an object loaded from 'this'",
+ "while trying to invoke the method 'java.lang.Object.hashCode()I' on a null reference loaded from " +
+ "the parameter nr. 5 of the method",
+ "while trying to invoke the method 'java.lang.Object.hashCode()I' on a null reference loaded from " +
+ "the parameter nr. 2 of the method",
+ "while trying to invoke the method 'java.lang.Object.hashCode()I' on a null reference loaded from " +
+ "the parameter nr. 9 of the method"
+ };
+
+ String[] expectedNames;
+ if (hasDebugInfo) {
+ expectedNames = expectedHasDebugInfoGoodNames;
+ } else {
+ expectedNames = expectedNoDebugInfoGoodNames;
+ }
+
+ // The two lists of messages should have the same length.
+ Asserts.assertEquals(names.size(), expectedNames.length);
+
+ for (int i = 0; i < expectedNames.length; ++i) {
+ Asserts.assertEquals(names.get(i), expectedNames[i]);
+ }
+ }
+
+ private void doTestMissingLocalVariableTable(ArrayList<String> names) {
+ curr = names;
+ doTestMissingLocalVariableTable1();
+ doTestMissingLocalVariableTable2(-1, null, false, 0.0, null, 0.1f, (byte) 0, (short) 0, null);
+ }
+
+ private void doTestMissingLocalVariableTable1() {
+ try {
+ this.nullInstanceField.hashCode();
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ curr.add(e.getMessage());
+ }
+ }
+
+ private void doTestMissingLocalVariableTable2(long l1, Object o1, boolean z1, double d1, Object[] a1,
+ float f1, byte b1, short s1, Object[][] aa1) {
+ try {
+ a1.hashCode();
+ Asserts.fail();
+ }
+ catch (NullPointerException e) {
+ curr.add(e.getMessage());
+ }
+
+ try {
+ o1.hashCode();
+ Asserts.fail();
+ }
+ catch (NullPointerException e) {
+ curr.add(e.getMessage());
+ }
+
+ try {
+ aa1.hashCode();
+ Asserts.fail();
+ }
+ catch (NullPointerException e) {
+ curr.add(e.getMessage());
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("null")
+ public void testNullMessages() {
+ boolean[] za1 = null;
+ byte[] ba1 = null;
+ short[] sa1 = null;
+ char[] ca1 = null;
+ int[] ia1 = null;
+ long[] la1 = null;
+ float[] fa1 = null;
+ double[] da1 = null;
+ Object[] oa1 = null;
+
+ Object[][] oa2 = new Object[2][];
+ oa2[1] = oa1;
+
+ try {
+ System.out.println(oa2[1][0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("oa2[1][0]", e.getMessage(),
+ "while trying to load from a null object array loaded from an array " +
+ "(which itself was loaded from " +
+ (hasDebugInfo ? "local variable 'oa2'" : "a local variable at slot 10") + ") " +
+ "with an index loaded from a constant");
+ }
+
+
+ try {
+ System.out.println(za1[0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("za1[0]", e.getMessage(),
+ "while trying to load from a null byte (or boolean) array loaded from " +
+ (hasDebugInfo ? "local variable 'za1'" : "a local variable at slot 1"));
+ }
+
+ try {
+ System.out.println(ba1[0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("ba1[0]", e.getMessage(),
+ "while trying to load from a null byte (or boolean) array loaded from " +
+ (hasDebugInfo ? "local variable 'ba1'" : "a local variable at slot 2"));
+ }
+
+ try {
+ System.out.println(sa1[0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("sa1[0]", e.getMessage(),
+ "while trying to load from a null short array loaded from " +
+ (hasDebugInfo ? "local variable 'sa1'" : "a local variable at slot 3"));
+ }
+
+ try {
+ System.out.println(ca1[0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("ca1[0]", e.getMessage(),
+ "while trying to load from a null char array loaded from " +
+ (hasDebugInfo ? "local variable 'ca1'" : "a local variable at slot 4"));
+ }
+
+ try {
+ System.out.println(ia1[0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("ia1[0]", e.getMessage(),
+ "while trying to load from a null int array loaded from " +
+ (hasDebugInfo ? "local variable 'ia1'" : "a local variable at slot 5"));
+ }
+
+ try {
+ System.out.println(la1[0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("la1[0]", e.getMessage(),
+ "while trying to load from a null long array loaded from " +
+ (hasDebugInfo ? "local variable 'la1'" : "a local variable at slot 6"));
+ }
+
+ try {
+ System.out.println(fa1[0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("fa1[0]", e.getMessage(),
+ "while trying to load from a null float array loaded from " +
+ (hasDebugInfo ? "local variable 'fa1'" : "a local variable at slot 7"));
+ }
+
+ try {
+ System.out.println(da1[0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("da1[0]", e.getMessage(),
+ "while trying to load from a null double array loaded from " +
+ (hasDebugInfo ? "local variable 'da1'" : "a local variable at slot 8"));
+ }
+
+ try {
+ System.out.println(oa1[0]);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("oa1[0]", e.getMessage(),
+ "while trying to load from a null object array loaded from " +
+ (hasDebugInfo ? "local variable 'oa1'" : "a local variable at slot 9"));
+ }
+
+ try {
+ System.out.println(za1[0] = false);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("za1[0] = false", e.getMessage(),
+ "while trying to store to a null byte (or boolean) array loaded from " +
+ (hasDebugInfo ? "local variable 'za1'" : "a local variable at slot 1"));
+ }
+
+ try {
+ System.out.println(ba1[0] = 0);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("ba1[0] = 0", e.getMessage(),
+ "while trying to store to a null byte (or boolean) array loaded from " +
+ (hasDebugInfo ? "local variable 'ba1'" : "a local variable at slot 2"));
+ }
+
+ try {
+ System.out.println(sa1[0] = 0);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("sa1[0] = 0", e.getMessage(),
+ "while trying to store to a null short array loaded from " +
+ (hasDebugInfo ? "local variable 'sa1'" : "a local variable at slot 3"));
+ }
+
+ try {
+ System.out.println(ca1[0] = 0);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("ca1[0] = 0", e.getMessage(),
+ "while trying to store to a null char array loaded from " +
+ (hasDebugInfo ? "local variable 'ca1'" : "a local variable at slot 4"));
+ }
+
+ try {
+ System.out.println(ia1[0] = 0);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("ia1[0] = 0", e.getMessage(),
+ "while trying to store to a null int array loaded from " +
+ (hasDebugInfo ? "local variable 'ia1'" : "a local variable at slot 5"));
+ }
+
+ try {
+ System.out.println(la1[0] = 0);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("la1[0] = 0", e.getMessage(),
+ "while trying to store to a null long array loaded from " +
+ (hasDebugInfo ? "local variable 'la1'" : "a local variable at slot 6"));
+ }
+
+ try {
+ System.out.println(fa1[0] = 0);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("fa1[0] = 0", e.getMessage(),
+ "while trying to store to a null float array loaded from " +
+ (hasDebugInfo ? "local variable 'fa1'" : "a local variable at slot 7"));
+ }
+
+ try {
+ System.out.println(da1[0] = 0);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("da1[0] = 0", e.getMessage(),
+ "while trying to store to a null double array loaded from " +
+ (hasDebugInfo ? "local variable 'da1'" : "a local variable at slot 8"));
+ }
+
+ try {
+ System.out.println(oa1[0] = null);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("oa1[0] = null", e.getMessage(),
+ "while trying to store to a null object array loaded from " +
+ (hasDebugInfo ? "local variable 'oa1'" : "a local variable at slot 9"));
+ }
+
+ try {
+ System.out.println(nullInstanceField.nullInstanceField);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("nullInstanceField.nullInstanceField", e.getMessage(),
+ "while trying to read the field 'nullInstanceField' of a null object loaded " +
+ "from field 'NullPointerExceptionTest.nullInstanceField' of an object " +
+ "loaded from 'this'");
+ }
+
+ try {
+ System.out.println(nullInstanceField.nullInstanceField = null);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("nullInstanceField.nullInstanceField = null", e.getMessage(),
+ "while trying to write the field 'NullPointerExceptionTest.nullInstanceField' " +
+ "of a null object loaded from field 'NullPointerExceptionTest.nullInstanceField' " +
+ "of an object loaded from 'this'");
+ }
+
+ try {
+ System.out.println(za1.length);
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("za1.length", e.getMessage(),
+ "while trying to get the length of a null array loaded from " +
+ (hasDebugInfo ? "local variable 'za1'" : "a local variable at slot 1"));
+ }
+
+ try {
+ throw null;
+ } catch (NullPointerException e) {
+ checkMessage("throw null", e.getMessage(),
+ "while trying to throw a null exception object loaded " +
+ "from a constant");
+ }
+
+ try {
+ synchronized (nullInstanceField) {
+ // desired
+ }
+ } catch (NullPointerException e) {
+ checkMessage("synchronized (nullInstanceField)", e.getMessage(),
+ "while trying to enter a null monitor loaded from field " +
+ "'NullPointerExceptionTest.nullInstanceField' of an object loaded from " +
+ "'this'");
+ }
+
+ try {
+ nullInstanceField.testCreationViaNew();
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("nullInstanceField.testCreationViaNew()", e.getMessage(),
+ "while trying to invoke the method 'NullPointerExceptionTest.testCreationViaNew()V' on a null reference " +
+ "loaded from field 'NullPointerExceptionTest.nullInstanceField' of an " +
+ "object loaded from 'this'");
+ }
+
+ try {
+ nullInstanceField.testNullMessages();
+ Asserts.fail();
+ } catch (NullPointerException e) {
+ checkMessage("nullInstanceField.testNullMessages()", e.getMessage(),
+ "while trying to invoke the method 'NullPointerExceptionTest.testNullMessages()V' on a null reference " +
+ "loaded from field 'NullPointerExceptionTest.nullInstanceField' of an " +
+ "object loaded from 'this'");
+ }
+
+ try {
+ // If we can get the value from more than one bci, we cannot know which one.
+ (Math.random() < 0.5 ? oa1 : (new Object[1])[0]).equals("");
+ } catch (NullPointerException e) {
+ checkMessage("(Math.random() < 0.5 ? oa1 : (new Object[1])[0]).equals(\"\")", e.getMessage(),
+ "while trying to invoke the method 'java.lang.Object.equals(Ljava/lang/Object;)Z' on a null reference");
+ }
+ }
+}
--- a/test/hotspot/jtreg/vmTestbase/jit/t/t104/t104.gold Tue Mar 19 17:03:18 2019 +0800
+++ b/test/hotspot/jtreg/vmTestbase/jit/t/t104/t104.gold Fri Feb 08 14:15:05 2019 +0100
@@ -1,2 +1,2 @@
Exception thrown.
-java.lang.NullPointerException
+java.lang.NullPointerException: while trying to enter a null monitor loaded from a local variable at slot 1