# HG changeset patch # User goetz # Date 1554877583 -7200 # Node ID aa400d41ebd65b07622685e2ee255a467e4a21de # Parent cb67307942f3082bffd0a65fdca9c8619980491f Print methods in Java syntax. Simplify computing the message Summary: Cleanup test: arrange according to messages and topics. Add test cases. diff -r cb67307942f3 -r aa400d41ebd6 src/hotspot/share/interpreter/bytecodeUtils.cpp --- a/src/hotspot/share/interpreter/bytecodeUtils.cpp Wed Apr 10 08:15:45 2019 +0200 +++ b/src/hotspot/share/interpreter/bytecodeUtils.cpp Wed Apr 10 08:26:23 2019 +0200 @@ -32,22 +32,10 @@ #include "utilities/events.hpp" /* - * Returns the name of the klass that is described at constant pool + * Prints 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_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) { +static void print_method_name(outputStream *os, 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); @@ -58,16 +46,17 @@ 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(); + signature->print_as_signature_external_return_type(os); + os->print(" %s.%s(", klass->as_klass_external_name(), name->as_C_string()); + signature->print_as_signature_external_parameters(os); + os->print(")"); } /* - * Returns the name of the field that is described at constant pool + * Prints 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) { +static void print_field_and_class(outputStream *os, 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); @@ -76,16 +65,14 @@ 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(); + os->print("%s.%s", klass->as_klass_external_name(), name->as_C_string()); } /* * Returns the name of the field that is described at constant pool * index cp_index in the constant pool of method 'method'. */ -char const* MethodBytecodePrinter::get_field_name(Method* method, int cp_index) { +static char const* 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); @@ -93,6 +80,54 @@ return name->as_C_string(); } +static void print_local_var(outputStream *os, int bci, Method* method, int slot) { + if (method->has_localvariable_table()) { + for (int i = 0; i < method->localvariable_table_length(); i++) { + LocalVariableTableElement* elem = method->localvariable_table_start() + i; + 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(); + os->print("%s", var); + + return; + } + } + } + + // Handle at least some cases we know. + if (!method->is_static() && (slot == 0)) { + os->print("this"); + } else { + int curr = method->is_static() ? 0 : 1; + SignatureStream ss(method->signature()); + int param_index = 0; + bool found = false; + + for (SignatureStream ss(method->signature()); !ss.is_done(); ss.next()) { + if (ss.at_return_type()) { + continue; + } + int size = type2size[ss.type()]; + if ((slot >= curr) && (slot < curr + size)) { + found = true; + break; + } + param_index += 1; + curr += size; + } + + if (found) { + os->print("", 1 + param_index); + } else { + // This is the best we can do. + os->print("", slot); + } + } +} + TrackingStackEntry::TrackingStackEntry(BasicType type) : _entry(INVALID + type * SCALE) { } TrackingStackEntry::TrackingStackEntry(int bci, BasicType type) : _entry(bci + type * SCALE) { @@ -105,7 +140,7 @@ } BasicType TrackingStackEntry::get_type() { - return BasicType (_entry / SCALE); + return BasicType(_entry / SCALE); } TrackingStackEntry TrackingStackEntry::merge(TrackingStackEntry other) { @@ -184,120 +219,6 @@ 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("this"); - } else { - reason.print("%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("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("", 1 + param_index); - } else { - // This is the best we can do. - reason.print("", slot); - } - } - - return TrackingStackSource(TrackingStackSource::LOCAL_VAR, bci, reason.as_string()); -} - - -static TrackingStackSource createConstantSource(int bci, const char *text) { - return TrackingStackSource(TrackingStackSource::CONSTANT, bci, text); -} - -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) { - reason.print("%s", array_source.as_string()); - } else { - reason.print("array"); - } - if (index_source.get_type() != TrackingStackSource::INVALID) { - reason.print("[%s]", index_source.as_string()); - } else { - reason.print("[...]"); - } - - 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; - - // GLGL We could also print the type of the field. Should we do that? - //MethodBytecodePrinter::get_klass_name(method, cp_index) - if (object_source.get_type() != TrackingStackSource::INVALID) { - reason.print("%s.", object_source.as_string()); - } - reason.print("%s", MethodBytecodePrinter::get_field_name(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("static %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(); @@ -912,128 +833,27 @@ 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); +int TrackingStackCreator::get_NPE_null_slot(int bci) { + // If this NPE was created via reflection, we have no real NPE. + if (_method->method_holder() == SystemDictionary::reflect_NativeConstructorAccessorImpl_klass()) { + return -2; } - - 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; - + 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 + source_bci + 1); + code = Bytecodes::java_code_at(_method, code_base + 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: - return createConstantSource(source_bci, "null"); - case Bytecodes::_iconst_m1: - return createConstantSource(source_bci, "-1"); - case Bytecodes::_iconst_0: - return createConstantSource(source_bci, "0"); - case Bytecodes::_iconst_1: - return createConstantSource(source_bci, "1"); - case Bytecodes::_iconst_2: - return createConstantSource(source_bci, "2"); - case Bytecodes::_iconst_3: - return createConstantSource(source_bci, "3"); - case Bytecodes::_iconst_4: - return createConstantSource(source_bci, "4"); - case Bytecodes::_iconst_5: - return createConstantSource(source_bci, "5"); - case Bytecodes::_lconst_0: - return createConstantSource(source_bci, "0L"); - case Bytecodes::_lconst_1: - return createConstantSource(source_bci, "1L"); - case Bytecodes::_fconst_0: - return createConstantSource(source_bci, "0.0f"); - case Bytecodes::_fconst_1: - return createConstantSource(source_bci, "1.0f"); - case Bytecodes::_fconst_2: - return createConstantSource(source_bci, "2.0f"); - case Bytecodes::_dconst_0: - return createConstantSource(source_bci, "0.0"); - case Bytecodes::_dconst_1: - return createConstantSource(source_bci, "1.0"); - case Bytecodes::_bipush: { - jbyte con = *(jbyte*) (code_base + source_bci + 1); - stringStream ss; - ss.print("%d", con); - return createConstantSource(source_bci, ss.as_string()); - } - case Bytecodes::_sipush: { - u2 con = Bytes::get_Java_u2(code_base + source_bci + 1); - stringStream ss; - ss.print("%d", con); - return createConstantSource(source_bci, ss.as_string()); - } + case Bytecodes::_getfield: + case Bytecodes::_arraylength: + case Bytecodes::_athrow: + case Bytecodes::_monitorenter: + case Bytecodes::_monitorexit: + return 0; case Bytecodes::_iaload: case Bytecodes::_faload: case Bytecodes::_aaload: @@ -1041,259 +861,26 @@ 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); - // We assume outermost caller has ResourceMark. - stringStream reason; - if (max_detail == 5 /* Todo: introduce a constant ... */) { - reason.print("The return value of '%s'", MethodBytecodePrinter::get_method_name(_method, cp_index)); - } else { - reason.print("%s", MethodBytecodePrinter::get_method_name(_method, cp_index)); - } - return TrackingStackSource(TrackingStackSource::METHOD, source_bci, reason.as_string()); - } - - 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 = "Can not load from null int array."; - } - - result = 1; - break; - - case Bytecodes::_faload: - if (reason != NULL) { - *reason = "Can not load from null float array."; - } - - result = 1; - break; - - case Bytecodes::_aaload: - if (reason != NULL) { - *reason = "Can not load from null object array."; - } - - result = 1; - break; - - case Bytecodes::_baload: - if (reason != NULL) { - *reason = "Can not load from null byte/boolean array."; - } - - result = 1; - break; - - case Bytecodes::_caload: - if (reason != NULL) { - *reason = "Can not load from null char array."; - } - - result = 1; - break; - - case Bytecodes::_saload: - if (reason != NULL) { - *reason = "Can not load from null short array."; - } - - result = 1; - break; - - case Bytecodes::_laload: - if (reason != NULL) { - *reason = "Can not load from null long array."; - } - - result = 1; - break; - case Bytecodes::_daload: - if (reason != NULL) { - *reason = "Can not load from null double array."; - } - - result = 1; - break; - + return 1; case Bytecodes::_iastore: - if (reason != NULL) { - *reason = "Can not store to null int array."; - } - - result = 2; - break; - - case Bytecodes::_lastore: - if (reason != NULL) { - *reason = "Can not store to null long array."; - } - - result = 3; - break; - case Bytecodes::_fastore: - if (reason != NULL) { - *reason = "Can not store to null float array."; - } - - result = 2; - break; - - case Bytecodes::_dastore: - if (reason != NULL) { - *reason = "Can not store to null double array."; - } - - result = 3; - break; - case Bytecodes::_aastore: - if (reason != NULL) { - *reason = "Can not store to null object array."; - } - - result = 2; - break; - case Bytecodes::_bastore: - if (reason != NULL) { - *reason = "Can not store to to null byte/boolean array."; - } - - result = 2; - break; - case Bytecodes::_castore: - if (reason != NULL) { - *reason = "Can not store to to null char array."; - } - - result = 2; - break; - case Bytecodes::_sastore: - if (reason != NULL) { - *reason = "Can not store to 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("Can not read field '%s'.", name->as_C_string()); - *reason = ss.as_string(); - } - - result = 0; - } - - break; - - case Bytecodes::_arraylength: - if (reason != NULL) { - *reason = "Can not read the array length."; - } - - result = 0; - break; - - case Bytecodes::_athrow: - if (reason != NULL) { - *reason = "Can not throw a null exception object."; - } - - result = 0; - break; - - case Bytecodes::_monitorenter: - if (reason != NULL) { - *reason = "Can not enter a null monitor."; - } - - result = 0; - break; - - case Bytecodes::_monitorexit: - if (reason != NULL) { - *reason = "Can not exit a null monitor."; - } - - result = 0; - break; - - case Bytecodes::_putfield: - { + return 2; + case Bytecodes::_lastore: + case Bytecodes::_dastore: + return 3; + case Bytecodes::_putfield: { int cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); ConstantPool* cp = _method->constants(); int name_and_type_index = cp->name_and_type_ref_index_at(cp_index); int type_index = cp->signature_ref_index_at(name_and_type_index); Symbol* signature = cp->symbol_at(type_index); - - if (reason != NULL) { - stringStream ss; - ss.print("Can not write field '%s'.", - MethodBytecodePrinter::get_field_name(_method, cp_index)); - *reason = ss.as_string(); - } - - result = type2size[char2type((char) signature->char_at(0))]; + return type2size[char2type((char) signature->char_at(0))]; } - - break; - case Bytecodes::_invokevirtual: case Bytecodes::_invokespecial: case Bytecodes::_invokeinterface: @@ -1310,26 +897,332 @@ // (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("Can not invoke method '%s'.", - MethodBytecodePrinter::get_method_name(_method, cp_index)); - *reason = ss.as_string(); - } - - result = ArgumentSizeComputer(signature).size(); - } - else { - result = -2; + return ArgumentSizeComputer(signature).size(); + } else { + return -2; } } - break; - default: break; } - return result; + return -1; +} + +const int TrackingStackCreator::_max_cause_detail = 5; + +void TrackingStackCreator::TrackingStackCreator::print_NPE_cause(outputStream *os, int bci, int slot) { + if (print_NPE_cause0(os, bci, slot, _max_cause_detail, "'")) { + os->print("' is null. "); + } } +/* Recursively print what was null. + * + * Go the the bytecode that pushed slot 'slot' on the operant stack + * at bytecode 'bci'. Compute a message for that bytecode. If + * necessary (array, field), recur further. + * At most do max_detail recursions. + * + * Returns true if something was printed. + */ +bool TrackingStackCreator::TrackingStackCreator::print_NPE_cause0(outputStream *os, int bci, int slot, + int max_detail, const char *prefix) { + assert(bci >= 0, "BCI too low"); + assert(bci < get_size(), "BCI to large"); + + if (max_detail <= 0) { + return false; + } + + if (_stacks->at(bci) == NULL) { + return false; + } + + 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 false; + } + + // 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; + } + + if (max_detail == _max_cause_detail && + prefix != NULL && + code != Bytecodes::_invokevirtual && + code != Bytecodes::_invokespecial && + code != Bytecodes::_invokestatic && + code != Bytecodes::_invokeinterface) { + os->print("%s", prefix); + } + + switch (code) { + case Bytecodes::_iload_0: + //case Bytecodes::_lload_0: // ? + //case Bytecodes::_fload_0: // ? + //case Bytecodes::_dload_0: // ? + case Bytecodes::_aload_0: + print_local_var(os, source_bci, _method, 0); + return true; + + case Bytecodes::_iload_1: + //case Bytecodes::_lload_1: // ? + //case Bytecodes::_fload_1: // ? + //case Bytecodes::_dload_1: // ? + case Bytecodes::_aload_1: + print_local_var(os, source_bci, _method, 1); + return true; + + case Bytecodes::_iload_2: + //case Bytecodes::_lload_2: // ? + //case Bytecodes::_fload_2: // ? + //case Bytecodes::_dload_2: // ? + case Bytecodes::_aload_2: + print_local_var(os, source_bci, _method, 2); + return true; + + case Bytecodes::_iload_3: + //case Bytecodes::_lload_3: // ? + //case Bytecodes::_fload_3: // ? + //case Bytecodes::_dload_3: // ? + case Bytecodes::_aload_3: + print_local_var(os, source_bci, _method, 3); + return true; + + 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); + } + + print_local_var(os, source_bci, _method, index); + return true; + } + + case Bytecodes::_aconst_null: + os->print("null"); + return true; + case Bytecodes::_iconst_m1: + os->print("-1"); + return true; + case Bytecodes::_iconst_0: + os->print("0"); + return true; + case Bytecodes::_iconst_1: + os->print("1"); + return true; + case Bytecodes::_iconst_2: + os->print("2"); + return true; + case Bytecodes::_iconst_3: + os->print("3"); + return true; + case Bytecodes::_iconst_4: + os->print("4"); + return true; + case Bytecodes::_iconst_5: + os->print("5"); + return true; + /* + case Bytecodes::_lconst_0: // ? + os->print("0L"); + return true; + case Bytecodes::_lconst_1: // ? + os->print("1L"); + return true; + case Bytecodes::_fconst_0: // ? + os->print("0.0f"); + return true; + case Bytecodes::_fconst_1: // ? + os->print("1.0f"); + return true; + case Bytecodes::_fconst_2: // ? + os->print("2.0f"); + return true; + case Bytecodes::_dconst_0: // ? + os->print("0.0"); + return true; + case Bytecodes::_dconst_1: // ? + os->print("1.0"); + return true; + */ + case Bytecodes::_bipush: { + jbyte con = *(jbyte*) (code_base + source_bci + 1); + os->print("%d", con); + return true; + } + case Bytecodes::_sipush: { + u2 con = Bytes::get_Java_u2(code_base + source_bci + 1); + os->print("%d", con); + return true; + } + case Bytecodes::_iaload: + //case Bytecodes::_faload: // ? + case Bytecodes::_aaload: { + //case Bytecodes::_baload: // ? + //case Bytecodes::_caload: // ? + //case Bytecodes::_saload: // ? + //case Bytecodes::_laload: // ? + //case Bytecodes::_daload: { // ? + + // Print the 'name' of the array. Go back to the bytecode that + // pushed the array reference on the operand stack. + if (!print_NPE_cause0(os, source_bci, 1, max_detail-1)) { + // Returned false. Max recursion depth was reached. Print dummy. + os->print(""); + } + os->print("["); + // Print the index expression. Go back to the bytecode that + // pushed the index on the operand stack. + // Don't decrement maxdetail so we get a value here and only + // cancel out on the dereference. + if (!print_NPE_cause0(os, source_bci, 0, max_detail)) { + // Returned false. We don't print complex array index expressions. Print placeholder. + os->print("..."); + } + os->print("]"); + return true; + } + + case Bytecodes::_getstatic: { + int cp_index = Bytes::get_native_u2(code_base + pos) + ConstantPool::CPCACHE_INDEX_TAG; + os->print("static "); + print_field_and_class(os, _method, cp_index); + return true; + } + + case Bytecodes::_getfield: { + // Print the sender. Go back to the bytecode that + // pushed the sender on the operand stack. + if (print_NPE_cause0(os, source_bci, 0, max_detail - 1)) { + os->print("."); + } + int cp_index = Bytes::get_native_u2(code_base + pos) + ConstantPool::CPCACHE_INDEX_TAG; + os->print("%s", get_field_name(_method, cp_index)); + return true; + } + + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + case Bytecodes::_invokeinterface: { + int cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); + if (max_detail == _max_cause_detail) { + os->print("The return value of '"); + } + print_method_name(os, _method, cp_index); + return true; + } + + default: break; + } + return false; +} + +void TrackingStackCreator::print_NPE_failedAction(outputStream *os, int bci) { + // If this NPE was created via reflection, we have no real NPE. + assert(_method->method_holder() != SystemDictionary::reflect_NativeConstructorAccessorImpl_klass(), + "We should have checked for reflection in get_NPE_null_slot()."); + + // Get the bytecode. + address code_base = _method->constMethod()->code_base(); + Bytecodes::Code code = Bytecodes::java_code_at(_method, code_base + bci); + int pos = bci + 1; + if (code == Bytecodes::_wide) { + code = Bytecodes::java_code_at(_method, code_base + bci + 1); + pos += 1; + } + + switch (code) { + case Bytecodes::_iaload: + os->print("Can not load from null int array."); break; + case Bytecodes::_faload: + os->print("Can not load from null float array."); break; + case Bytecodes::_aaload: + os->print("Can not load from null object array."); break; + case Bytecodes::_baload: + os->print("Can not load from null byte/boolean array."); break; + case Bytecodes::_caload: + os->print("Can not load from null char array."); break; + case Bytecodes::_saload: + os->print("Can not load from null short array."); break; + case Bytecodes::_laload: + os->print("Can not load from null long array."); break; + case Bytecodes::_daload: + os->print("Can not load from null double array."); break; + + case Bytecodes::_iastore: + os->print("Can not store to null int array."); break; + case Bytecodes::_fastore: + os->print("Can not store to null float array."); break; + case Bytecodes::_aastore: + os->print("Can not store to null object array."); break; + case Bytecodes::_bastore: + os->print("Can not store to null byte/boolean array."); break; + case Bytecodes::_castore: + os->print("Can not store to null char array."); break; + case Bytecodes::_sastore: + os->print("Can not store to null short array."); break; + case Bytecodes::_lastore: + os->print("Can not store to null long array."); break; + case Bytecodes::_dastore: + os->print("Can not store to null double array."); break; + + case Bytecodes::_arraylength: + os->print("Can not read the array length."); break; + case Bytecodes::_athrow: + os->print("Can not throw a null exception object."); break; + case Bytecodes::_monitorenter: + os->print("Can not enter a null monitor."); break; + case Bytecodes::_monitorexit: + os->print("Can not exit a null monitor."); break; + case Bytecodes::_getfield: { + int cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); + ConstantPool* cp = _method->constants(); + int name_and_type_index = cp->name_and_type_ref_index_at(cp_index); + int name_index = cp->name_ref_index_at(name_and_type_index); + Symbol* name = cp->symbol_at(name_index); + os->print("Can not read field '%s'.", name->as_C_string()); + } break; + case Bytecodes::_putfield: { + int cp_index = Bytes::get_native_u2(code_base + pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); + os->print("Can not write field '%s'.", get_field_name(_method, cp_index)); + } break; + case Bytecodes::_invokevirtual: + case Bytecodes::_invokespecial: + case Bytecodes::_invokeinterface: { + int cp_index = Bytes::get_native_u2(code_base+ pos) DEBUG_ONLY(+ ConstantPool::CPCACHE_INDEX_TAG); + os->print("Can not invoke method '"); + print_method_name(os, _method, cp_index); + os->print("'."); + } break; + + default: + assert(0, "We should have checked this bytecode in get_NPE_null_slot()."); + break; + } +} + diff -r cb67307942f3 -r aa400d41ebd6 src/hotspot/share/interpreter/bytecodeUtils.hpp --- a/src/hotspot/share/interpreter/bytecodeUtils.hpp Wed Apr 10 08:15:45 2019 +0200 +++ b/src/hotspot/share/interpreter/bytecodeUtils.hpp Wed Apr 10 08:26:23 2019 +0200 @@ -23,37 +23,13 @@ * */ -#ifndef SHARE_CLASSFILE_BYTECODEUTILS_HPP -#define SHARE_CLASSFILE_BYTECODEUTILS_HPP +#ifndef SHARE_INTERPRETER_BYTECODEUTILS_HPP +#define SHARE_INTERPRETER_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); -}; +#include "utilities/globalDefinitions.hpp" class TrackingStack; class TrackingStackCreator; @@ -140,56 +116,9 @@ 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 +// the object on the stack. The bci (if valid) holds the bci of the // instruction, which put the entry on the stack. class TrackingStackCreator { @@ -213,6 +142,8 @@ // If true, we have processed all bytecodes. bool _all_processed; + static const int _max_cause_detail; + // 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. @@ -222,6 +153,8 @@ // the size of the instruction. int do_instruction(int bci); + bool print_NPE_cause0(outputStream *os, int bci, int slot, int max_detail, const char *prefix = NULL); + public: // Creates tracking stacks for the given method (the method must be @@ -236,22 +169,21 @@ // 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); + // cannot be a NullPointerException at the bci, -1 is returned. + int get_NPE_null_slot(int bci); + // Prints a java-like expression for the bytecode that pushed + // the value to the given slot being live at the given bci. + // It constructs the expression recuring backwards over the + // bytecode. + // The TOS has the slot number 0, that below 1 and so on. + void print_NPE_cause(outputStream *os, int bci, int slot); + + // Prints a string describing the failed action. + void print_NPE_failedAction(outputStream *os, int bci); }; -#endif // SHARE_CLASSFILE_BYTECODEUTILS_HPP +#endif // SHARE_INTERPRETER_BYTECODEUTILS_HPP diff -r cb67307942f3 -r aa400d41ebd6 src/hotspot/share/prims/jvm.cpp --- a/src/hotspot/share/prims/jvm.cpp Wed Apr 10 08:15:45 2019 +0200 +++ b/src/hotspot/share/prims/jvm.cpp Wed Apr 10 08:26:23 2019 +0200 @@ -556,31 +556,29 @@ } ResourceMark rm(THREAD); + + // Analyse the bytecodes. TrackingStackCreator stc(method, bci); - char const* reason; - int slot = stc.get_null_pointer_slot(bci, &reason); + + // The slot of the operand stack that contains the null reference. + int slot = stc.get_NPE_null_slot(bci); // Build the message. stringStream ss; if (slot == -2) { + // We don't want to print a message. 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()); + // We encoutered a bytecode that does not dereference a reference. + DEBUG_ONLY(ss.print("There cannot be a NullPointerException at bci %d of method %s", + bci, method->external_name())); + NOT_DEBUG(return NULL); } else { - TrackingStackSource source = stc.get_source(bci, slot, 5); - if (source.get_type() != TrackingStackSource::INVALID) { - const char *msg = source.as_string(); - if (strncmp("The return value of", msg, strlen("The return value of")) == 0) { - ss.print("%s is null. ", msg); - } else { - ss.print("'%s' is null. ", msg); - } - } - ss.print("%s", reason); + // Print a description of what is null. + stc.print_NPE_cause(&ss, bci, slot); + // Print string describing which action (bytecode) could not be + // perfromed because of the null reference. + stc.print_NPE_failedAction(&ss, bci); } oop result = java_lang_String::create_oop_from_str(ss.as_string(), CHECK_0); diff -r cb67307942f3 -r aa400d41ebd6 test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java --- a/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java Wed Apr 10 08:15:45 2019 +0200 +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java Wed Apr 10 08:26:23 2019 +0200 @@ -27,6 +27,8 @@ * @summary Test extended NullPointerException message for class * files generated without debugging information. The message lists * detailed information about the entity that is null. + * @modules java.base/java.lang:open + * java.base/jdk.internal.org.objectweb.asm * @library /test/lib * @compile NullPointerExceptionTest.java * @run main NullPointerExceptionTest @@ -36,9 +38,11 @@ * @summary Test extended NullPointerException message for * classfiles generated with debug information. In this case the name * of the variable containing the array is printed. + * @modules java.base/java.lang:open + * java.base/jdk.internal.org.objectweb.asm * @library /test/lib * @compile -g NullPointerExceptionTest.java - * @run main/othervm -XX:+WizardMode -DhasDebugInfo NullPointerExceptionTest + * @run main/othervm -XX:+WizardMode NullPointerExceptionTest hasDebugInfo */ import java.io.ByteArrayInputStream; @@ -49,10 +53,22 @@ import jdk.test.lib.Asserts; +import java.lang.reflect.*; +import java.lang.invoke.MethodHandles.Lookup; +import static java.lang.invoke.MethodHandles.*; +import static java.lang.invoke.MethodHandles.Lookup.*; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Type; +import jdk.internal.org.objectweb.asm.Label; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + /** * Tests NullPointerExceptions */ public class NullPointerExceptionTest { + // Some fields used in the test. static Object nullStaticField; NullPointerExceptionTest nullInstanceField; @@ -61,15 +77,9 @@ DoubleArrayGen dag; ArrayList names = new ArrayList<>(); ArrayList curr; - static boolean hasDebugInfo = true; + static boolean hasDebugInfo = false; 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][]; @@ -80,58 +90,537 @@ 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); + if (obtainedMsg.equals(expectedMsg)) return; + 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(); + if (args.length > 0) { + hasDebugInfo = true; + } + + // Test the message printed for the failed action. + t.testFailedAction(); + + // Test the method printed for the null entity. + t.testNullEntity(); + + // Test that no message is printed for exceptions + // allocated explicitly. + t.testCreation(); + + // Test that no message is printed for exceptions + // thrown in native methods. + t.testNative(); + + // Test that two calls to getMessage() return the same + // message. + // It is a design decision that it returns two different + // String objects. 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(); + + // Test serialization. + // It is a design decision that after serialization the + // the message is lost. + t.testSerialization(); + + // Test that messages are printed for code generated + // on-the-fly. + t.testGeneratedCode(); + + + // Some more interesting complex messages. + t.testComplexMessages(); + } + + // Helper method to cause test case. + private double callWithTypes(String[][] dummy1, int[][][] dummy2, float dummy3, long dummy4, short dummy5, + boolean dummy6, byte dummy7, double dummy8, char dummy9) { + return 0.0; } + public void testFailedAction() { + int[] ia1 = null; + float[] fa1 = null; + Object[] oa1 = null; + boolean[] za1 = null; + byte[] ba1 = null; + char[] ca1 = null; + short[] sa1 = null; + long[] la1 = null; + double[] da1 = null; + + // iaload + try { + System.out.println(ia1[0]); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("ia1[0]", e.getMessage(), + (hasDebugInfo ? "'ia1'" : "''") + " is null. " + + "Can not load from null int array."); + } + // faload + try { + System.out.println(fa1[0]); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("fa1[0]", e.getMessage(), + (hasDebugInfo ? "'fa1'" : "''") + " is null. " + + "Can not load from null float array."); + } + // aaload + try { + System.out.println(oa1[0]); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("oa1[0]", e.getMessage(), + (hasDebugInfo ? "'oa1'" : "''") + " is null. " + + "Can not load from null object array."); + } + // baload (boolean) + try { + System.out.println(za1[0]); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("za1[0]", e.getMessage(), + (hasDebugInfo ? "'za1'" : "''") + " is null. " + + "Can not load from null byte/boolean array."); + } + // baload (byte) + try { + System.out.println(ba1[0]); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("ba1[0]", e.getMessage(), + (hasDebugInfo ? "'ba1'" : "''") + " is null. " + + "Can not load from null byte/boolean array."); + } + // caload + try { + System.out.println(ca1[0]); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("ca1[0]", e.getMessage(), + (hasDebugInfo ? "'ca1'" : "''") + " is null. " + + "Can not load from null char array."); + } + // saload + try { + System.out.println(sa1[0]); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("sa1[0]", e.getMessage(), + (hasDebugInfo ? "'sa1'" : "''") + " is null. " + + "Can not load from null short array."); + } + // laload + try { + System.out.println(la1[0]); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("la1[0]", e.getMessage(), + (hasDebugInfo ? "'la1'" : "''") + " is null. " + + "Can not load from null long array."); + } + // daload + try { + System.out.println(da1[0]); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("da1[0]", e.getMessage(), + (hasDebugInfo ? "'da1'" : "''") + " is null. " + + "Can not load from null double array."); + } + + // iastore + try { + System.out.println(ia1[0] = 0); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("ia1[0] = 0", e.getMessage(), + (hasDebugInfo ? "'ia1'" : "''") + " is null. " + + "Can not store to null int array."); + } + // fastore + try { + System.out.println(fa1[0] = 0.7f); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("fa1[0] = false", e.getMessage(), + (hasDebugInfo ? "'fa1'" : "''") + " is null. " + + "Can not store to null float array."); + } + // aastore + try { + System.out.println(oa1[0] = null); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("oa1[0] = null", e.getMessage(), + (hasDebugInfo ? "'oa1'" : "''") + " is null. " + + "Can not store to null object array."); + } + // bastore (boolean) + try { + System.out.println(za1[0] = false); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("za1[0] = false", e.getMessage(), + (hasDebugInfo ? "'za1'" : "''") + " is null. " + + "Can not store to null byte/boolean array."); + } + // bastore (byte) + try { + System.out.println(ba1[0] = 0); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("ba1[0] = 0", e.getMessage(), + (hasDebugInfo ? "'ba1'" : "''") + " is null. " + + "Can not store to null byte/boolean array."); + } + // castore + try { + System.out.println(ca1[0] = 0); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("ca1[0] = 0", e.getMessage(), + (hasDebugInfo ? "'ca1'" : "''") + " is null. " + + "Can not store to null char array."); + } + // sastore + try { + System.out.println(sa1[0] = 0); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("sa1[0] = 0", e.getMessage(), + (hasDebugInfo ? "'sa1'" : "''") + " is null. " + + "Can not store to null short array."); + } + // lastore + try { + System.out.println(la1[0] = 0); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("la1[0] = 0", e.getMessage(), + (hasDebugInfo ? "'la1'" : "''") + " is null. " + + "Can not store to null long array."); + } + // dastore + try { + System.out.println(da1[0] = 0); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("da1[0] = 0", e.getMessage(), + (hasDebugInfo ? "'da1'" : "''") + " is null. " + + "Can not store to null double array."); + } + + // arraylength + try { + System.out.println(za1.length); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("za1.length", e.getMessage(), + (hasDebugInfo ? "'za1'" : "''") + " is null. " + + "Can not read the array length."); + } + // athrow + try { + throw null; + } catch (NullPointerException e) { + checkMessage("throw null", e.getMessage(), + "'null' is null. " + + "Can not throw a null exception object."); + } + // monitorenter + try { + synchronized (nullInstanceField) { + // desired + } + } catch (NullPointerException e) { + checkMessage("synchronized (nullInstanceField)", e.getMessage(), + "'this.nullInstanceField' is null. " + + "Can not enter a null monitor."); + } + // monitorexit + // No test available + + // getfield + try { + System.out.println(nullInstanceField.nullInstanceField); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("nullInstanceField.nullInstanceField", e.getMessage(), + "'this.nullInstanceField' is null. " + + "Can not read field 'nullInstanceField'."); + } + // putfield + try { + System.out.println(nullInstanceField.nullInstanceField = null); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("nullInstanceField.nullInstanceField = null", e.getMessage(), + "'this.nullInstanceField' is null. " + + "Can not write field 'nullInstanceField'."); + } + // invoke + try { + nullInstanceField.toString(); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("nullInstanceField.toString()", e.getMessage(), + "'this.nullInstanceField' is null. " + + "Can not invoke method 'java.lang.String java.lang.Object.toString()'."); + } + // Test parameter and return types + try { + Asserts.assertTrue(nullInstanceField.callWithTypes(null, null, 0.0f, 0L, (short)0, false, (byte)0, 0.0, 'x') == 0.0); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("nullInstanceField.callWithTypes(null, null, 0.0f, 0L, (short)0, false, (byte)0, 0.0, 'x')", e.getMessage(), + "'this.nullInstanceField' is null. " + + "Can not invoke method 'double NullPointerExceptionTest.callWithTypes(java.lang.String[][], int[][][], float, long, short, boolean, byte, double, char)'."); + } + } + + static void test_iload() { + int i0 = 0; + int i1 = 1; + int i2 = 2; + int i3 = 3; + int i4 = 4; + int i5 = 5; + + int[][] a = new int[6][]; + + // iload_0 + try { + a[i0][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[i0][0]", e.getMessage(), + (hasDebugInfo ? "'a[i0]'" : "'[]'") + " is null. " + + "Can not store to null int array."); + } + // iload_1 + try { + a[i1][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[i1][0]", e.getMessage(), + (hasDebugInfo ? "'a[i1]'" : "'[]'") + " is null. " + + "Can not store to null int array."); + } + // iload_2 + try { + a[i2][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[i2][0]", e.getMessage(), + (hasDebugInfo ? "'a[i2]'" : "'[]'") + " is null. " + + "Can not store to null int array."); + } + // iload_3 + try { + a[i3][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[i3][0]", e.getMessage(), + (hasDebugInfo ? "'a[i3]'" : "'[]'") + " is null. " + + "Can not store to null int array."); + } + // iload + try { + a[i5][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[i5][0]", e.getMessage(), + (hasDebugInfo ? "'a[i5]'" : "'[]'") + " is null. " + + "Can not store to null int array."); + } + } + + // Other datatyes than int are not needed. + // If we implement l2d and similar bytecodes, we can print + // long expressions as array indexes. Then these here could + // be used. + static void test_lload() { + long l0 = 0L; + long l1 = 1L; + long l2 = 2L; + long l3 = 3L; + long l4 = 4L; + long l5 = 5L; + + int[][] a = new int[6][]; + + // lload_0 + try { + a[(int)l0][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)l0][0]", e.getMessage(), + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null. " + + "Can not store to null int array."); + } + // lload_1 + try { + a[(int)l1][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)l1][0]", e.getMessage(), + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null. " + + "Can not store to null int array."); + } + // lload_2 + try { + a[(int)l2][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)l2][0]", e.getMessage(), + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null. " + + "Can not store to null int array."); + } + // lload_3 + try { + a[(int)l3][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)l3][0]", e.getMessage(), + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null. " + + "Can not store to null int array."); + } + // lload + try { + a[(int)l5][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)l5][0]", e.getMessage(), + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null. " + + "Can not store to null int array."); + } + } + + static void test_fload() { + float f0 = 0.0f; + float f1 = 1.0f; + float f2 = 2.0f; + float f3 = 3.0f; + float f4 = 4.0f; + float f5 = 5.0f; + + int[][] a = new int[6][]; + + // fload_0 + try { + a[(int)f0][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)f0][0]", e.getMessage(), + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null. " + + "Can not store to null int array."); + } + // fload_1 + try { + a[(int)f1][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)f1][0]", e.getMessage(), + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null. " + + "Can not store to null int array."); + } + // fload_2 + try { + a[(int)f2][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)f2][0]", e.getMessage(), + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null. " + + "Can not store to null int array."); + } + // fload_3 + try { + a[(int)f3][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)f3][0]", e.getMessage(), + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null. " + + "Can not store to null int array."); + } + // fload + try { + a[(int)f5][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)f5][0]", e.getMessage(), + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null. " + + "Can not store to null int array."); + } + } + + static void test_aload() { + F f0 = null; + F f1 = null; + F f2 = null; + F f3 = null; + F f4 = null; + F f5 = null; + + // aload_0 + try { + f0.i = 33; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("f0.i", e.getMessage(), + (hasDebugInfo ? "'f0'" : "''") + " is null. " + + "Can not write field 'i'."); + } + // aload_1 + try { + f1.i = 33; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("f1.i", e.getMessage(), + (hasDebugInfo ? "'f1'" : "''") + " is null. " + + "Can not write field 'i'."); + } + // aload_2 + try { + f2.i = 33; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("f2.i", e.getMessage(), + (hasDebugInfo ? "'f2'" : "''") + " is null. " + + "Can not write field 'i'."); + } + // aload_3 + try { + f3.i = 33; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("f3.i", e.getMessage(), + (hasDebugInfo ? "'f3'" : "''") + " is null. " + + "Can not write field 'i'."); + } + // aload + try { + f5.i = 33; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("f5.i", e.getMessage(), + (hasDebugInfo ? "'f5'" : "''") + " is null. " + + "Can not write field 'i'."); + } + } + + // Helper class for test cases. class A { public B to_b; public B getB() { return to_b; } } + // Helper class for test cases. class B { public C to_c; public B to_b; @@ -139,16 +628,83 @@ public B getBfromB() { return to_b; } } + // Helper class for test cases. class C { public D to_d; public D getD() { return to_d; } } + // Helper class for test cases. class D { public int num; public int[][] ar; } + + public void testArrayChasing() { + int[][][][][][] a = null; + try { + a[0][0][0][0][0][0] = 99; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("int[0][0][0][0][0] = 99 // a is null", e.getMessage(), + (hasDebugInfo ? "'a'" : "''") + " is null. " + + "Can not load from null object array."); + } + a = new int[1][][][][][]; + try { + a[0][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(), + (hasDebugInfo ? "'a[0]'" : "'[0]'") + " is null. " + + "Can not load from null object array."); + } + a[0] = new int[1][][][][]; + try { + a[0][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(), + (hasDebugInfo ? "'a[0][0]'" : "'[0][0]'") + " is null. " + + "Can not load from null object array."); + } + a[0][0] = new int[1][][][]; + try { + a[0][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(), + (hasDebugInfo ? "'a[0][0][0]'" : "'[0][0][0]'") + " is null. " + + "Can not load from null object array."); + } + a[0][0][0] = new int[1][][]; + try { + a[0][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(), + (hasDebugInfo ? "'a[0][0][0][0]'" : "'[0][0][0][0]'") + " is null. " + + "Can not load from null object array."); + } + a[0][0][0][0] = new int[1][]; + // Reaching max recursion depth. Prints . + try { + a[0][0][0][0][0][0] = 99; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("int[0][0][0][0][0] = 99 // a[0][0][0][0][0] is null", e.getMessage(), + "'[0][0][0][0][0]' is null. " + + "Can not store to null int array."); + } + a[0][0][0][0][0] = new int[1]; + try { + a[0][0][0][0][0][0] = 99; + } catch (NullPointerException e) { + Asserts.fail(); + } + } + public void testPointerChasing() { A a = null; try { @@ -156,8 +712,8 @@ 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")); + (hasDebugInfo ? "'a'" : "''") + " is null. " + + "Can not read field 'to_b'."); } a = new A(); try { @@ -165,8 +721,8 @@ 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")); + (hasDebugInfo ? "'a.to_b'" : "'.to_b'") + " is null. " + + "Can not read field 'to_c'."); } a.to_b = new B(); try { @@ -174,7 +730,8 @@ 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"); + (hasDebugInfo ? "'a.to_b.to_c'" : "'.to_b.to_c'") + " is null. " + + "Can not read field 'to_d'."); } a.to_b.to_c = new C(); try { @@ -182,59 +739,8 @@ 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(); + (hasDebugInfo ? "'a.to_b.to_c.to_d'" : "'.to_b.to_c.to_d'") + " is null. " + + "Can not write field 'num'."); } } @@ -245,8 +751,8 @@ 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")); + (hasDebugInfo ? "'a" : "'") + "' is null. " + + "Can not invoke method 'NullPointerExceptionTest$B NullPointerExceptionTest$A.getB()'."); } a = new A(); try { @@ -254,7 +760,8 @@ 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;'"); + "The return value of 'NullPointerExceptionTest$B NullPointerExceptionTest$A.getB()' is null. " + + "Can not invoke method 'NullPointerExceptionTest$B NullPointerExceptionTest$B.getBfromB()'."); } a.to_b = new B(); try { @@ -262,7 +769,8 @@ 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;'"); + "The return value of 'NullPointerExceptionTest$B NullPointerExceptionTest$B.getBfromB()' is null. " + + "Can not invoke method 'NullPointerExceptionTest$C NullPointerExceptionTest$B.getC()'."); } a.to_b.to_b = new B(); try { @@ -270,7 +778,8 @@ 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;'"); + "The return value of 'NullPointerExceptionTest$C NullPointerExceptionTest$B.getC()' is null. " + + "Can not invoke method 'NullPointerExceptionTest$D NullPointerExceptionTest$C.getD()'."); } a.to_b.to_b.to_c = new C(); try { @@ -278,7 +787,8 @@ 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;'"); + "The return value of 'NullPointerExceptionTest$D NullPointerExceptionTest$C.getD()' is null. " + + "Can not write field 'num'."); } } @@ -289,8 +799,8 @@ 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")); + (hasDebugInfo ? "'a'" : "''") + " is null. " + + "Can not invoke method 'NullPointerExceptionTest$B NullPointerExceptionTest$A.getB()'."); } a = new A(); try { @@ -298,7 +808,8 @@ 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;'"); + "The return value of 'NullPointerExceptionTest$B NullPointerExceptionTest$A.getB()' is null. " + + "Can not invoke method 'NullPointerExceptionTest$B NullPointerExceptionTest$B.getBfromB()'."); } a.to_b = new B(); try { @@ -306,7 +817,8 @@ 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;'"); + "The return value of 'NullPointerExceptionTest$B NullPointerExceptionTest$B.getBfromB()' is null. " + + "Can not read field 'to_c'."); } a.to_b.to_b = new B(); try { @@ -314,7 +826,8 @@ 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;'"); + "'NullPointerExceptionTest$B NullPointerExceptionTest$B.getBfromB().to_c' is null. " + + "Can not read field 'to_d'."); } a.to_b.to_b.to_c = new C(); try { @@ -322,7 +835,8 @@ 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"); + "'NullPointerExceptionTest$B NullPointerExceptionTest$B.getBfromB().to_c.to_d' is null. " + + "Can not read field 'ar'."); } a.to_b.to_b.to_c.to_d = new D(); try { @@ -330,14 +844,16 @@ 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"); + "'NullPointerExceptionTest$B NullPointerExceptionTest$B.getBfromB().to_c.to_d.ar' is null. " + + "Can not load from null object array."); } 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;'"); + "'NullPointerExceptionTest$D NullPointerExceptionTest$C.getD().ar' is null. " + + "Can not load from null object array."); } a.to_b.to_b.to_c.to_d.ar = new int[1][]; try { @@ -345,25 +861,255 @@ 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"); + "'NullPointerExceptionTest$B NullPointerExceptionTest$B.getBfromB().to_c.to_d.ar[0]' is null. " + + "Can not store to null int array."); } 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"); + "'NullPointerExceptionTest$D NullPointerExceptionTest$C.getD().ar[0]' is null. " + + "Can not store to null int array."); + } + } + + // Helper method to cause test case. + private Object returnNull(String[][] dummy1, int[][][] dummy2, float dummy3) { + return null; + } + + // Helper method to cause test case. + private NullPointerExceptionTest returnMeAsNull(Throwable dummy1, int dummy2, char dummy3) { + return null; + } + + // Helper interface for test cases. + static interface DoubleArrayGen { + public double[] getArray(); + } + + // Helper class for test cases. + static class DoubleArrayGenImpl implements DoubleArrayGen { + @Override + public double[] getArray() { + return null; + } + } + + // Helper class for test cases. + static class NullPointerGenerator { + public static Object nullReturner(boolean dummy1) { + return null; + } + + public Object returnMyNull(double dummy1, long dummy2, short dummy3) { + return null; + } + } + + // Helper method to cause test case. + public void ImplTestLoadedFromMethod(DoubleArrayGen gen) { + try { + (gen.getArray())[0] = 1.0; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("(gen.getArray())[0]", e.getMessage(), + "The return value of 'double[] NullPointerExceptionTest$DoubleArrayGen.getArray()' is null. Can not store to null double array."); } } + public void testNullEntity() { + int[][] a = new int[820][]; + + test_iload(); + test_lload(); + test_fload(); + // test_dload(); + test_aload(); + // aload_0: 'this' + try { + this.nullInstanceField.nullInstanceField = null; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("this.nullInstanceField.nullInstanceField = null", e.getMessage(), + "'this.nullInstanceField' is null. Can not write field 'nullInstanceField'."); + } + + // aconst_null + try { + throw null; + } catch (NullPointerException e) { + checkMessage("throw null", e.getMessage(), + "'null' is null. Can not throw a null exception object."); + } + // iconst_0 + try { + a[0][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[0][0]", e.getMessage(), + (hasDebugInfo ? "'a[0]'" : "'[0]'") + " is null. " + + "Can not store to null int array."); + } + // iconst_1 + try { + a[1][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[1][0]", e.getMessage(), + (hasDebugInfo ? "'a[1]'" : "'[1]'") + " is null. " + + "Can not store to null int array."); + } + // iconst_2 + try { + a[2][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[2][0]", e.getMessage(), + (hasDebugInfo ? "'a[2]'" : "'[2]'") + " is null. " + + "Can not store to null int array."); + } + // iconst_3 + try { + a[3][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[3][0]", e.getMessage(), + (hasDebugInfo ? "'a[3]'" : "'[3]'") + " is null. " + + "Can not store to null int array."); + } + // iconst_4 + try { + a[4][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[4][0]", e.getMessage(), + (hasDebugInfo ? "'a[4]'" : "'[4]'") + " is null. " + + "Can not store to null int array."); + } + // iconst_5 + try { + a[5][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[5][0]", e.getMessage(), + (hasDebugInfo ? "'a[5]'" : "'[5]'") + " is null. " + + "Can not store to null int array."); + } + // long --> iconst + try { + a[(int)0L][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[(int)0L][0]", e.getMessage(), + (hasDebugInfo ? "'a[0]'" : "'[0]'") + " is null. " + + "Can not store to null int array."); + } + // bipush + try { + a[139 /*0x77*/][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[139][0]", e.getMessage(), + (hasDebugInfo ? "'a[139]'" : "'[139]'") + " is null. " + + "Can not store to null int array."); + } + // sipush + try { + a[819 /*0x333*/][0] = 77; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("a[819][0]", e.getMessage(), + (hasDebugInfo ? "'a[819]'" : "'[819]'") + " is null. " + + "Can not store to null int array."); + } + + // aaload, with recursive descend. + testArrayChasing(); + + // getstatic + try { + Asserts.assertTrue(((float[]) nullStaticField)[0] == 1.0f); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("((float[]) nullStaticField)[0]", e.getMessage(), + "'static NullPointerExceptionTest.nullStaticField' is null. Can not load from null float array."); + } + + // getfield, with recursive descend. + testPointerChasing(); + + // invokestatic + try { + Asserts.assertTrue(((char[]) NullPointerGenerator.nullReturner(false))[0] == 'A'); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("((char[]) NullPointerGenerator.nullReturner(false))[0]", e.getMessage(), + "The return value of 'java.lang.Object NullPointerExceptionTest$NullPointerGenerator.nullReturner(boolean)' is null. Can not load from null char array."); + } + // invokevirtual + 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(), + "The return value of 'java.lang.Object NullPointerExceptionTest$NullPointerGenerator.returnMyNull(double, long, short)' is null. Can not load from null char array."); + } + // Call with array arguments. + 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(), + "The return value of 'java.lang.Object NullPointerExceptionTest.returnNull(java.lang.String[][], int[][][], float)' is null. Can not load from null double array."); + } + // invokeinterface + ImplTestLoadedFromMethod(new DoubleArrayGenImpl()); + try { + returnMeAsNull(null, 1, 'A').dag = null; + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage("returnMeAsNull(null, 1, 'A').dag = null", e.getMessage(), + "The return value of 'NullPointerExceptionTest NullPointerExceptionTest.returnMeAsNull(java.lang.Throwable, int, char)' is null. Can not write field 'dag'."); + } + testMethodChasing(); + + // Mixed recursive descend. + testMixedChasing(); + + } + + + public void testCreation() throws Exception { + // If allocated with new, the message should not be generated. + Asserts.assertNull(new NullPointerException().getMessage()); + String msg = new String("A pointless message."); + Asserts.assertTrue(new NullPointerException(msg).getMessage() == msg); + + // If created via reflection, the message should not be generated. + Exception ex = NullPointerException.class.getDeclaredConstructor().newInstance(); + Asserts.assertNull(ex.getMessage()); + } + + public void testNative() throws Exception { + // If NPE is thrown in a native method, the message should + // not be generated. + try { + Class.forName(null); + Asserts.fail(); + } catch (NullPointerException e) { + Asserts.assertNull(e.getMessage()); + } + + } // 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"); + (hasDebugInfo ? "'null_o" : "'") + "' is null. " + + "Can not invoke method 'int java.lang.Object.hashCode()'."; try { null_o.hashCode(); @@ -374,30 +1120,13 @@ 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. + // message anew on every call, so this does not hold. + //Asserts.assertTrue(msg1 == msg2); 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 { + public void testSerialization() throws Exception { // NPE without message. Object o1 = new NullPointerException(); ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); @@ -429,9 +1158,9 @@ } 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")); + checkMessage("null_o3.hashCode()", msg3, + (hasDebugInfo ? "'null_o3'" : "''") + " is null. " + + "Can not invoke method 'int java.lang.Object.hashCode()'."); } ByteArrayOutputStream bos3 = new ByteArrayOutputStream(); ObjectOutputStream oos3 = new ObjectOutputStream(bos3); @@ -446,791 +1175,124 @@ 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() { + public void testComplexMessages() { 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"); + checkMessage("staticLongArray[0][0] = 2L", e.getMessage(), + "'static NullPointerExceptionTest.staticLongArray[0]' is null. " + + "Can not store to null long array."); } - } - /** - * - */ - 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'"); + checkMessage("this.nullInstanceField.nullInstanceField", e.getMessage(), + "'this.nullInstanceField' is null. " + + "Can not read field 'nullInstanceField'."); } - } - - /** - * - */ - 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 { + NullPointerExceptionTest obj = this; 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")); + checkMessage("obj.dag.getArray().clone()", e.getMessage(), + (hasDebugInfo ? "'obj" : "'") + ".dag' is null. " + + "Can not invoke method 'double[] NullPointerExceptionTest$DoubleArrayGen.getArray()'."); } - } - - /** - * - */ - public void testLoadedFromInstanceField4() { - int indexes[] = new int[1]; - - NullPointerExceptionTest[] objs = new NullPointerExceptionTest[] {this}; - try { + int indexes[] = new int[1]; + NullPointerExceptionTest[] objs = new NullPointerExceptionTest[] {this}; 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"); + checkMessage("objs[indexes[0]].nullInstanceField.returnNull(null, null, 1f)", e.getMessage(), + (hasDebugInfo ? "'objs[indexes" : "'[") + "[0]].nullInstanceField' is null. " + + "Can not invoke method 'java.lang.Object NullPointerExceptionTest.returnNull(java.lang.String[][], int[][][], float)'."); } - } - - /** - * - */ - 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. + int indexes[] = new int[1]; + NullPointerExceptionTest[][] objs = + new NullPointerExceptionTest[][] {new NullPointerExceptionTest[] {this}}; 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) { - // GLGLGL not for now Asserts.assertEquals(names.get(i), expectedNames[i]); - } - } - - private void doTestMissingLocalVariableTable(ArrayList 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()); + checkMessage("synchronized (objs[indexes[0]][0].nullInstanceField)", e.getMessage(), + (hasDebugInfo ? "'objs[indexes" : "'[" ) + "[0]][0].nullInstanceField' is null. " + + "Can not enter a null monitor."); } } - /** - * - */ - @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")); - } + // Generates: + // class E implements E0 { + // public int throwNPE(F f) { + // return f.i; + // } + // } + static byte[] generateTestClass() { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; - 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")); - } + cw.visit(57, ACC_SUPER, "E", null, "java/lang/Object", new String[] { "E0" }); - 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")); + { + mv = cw.visitMethod(0, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); } - 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")); + { + mv = cw.visitMethod(ACC_PUBLIC, "throwNPE", "(LF;)I", null, null); + mv.visitCode(); + Label label0 = new Label(); + mv.visitLabel(label0); + mv.visitLineNumber(118, label0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(GETFIELD, "F", "i", "I"); + mv.visitInsn(IRETURN); + Label label1 = new Label(); + mv.visitLabel(label1); + mv.visitLocalVariable("this", "LE;", null, label0, label1, 0); + mv.visitLocalVariable("f", "LE;", null, label0, label1, 1); + mv.visitMaxs(1, 2); + mv.visitEnd(); } - - 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")); - } + cw.visitEnd(); - 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")); - } + return cw.toByteArray(); + } - 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")); - } - + // Tests that a class generated on the fly is handled properly. + public void testGeneratedCode() throws Exception { + byte[] classBytes = generateTestClass(); + Lookup lookup = lookup(); + Class clazz = lookup.defineClass(classBytes); + E0 e = (E0) clazz.getDeclaredConstructor().newInstance(); 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"); + e.throwNPE(null); + } catch (NullPointerException ex) { + checkMessage("return f.i;", + ex.getMessage(), + "'f' is null. Can not read field 'i'."); } } } + +// Helper interface for test cases needed for generateTestClass(). +interface E0 { + public int throwNPE(F f); +} + +// Helper class for test cases needed for generateTestClass(). +class F { + int i; +}