# HG changeset patch # User goetz # Date 1523884640 -7200 # Mon Apr 16 15:17:20 2018 +0200 # Node ID b326ee9d3bd59f1b5769e6f6209154b3c14e3998 # Parent 37b2446d7f86789658a1a485f15300a5184a0a07 8201593: Print array length in ArrayIndexOutOfBoundsException. Reviewed-by: dholmes, mdoerr, smonteith diff --git a/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp --- a/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp @@ -48,11 +48,14 @@ __ b(_continuation); } -RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, - bool throw_index_out_of_bounds_exception) - : _throw_index_out_of_bounds_exception(throw_index_out_of_bounds_exception) - , _index(index) -{ +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, LIR_Opr array) + : _throw_index_out_of_bounds_exception(false), _index(index), _array(array) { + assert(info != NULL, "must have info"); + _info = new CodeEmitInfo(info); +} + +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index) + : _throw_index_out_of_bounds_exception(true), _index(index), _array(NULL) { assert(info != NULL, "must have info"); _info = new CodeEmitInfo(info); } @@ -69,14 +72,16 @@ } if (_index->is_cpu_register()) { - __ mov(rscratch1, _index->as_register()); + __ mov(r22, _index->as_register()); } else { - __ mov(rscratch1, _index->as_jint()); + __ mov(r22, _index->as_jint()); } Runtime1::StubID stub_id; if (_throw_index_out_of_bounds_exception) { stub_id = Runtime1::throw_index_exception_id; } else { + assert(_array != NULL, "sanity"); + __ mov(r23, _array->as_pointer_register()); stub_id = Runtime1::throw_range_check_failed_id; } __ far_call(RuntimeAddress(Runtime1::entry_for(stub_id)), NULL, rscratch2); diff --git a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp --- a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp @@ -323,7 +323,7 @@ // target: the entry point of the method that creates and posts the exception oop -// has_argument: true if the exception needs an argument (passed in rscratch1) +// has_argument: true if the exception needs arguments (passed in r22 and r23) OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, bool has_argument) { // make a frame and preserve the caller's caller-save registers @@ -332,7 +332,7 @@ if (!has_argument) { call_offset = __ call_RT(noreg, noreg, target); } else { - call_offset = __ call_RT(noreg, noreg, target, rscratch1); + call_offset = __ call_RT(noreg, noreg, target, r22, r23); } OopMapSet* oop_maps = new OopMapSet(); oop_maps->add_gc_map(call_offset, oop_map); diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -333,16 +333,17 @@ return entry; } -address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler( - const char* name) { +address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler() { address entry = __ pc(); // expression stack must be empty before entering the VM if an // exception happened __ empty_expression_stack(); // setup parameters + // ??? convention: expect aberrant index in register r1 __ movw(c_rarg2, r1); - __ mov(c_rarg1, (address)name); + // ??? convention: expect array in register r3 + __ mov(c_rarg1, r3); __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime:: diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -747,6 +747,8 @@ } Label ok; __ br(Assembler::LO, ok); + // ??? convention: move array into r3 for exception message + __ mov(r3, array); __ mov(rscratch1, Interpreter::_throw_ArrayIndexOutOfBoundsException_entry); __ br(rscratch1); __ bind(ok); diff --git a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp --- a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp +++ b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp @@ -50,14 +50,18 @@ // TODO: ARM - is it possible to inline these stubs into the main code stream? -RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, - bool throw_index_out_of_bounds_exception) - : _throw_index_out_of_bounds_exception(throw_index_out_of_bounds_exception) - , _index(index) -{ - _info = info == NULL ? NULL : new CodeEmitInfo(info); + +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, LIR_Opr array) + : _throw_index_out_of_bounds_exception(false), _index(index), _array(array) { + assert(info != NULL, "must have info"); + _info = new CodeEmitInfo(info); } +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index) + : _throw_index_out_of_bounds_exception(true), _index(index), _array(NULL) { + assert(info != NULL, "must have info"); + _info = new CodeEmitInfo(info); +} void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); @@ -73,7 +77,7 @@ return; } // Pass the array index on stack because all registers must be preserved - ce->verify_reserved_argument_area_size(1); + ce->verify_reserved_argument_area_size(_throw_index_out_of_bounds_exception ? 1 : 2); if (_index->is_cpu_register()) { __ str_32(_index->as_register(), Address(SP)); } else { @@ -87,6 +91,7 @@ #endif __ call(Runtime1::entry_for(Runtime1::throw_index_exception_id), relocInfo::runtime_call_type); } else { + __ str(_array->as_pointer_register(), Address(SP, BytesPerWord)); // ??? Correct offset? Correct instruction? __ call(Runtime1::entry_for(Runtime1::throw_range_check_failed_id), relocInfo::runtime_call_type); } ce->add_call_info_here(_info); diff --git a/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp b/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp --- a/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp +++ b/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp @@ -366,11 +366,15 @@ OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, bool has_argument) { OopMap* oop_map = save_live_registers(sasm); + int call_offset; if (has_argument) { __ ldr(R1, Address(SP, arg1_offset)); + __ ldr(R2, Address(SP, arg2_offset)); + call_offset = __ call_RT(noreg, noreg, target, R1, R2); + } else { + call_offset = __ call_RT(noreg, noreg, target); } - int call_offset = __ call_RT(noreg, noreg, target); OopMapSet* oop_maps = new OopMapSet(); oop_maps->add_gc_map(call_offset, oop_map); diff --git a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp --- a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -185,18 +185,16 @@ return entry; } -address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler(const char* name) { +address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler() { address entry = __ pc(); // index is in R4_ArrayIndexOutOfBounds_index - InlinedString Lname(name); - // expression stack must be empty before entering the VM if an exception happened __ empty_expression_stack(); // setup parameters - __ ldr_literal(R1, Lname); + // Array expected in R1. __ mov(R2, R4_ArrayIndexOutOfBounds_index); __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException), R1, R2); @@ -204,7 +202,6 @@ __ nop(); // to avoid filling CPU pipeline with invalid instructions __ nop(); __ should_not_reach_here(); - __ bind_literal(Lname); return entry; } diff --git a/src/hotspot/cpu/arm/templateTable_arm.cpp b/src/hotspot/cpu/arm/templateTable_arm.cpp --- a/src/hotspot/cpu/arm/templateTable_arm.cpp +++ b/src/hotspot/cpu/arm/templateTable_arm.cpp @@ -746,6 +746,7 @@ // convention with generate_ArrayIndexOutOfBounds_handler() __ mov(R4_ArrayIndexOutOfBounds_index, index, hs); } + __ mov(R1, array, hs); __ b(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry, hs); } diff --git a/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp b/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp --- a/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2015 SAP SE. All rights reserved. + * Copyright (c) 2012, 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,10 +37,14 @@ #define __ ce->masm()-> -RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, - bool throw_index_out_of_bounds_exception) - : _throw_index_out_of_bounds_exception(throw_index_out_of_bounds_exception) - , _index(index) { +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, LIR_Opr array) + : _throw_index_out_of_bounds_exception(false), _index(index), _array(array) { + assert(info != NULL, "must have info"); + _info = new CodeEmitInfo(info); +} + +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index) + : _throw_index_out_of_bounds_exception(true), _index(index), _array(NULL) { assert(info != NULL, "must have info"); _info = new CodeEmitInfo(info); } @@ -68,12 +72,16 @@ __ add_const_optimized(R0, R29_TOC, MacroAssembler::offset_to_global_toc(stub)); __ mtctr(R0); - Register index = R0; // pass in R0 + Register index = R0; if (_index->is_register()) { __ extsw(index, _index->as_register()); } else { __ load_const_optimized(index, _index->as_jint()); } + if (_array) { + __ std(_array->as_pointer_register(), -8, R1_SP); + } + __ std(index, -16, R1_SP); __ bctrl(); ce->add_call_info_here(_info); diff --git a/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp b/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp --- a/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2015 SAP SE. All rights reserved. + * Copyright (c) 2012, 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -502,8 +502,7 @@ case throw_range_check_failed_id: { __ set_info("range_check_failed", dont_gc_arguments); // Arguments will be discarded. - __ std(R0, -8, R1_SP); // Pass index on stack. - oop_maps = generate_exception_throw_with_stack_parms(sasm, CAST_FROM_FN_PTR(address, throw_range_check_exception), 1); + oop_maps = generate_exception_throw_with_stack_parms(sasm, CAST_FROM_FN_PTR(address, throw_range_check_exception), 2); } break; diff --git a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp --- a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp @@ -564,13 +564,13 @@ return entry; } -address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler(const char* name) { +address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler() { address entry = __ pc(); __ empty_expression_stack(); - __ load_const_optimized(R4_ARG2, (address) name); + // R4_ARG2 already contains the array. // Index is in R17_tos. __ mr(R5_ARG3, R17_tos); - __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException)); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException), R4_ARG2, R5_ARG3); return entry; } diff --git a/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp b/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp --- a/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp +++ b/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016 SAP SE. All rights reserved. + * Copyright (c) 2016, 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,10 +39,14 @@ #undef CHECK_BAILOUT #define CHECK_BAILOUT() { if (ce->compilation()->bailed_out()) return; } -RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, - bool throw_index_out_of_bounds_exception) : - _throw_index_out_of_bounds_exception(throw_index_out_of_bounds_exception), - _index(index) { +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, LIR_Opr array) + : _throw_index_out_of_bounds_exception(false), _index(index), _array(array) { + assert(info != NULL, "must have info"); + _info = new CodeEmitInfo(info); +} + +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index) + : _throw_index_out_of_bounds_exception(true), _index(index), _array(NULL) { assert(info != NULL, "must have info"); _info = new CodeEmitInfo(info); } @@ -71,6 +75,7 @@ stub_id = Runtime1::throw_index_exception_id; } else { stub_id = Runtime1::throw_range_check_failed_id; + __ lgr_if_needed(Z_R0_scratch, _array->as_pointer_register()); } ce->emit_call_c(Runtime1::entry_for (stub_id)); CHECK_BAILOUT(); diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2017 SAP SE. All rights reserved. + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -314,8 +314,8 @@ __ save_return_pc(return_pc); // Push a new frame (includes stack linkage). - // use return_pc as scratch for push_frame. Z_R0_scratch (the default) and Z_R1_scratch are - // illegally used to pass parameters (SAPJVM extension) by RangeCheckStub::emit_code(). + // Use return_pc as scratch for push_frame. Z_R0_scratch (the default) and Z_R1_scratch are + // illegally used to pass parameters by RangeCheckStub::emit_code(). __ push_frame(frame_size_in_bytes, return_pc); // We have to restore return_pc right away. // Nobody else will. Furthermore, return_pc isn't necessarily the default (Z_R14). diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp @@ -551,9 +551,10 @@ // // Args: +// Z_ARG2: oop of array // Z_ARG3: aberrant index // -address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler(const char * name) { +address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler() { address entry = __ pc(); address excp = CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException); @@ -562,8 +563,7 @@ __ empty_expression_stack(); // Setup parameters. - // Leave out the name and use register for array to create more detailed exceptions. - __ load_absolute_address(Z_ARG2, (address) name); + // Pass register with array to create more detailed exceptions. __ call_VM(noreg, excp, Z_ARG2, Z_ARG3); return entry; } diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -784,7 +784,7 @@ __ z_cl(index, Address(array, arrayOopDesc::length_offset_in_bytes())); __ z_brl(index_ok); __ lgr_if_needed(Z_ARG3, index); // See generate_ArrayIndexOutOfBounds_handler(). - // Give back the array to create more detailed exceptions. + // Pass the array to create more detailed exceptions. __ lgr_if_needed(Z_ARG2, array); // See generate_ArrayIndexOutOfBounds_handler(). __ load_absolute_address(Z_R1_scratch, Interpreter::_throw_ArrayIndexOutOfBoundsException_entry); diff --git a/src/hotspot/cpu/sparc/c1_CodeStubs_sparc.cpp b/src/hotspot/cpu/sparc/c1_CodeStubs_sparc.cpp --- a/src/hotspot/cpu/sparc/c1_CodeStubs_sparc.cpp +++ b/src/hotspot/cpu/sparc/c1_CodeStubs_sparc.cpp @@ -35,15 +35,17 @@ #define __ ce->masm()-> -RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, - bool throw_index_out_of_bounds_exception) - : _throw_index_out_of_bounds_exception(throw_index_out_of_bounds_exception) - , _index(index) -{ +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, LIR_Opr array) + : _throw_index_out_of_bounds_exception(false), _index(index), _array(array) { assert(info != NULL, "must have info"); _info = new CodeEmitInfo(info); } +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index) + : _throw_index_out_of_bounds_exception(true), _index(index), _array(NULL) { + assert(info != NULL, "must have info"); + _info = new CodeEmitInfo(info); +} void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); @@ -66,6 +68,7 @@ if (_throw_index_out_of_bounds_exception) { __ call(Runtime1::entry_for(Runtime1::throw_index_exception_id), relocInfo::runtime_call_type); } else { + __ mov(_array->as_pointer_register(), G5); __ call(Runtime1::entry_for(Runtime1::throw_range_check_failed_id), relocInfo::runtime_call_type); } __ delayed()->nop(); diff --git a/src/hotspot/cpu/sparc/c1_Runtime1_sparc.cpp b/src/hotspot/cpu/sparc/c1_Runtime1_sparc.cpp --- a/src/hotspot/cpu/sparc/c1_Runtime1_sparc.cpp +++ b/src/hotspot/cpu/sparc/c1_Runtime1_sparc.cpp @@ -302,7 +302,7 @@ if (!has_argument) { call_offset = __ call_RT(noreg, noreg, target); } else { - call_offset = __ call_RT(noreg, noreg, target, G4); + call_offset = __ call_RT(noreg, noreg, target, G4, G5); } OopMapSet* oop_maps = new OopMapSet(); oop_maps->add_gc_map(call_offset, oop_map); diff --git a/src/hotspot/cpu/sparc/interp_masm_sparc.cpp b/src/hotspot/cpu/sparc/interp_masm_sparc.cpp --- a/src/hotspot/cpu/sparc/interp_masm_sparc.cpp +++ b/src/hotspot/cpu/sparc/interp_masm_sparc.cpp @@ -879,27 +879,32 @@ assert_not_delayed(); verify_oop(array); - // sign extend since tos (index) can be a 32bit value + // Sign extend since tos (index) can be a 32bit value. sra(index, G0, index); - // check array + // Check array. Label ptr_ok; tst(array); - throw_if_not_1_x( notZero, ptr_ok ); - delayed()->ld( array, arrayOopDesc::length_offset_in_bytes(), tmp ); // check index - throw_if_not_2( Interpreter::_throw_NullPointerException_entry, G3_scratch, ptr_ok); + throw_if_not_1_x(notZero, ptr_ok); + delayed()->ld(array, arrayOopDesc::length_offset_in_bytes(), tmp); // Check index. + throw_if_not_2(Interpreter::_throw_NullPointerException_entry, G3_scratch, ptr_ok); Label index_ok; cmp(index, tmp); - throw_if_not_1_icc( lessUnsigned, index_ok ); - if (index_shift > 0) delayed()->sll(index, index_shift, index); - else delayed()->add(array, index, res); // addr - const offset in index - // convention: move aberrant index into G3_scratch for exception message - mov(index, G3_scratch); - throw_if_not_2( Interpreter::_throw_ArrayIndexOutOfBoundsException_entry, G4_scratch, index_ok); + throw_if_not_1_icc(lessUnsigned, index_ok); + if (index_shift > 0) { + delayed()->sll(index, index_shift, index); + } else { + delayed()->add(array, index, res); // addr - const offset in index + } + // Pass the array to create more detailed exceptions. + // Convention: move aberrant index into Otos_i for exception message. + mov(index, Otos_i); + mov(array, G3_scratch); + throw_if_not_2(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry, G4_scratch, index_ok); // add offset if didn't do it in delay slot - if (index_shift > 0) add(array, index, res); // addr - const offset in index + if (index_shift > 0) { add(array, index, res); } // addr - const offset in index } diff --git a/src/hotspot/cpu/sparc/templateInterpreterGenerator_sparc.cpp b/src/hotspot/cpu/sparc/templateInterpreterGenerator_sparc.cpp --- a/src/hotspot/cpu/sparc/templateInterpreterGenerator_sparc.cpp +++ b/src/hotspot/cpu/sparc/templateInterpreterGenerator_sparc.cpp @@ -255,15 +255,14 @@ } -address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler(const char* name) { +address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler() { address entry = __ pc(); // expression stack must be empty before entering the VM if an exception happened __ empty_expression_stack(); + // Pass the array to create more detailed exceptions. // convention: expect aberrant index in register G3_scratch, then shuffle the // index to G4_scratch for the VM call - __ mov(G3_scratch, G4_scratch); - __ set((intptr_t)name, G3_scratch); - __ call_VM(Oexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException), G3_scratch, G4_scratch); + __ call_VM(Oexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException), G3_scratch, Otos_i); __ should_not_reach_here(); return entry; } diff --git a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp --- a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp +++ b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp @@ -88,15 +88,17 @@ __ jmp(_continuation); } -RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, - bool throw_index_out_of_bounds_exception) - : _throw_index_out_of_bounds_exception(throw_index_out_of_bounds_exception) - , _index(index) -{ +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, LIR_Opr array) + : _throw_index_out_of_bounds_exception(false), _index(index), _array(array) { assert(info != NULL, "must have info"); _info = new CodeEmitInfo(info); } +RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index) + : _throw_index_out_of_bounds_exception(true), _index(index), _array(NULL) { + assert(info != NULL, "must have info"); + _info = new CodeEmitInfo(info); +} void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); @@ -120,6 +122,7 @@ stub_id = Runtime1::throw_index_exception_id; } else { stub_id = Runtime1::throw_range_check_failed_id; + ce->store_parameter(_array->as_pointer_register(), 1); } __ call(RuntimeAddress(Runtime1::entry_for(stub_id))); ce->add_call_info_here(_info); diff --git a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp --- a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp +++ b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp @@ -606,26 +606,29 @@ } -// target: the entry point of the method that creates and posts the exception oop -// has_argument: true if the exception needs an argument (passed on stack because registers must be preserved) - +// Target: the entry point of the method that creates and posts the exception oop. +// has_argument: true if the exception needs arguments (passed on the stack because +// registers must be preserved). OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, bool has_argument) { - // preserve all registers - int num_rt_args = has_argument ? 2 : 1; + // Preserve all registers. + int num_rt_args = has_argument ? (2 + 1) : 1; OopMap* oop_map = save_live_registers(sasm, num_rt_args); - // now all registers are saved and can be used freely - // verify that no old value is used accidentally + // Now all registers are saved and can be used freely. + // Verify that no old value is used accidentally. __ invalidate_registers(true, true, true, true, true, true); - // registers used by this stub + // Registers used by this stub. const Register temp_reg = rbx; - // load argument for exception that is passed as an argument into the stub + // Load arguments for exception that are passed as arguments into the stub. if (has_argument) { #ifdef _LP64 __ movptr(c_rarg1, Address(rbp, 2*BytesPerWord)); + __ movptr(c_rarg2, Address(rbp, 3*BytesPerWord)); #else + __ movptr(temp_reg, Address(rbp, 3*BytesPerWord)); + __ push(temp_reg); __ movptr(temp_reg, Address(rbp, 2*BytesPerWord)); __ push(temp_reg); #endif // _LP64 diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp @@ -102,16 +102,16 @@ return entry; } -address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler( - const char* name) { +address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler() { address entry = __ pc(); - // expression stack must be empty before entering the VM if an - // exception happened + // The expression stack must be empty before entering the VM if an + // exception happened. __ empty_expression_stack(); - // setup parameters - // ??? convention: expect aberrant index in register ebx + + // Setup parameters. + // ??? convention: expect aberrant index in register ebx/rbx. + // Pass array to create more detailed exceptions. Register rarg = NOT_LP64(rax) LP64_ONLY(c_rarg1); - __ lea(rarg, ExternalAddress((address)name)); __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime:: diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -757,11 +757,14 @@ assert(rbx != array, "different registers"); __ movl(rbx, index); } - __ jump_cc(Assembler::aboveEqual, - ExternalAddress(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry)); + Label skip; + __ jccb(Assembler::below, skip); + // Pass array to create more detailed exceptions. + __ mov(NOT_LP64(rax) LP64_ONLY(c_rarg1), array); + __ jump(ExternalAddress(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry)); + __ bind(skip); } - void TemplateTable::iaload() { transition(itos, itos); // rax: index diff --git a/src/hotspot/share/c1/c1_CodeStubs.hpp b/src/hotspot/share/c1/c1_CodeStubs.hpp --- a/src/hotspot/share/c1/c1_CodeStubs.hpp +++ b/src/hotspot/share/c1/c1_CodeStubs.hpp @@ -147,10 +147,14 @@ private: CodeEmitInfo* _info; LIR_Opr _index; + LIR_Opr _array; bool _throw_index_out_of_bounds_exception; public: - RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, bool throw_index_out_of_bounds_exception = false); + // For ArrayIndexOutOfBoundsException. + RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, LIR_Opr array); + // For IndexOutOfBoundsException. + RangeCheckStub(CodeEmitInfo* info, LIR_Opr index); virtual void emit_code(LIR_Assembler* e); virtual CodeEmitInfo* info() const { return _info; } virtual bool is_exception_throw_stub() const { return true; } @@ -158,6 +162,7 @@ virtual void visit(LIR_OpVisitState* visitor) { visitor->do_slow_case(_info); visitor->do_input(_index); + if (_array) { visitor->do_input(_array); } } #ifndef PRODUCT virtual void print_name(outputStream* out) const { out->print("RangeCheckStub"); } diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -480,7 +480,7 @@ void LIRGenerator::array_range_check(LIR_Opr array, LIR_Opr index, CodeEmitInfo* null_check_info, CodeEmitInfo* range_check_info) { - CodeStub* stub = new RangeCheckStub(range_check_info, index); + CodeStub* stub = new RangeCheckStub(range_check_info, index, array); if (index->is_constant()) { cmp_mem_int(lir_cond_belowEqual, array, arrayOopDesc::length_offset_in_bytes(), index->as_jint(), null_check_info); @@ -494,7 +494,7 @@ void LIRGenerator::nio_range_check(LIR_Opr buffer, LIR_Opr index, LIR_Opr result, CodeEmitInfo* info) { - CodeStub* stub = new RangeCheckStub(info, index, true); + CodeStub* stub = new RangeCheckStub(info, index); if (index->is_constant()) { cmp_mem_int(lir_cond_belowEqual, buffer, java_nio_Buffer::limit_offset(), index->as_jint(), info); __ branch(lir_cond_belowEqual, T_INT, stub); // forward branch @@ -1592,7 +1592,7 @@ if (GenerateRangeChecks && needs_range_check) { if (use_length) { __ cmp(lir_cond_belowEqual, length.result(), index.result()); - __ branch(lir_cond_belowEqual, T_INT, new RangeCheckStub(range_check_info, index.result())); + __ branch(lir_cond_belowEqual, T_INT, new RangeCheckStub(range_check_info, index.result(), array.result())); } else { array_range_check(array.result(), index.result(), null_check_info, range_check_info); // range_check also does the null check @@ -1756,7 +1756,7 @@ LIR_Opr result = rlock_result(x); if (GenerateRangeChecks) { CodeEmitInfo* info = state_for(x); - CodeStub* stub = new RangeCheckStub(info, index.result(), true); + CodeStub* stub = new RangeCheckStub(info, index.result()); if (index.result()->is_constant()) { cmp_mem_int(lir_cond_belowEqual, buf.result(), java_nio_Buffer::limit_offset(), index.result()->as_jint(), info); __ branch(lir_cond_belowEqual, T_INT, stub); @@ -1837,12 +1837,12 @@ if (GenerateRangeChecks && needs_range_check) { if (StressLoopInvariantCodeMotion && range_check_info->deoptimize_on_exception()) { - __ branch(lir_cond_always, T_ILLEGAL, new RangeCheckStub(range_check_info, index.result())); + __ branch(lir_cond_always, T_ILLEGAL, new RangeCheckStub(range_check_info, index.result(), array.result())); } else if (use_length) { // TODO: use a (modified) version of array_range_check that does not require a // constant length to be loaded to a register __ cmp(lir_cond_belowEqual, length.result(), index.result()); - __ branch(lir_cond_belowEqual, T_INT, new RangeCheckStub(range_check_info, index.result())); + __ branch(lir_cond_belowEqual, T_INT, new RangeCheckStub(range_check_info, index.result(), array.result())); } else { array_range_check(array.result(), index.result(), null_check_info, range_check_info); // The range check performs the null check, so clear it out for the load diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -641,10 +641,12 @@ } -JRT_ENTRY(void, Runtime1::throw_range_check_exception(JavaThread* thread, int index)) +JRT_ENTRY(void, Runtime1::throw_range_check_exception(JavaThread* thread, int index, arrayOopDesc* a)) NOT_PRODUCT(_throw_range_check_exception_count++;) - char message[jintAsStringSize]; - sprintf(message, "%d", index); + const int len = 35; + assert(len < strlen("Index %d out-of-bounds for length %d."), "Must allocate more space for message."); + char message[2 * jintAsStringSize + len]; + sprintf(message, "Index %d out-of-bounds for length %d.", index, a->length()); SharedRuntime::throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), message); JRT_END diff --git a/src/hotspot/share/c1/c1_Runtime1.hpp b/src/hotspot/share/c1/c1_Runtime1.hpp --- a/src/hotspot/share/c1/c1_Runtime1.hpp +++ b/src/hotspot/share/c1/c1_Runtime1.hpp @@ -149,7 +149,7 @@ static address exception_handler_for_pc(JavaThread* thread); - static void throw_range_check_exception(JavaThread* thread, int index); + static void throw_range_check_exception(JavaThread* thread, int index, arrayOopDesc* a); static void throw_index_exception(JavaThread* thread, int index); static void throw_div0_exception(JavaThread* thread); static void throw_null_pointer_exception(JavaThread* thread); diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -58,6 +58,7 @@ #include "runtime/icache.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/java.hpp" +#include "runtime/javaCalls.hpp" #include "runtime/jfieldIDWorkaround.hpp" #include "runtime/osThread.hpp" #include "runtime/sharedRuntime.hpp" @@ -446,17 +447,16 @@ thread->set_vm_result(exception()); IRT_END - -IRT_ENTRY(void, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException(JavaThread* thread, char* name, jint index)) - char message[jintAsStringSize]; - // lookup exception klass - TempNewSymbol s = SymbolTable::new_symbol(name, CHECK); +IRT_ENTRY(void, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException(JavaThread* thread, arrayOopDesc* a, jint index)) if (ProfileTraps) { note_trap(thread, Deoptimization::Reason_range_check, CHECK); } - // create exception - sprintf(message, "%d", index); - THROW_MSG(s, message); + + ResourceMark rm(thread); + stringStream ss; + ss.print("Index %d out-of-bounds for length %d.", index, a->length()); + + THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string()); IRT_END IRT_ENTRY(void, InterpreterRuntime::throw_ClassCastException( diff --git a/src/hotspot/share/interpreter/interpreterRuntime.hpp b/src/hotspot/share/interpreter/interpreterRuntime.hpp --- a/src/hotspot/share/interpreter/interpreterRuntime.hpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.hpp @@ -83,7 +83,7 @@ Klass* interfaceKlass); static void throw_StackOverflowError(JavaThread* thread); static void throw_delayed_StackOverflowError(JavaThread* thread); - static void throw_ArrayIndexOutOfBoundsException(JavaThread* thread, char* name, jint index); + static void throw_ArrayIndexOutOfBoundsException(JavaThread* thread, arrayOopDesc* a, jint index); static void throw_ClassCastException(JavaThread* thread, oopDesc* obj); static void create_exception(JavaThread* thread, char* name, char* message); static void create_klass_exception(JavaThread* thread, char* name, oopDesc* obj); diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp @@ -173,11 +173,11 @@ } { CodeletMark cm(_masm, "throw exception entrypoints"); - Interpreter::_throw_ArrayIndexOutOfBoundsException_entry = generate_ArrayIndexOutOfBounds_handler("java/lang/ArrayIndexOutOfBoundsException"); - Interpreter::_throw_ArrayStoreException_entry = generate_klass_exception_handler("java/lang/ArrayStoreException" ); - Interpreter::_throw_ArithmeticException_entry = generate_exception_handler("java/lang/ArithmeticException" , "/ by zero"); + Interpreter::_throw_ArrayIndexOutOfBoundsException_entry = generate_ArrayIndexOutOfBounds_handler(); + Interpreter::_throw_ArrayStoreException_entry = generate_klass_exception_handler("java/lang/ArrayStoreException"); + Interpreter::_throw_ArithmeticException_entry = generate_exception_handler("java/lang/ArithmeticException", "/ by zero"); Interpreter::_throw_ClassCastException_entry = generate_ClassCastException_handler(); - Interpreter::_throw_NullPointerException_entry = generate_exception_handler("java/lang/NullPointerException" , NULL ); + Interpreter::_throw_NullPointerException_entry = generate_exception_handler("java/lang/NullPointerException", NULL); Interpreter::_throw_StackOverflowError_entry = generate_StackOverflowError_handler(); } diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,7 +51,7 @@ } address generate_exception_handler_common(const char* name, const char* message, bool pass_oop); address generate_ClassCastException_handler(); - address generate_ArrayIndexOutOfBounds_handler(const char* name); + address generate_ArrayIndexOutOfBounds_handler(); address generate_return_entry_for(TosState state, int step, size_t index_size); address generate_earlyret_entry_for(TosState state); address generate_deopt_entry_for(TosState state, int step, address continuation = NULL); diff --git a/src/hotspot/share/oops/objArrayKlass.cpp b/src/hotspot/share/oops/objArrayKlass.cpp --- a/src/hotspot/share/oops/objArrayKlass.cpp +++ b/src/hotspot/share/oops/objArrayKlass.cpp @@ -251,12 +251,34 @@ // Check is all offsets and lengths are non negative if (src_pos < 0 || dst_pos < 0 || length < 0) { - THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + // Pass specific exception reason. + ResourceMark rm; + stringStream ss; + if (src_pos < 0) { + ss.print("arraycopy source index %i out of bounds for object array[%i].", + src_pos, s->length()); + } else if (dst_pos < 0) { + ss.print("arraycopy destination index %i out of bounds for object array[%i].", + dst_pos, d->length()); + } else { + ss.print("arraycopy length %i is negative.", length); + } + THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string()); } // Check if the ranges are valid - if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length()) - || (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) { - THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + if ((((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length()) || + (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length())) { + // Pass specific exception reason. + ResourceMark rm; + stringStream ss; + if (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length()) { + ss.print("arraycopy: last source index %u out of bounds for object array[%i].", + (unsigned int) length + (unsigned int) src_pos, s->length()); + } else { + ss.print("arraycopy: last destination index %u out of bounds for object array[%i].", + (unsigned int) length + (unsigned int) dst_pos, d->length()); + } + THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string()); } // Special case. Boundary cases must be checked first diff --git a/src/hotspot/share/oops/typeArrayKlass.cpp b/src/hotspot/share/oops/typeArrayKlass.cpp --- a/src/hotspot/share/oops/typeArrayKlass.cpp +++ b/src/hotspot/share/oops/typeArrayKlass.cpp @@ -138,12 +138,36 @@ // Check is all offsets and lengths are non negative if (src_pos < 0 || dst_pos < 0 || length < 0) { - THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + // Pass specific exception reason. + ResourceMark rm; + stringStream ss; + if (src_pos < 0) { + ss.print("arraycopy source index %i out of bounds for %s[%i].", + src_pos, type2name_tab[ArrayKlass::cast(s->klass())->element_type()], s->length()); + } else if (dst_pos < 0) { + ss.print("arraycopy destination index %i out of bounds for %s[%i].", + dst_pos, type2name_tab[ArrayKlass::cast(d->klass())->element_type()], d->length()); + } else { + ss.print("arraycopy length %i is negative.", length); + } + THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string()); } // Check if the ranges are valid - if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length()) - || (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) { - THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); + if ((((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length()) || + (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length())) { + // Pass specific exception reason. + ResourceMark rm; + stringStream ss; + if (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length()) { + ss.print("arraycopy: last source index %u out of bounds for %s[%i].", + (unsigned int) length + (unsigned int) src_pos, + type2name_tab[ArrayKlass::cast(s->klass())->element_type()], s->length()); + } else { + ss.print("arraycopy: last destination index %u out of bounds for %s[%i].", + (unsigned int) length + (unsigned int) dst_pos, + type2name_tab[ArrayKlass::cast(d->klass())->element_type()], d->length()); + } + THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string()); } // Check zero copy if (length == 0) @@ -157,7 +181,6 @@ HeapAccess::arraycopy(s, d, src, dst, (size_t)length << l2es); } - // create a klass of array holding typeArrays Klass* TypeArrayKlass::array_klass_impl(bool or_null, int n, TRAPS) { int dim = dimension(); @@ -240,16 +263,11 @@ void TypeArrayKlass::print_value_on(outputStream* st) const { assert(is_klass(), "must be klass"); st->print("{type array "); - switch (element_type()) { - case T_BOOLEAN: st->print("bool"); break; - case T_CHAR: st->print("char"); break; - case T_FLOAT: st->print("float"); break; - case T_DOUBLE: st->print("double"); break; - case T_BYTE: st->print("byte"); break; - case T_SHORT: st->print("short"); break; - case T_INT: st->print("int"); break; - case T_LONG: st->print("long"); break; - default: ShouldNotReachHere(); + BasicType bt = element_type(); + if (bt == T_BOOLEAN) { + st->print("bool"); + } else { + st->print("%s", type2name_tab[bt]); } st->print("}"); } diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/ArrayIndexOutOfBoundsException/ArrayIndexOutOfBoundsExceptionTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/ArrayIndexOutOfBoundsException/ArrayIndexOutOfBoundsExceptionTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/ArrayIndexOutOfBoundsException/ArrayIndexOutOfBoundsExceptionTest.java @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Test extended ArrayIndexOutOfBoundsException message. The + * message lists information about the array and the indexes involved. + * @compile ArrayIndexOutOfBoundsExceptionTest.java + * @run testng ArrayIndexOutOfBoundsExceptionTest + * @run testng/othervm -Xcomp -XX:-TieredCompilation ArrayIndexOutOfBoundsExceptionTest + * @run testng/othervm -Xcomp -XX:TieredStopAtLevel=1 ArrayIndexOutOfBoundsExceptionTest + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/** + * Tests the detailed messages of the ArrayIndexOutOfBoundsException. + */ +public class ArrayIndexOutOfBoundsExceptionTest { + + // Some fields used in the test. + static int[] staticArray = new int[0]; + static long[][] staticLongArray = new long[0][0]; + ArrayList names = new ArrayList<>(); + ArrayList curr; + + public static void main(String[] args) { + ArrayIndexOutOfBoundsExceptionTest t = new ArrayIndexOutOfBoundsExceptionTest(); + try { + t.testAIOOBMessages(); + } catch (Exception e) {} + } + + /** + * + */ + public static class ArrayGenerator { + + /** + * @param dummy1 + * @return Object Array + */ + public static Object[] arrayReturner(boolean dummy1) { + return new Object[0]; + } + + /** + * @param dummy1 + * @param dummy2 + * @param dummy3 + * @return Object Array + */ + public Object[] returnMyArray(double dummy1, long dummy2, short dummy3) { + return new Object[0]; + } + } + + /** + * + */ + @Test + public void testAIOOBMessages() { + boolean[] za1 = new boolean[0]; + byte[] ba1 = new byte[0]; + short[] sa1 = new short[0]; + char[] ca1 = new char[0]; + int[] ia1 = new int[0]; + long[] la1 = new long[0]; + float[] fa1 = new float[0]; + double[] da1 = new double[0]; + Object[] oa1 = new Object[10]; + Object[] oa2 = new Object[5]; + + boolean[] za2 = new boolean[10]; + boolean[] za3 = new boolean[5]; + byte[] ba2 = new byte[10]; + byte[] ba3 = new byte[5]; + short[] sa2 = new short[10]; + short[] sa3 = new short[5]; + char[] ca2 = new char[10]; + char[] ca3 = new char[5]; + int[] ia2 = new int[10]; + int[] ia3 = new int[5]; + long[] la2 = new long[10]; + long[] la3 = new long[5]; + float[] fa2 = new float[10]; + float[] fa3 = new float[5]; + double[] da2 = new double[10]; + double[] da3 = new double[5]; + + try { + System.out.println(za1[-5]); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index -5 out-of-bounds for length 0."); + } + + try { + System.out.println(ba1[0]); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(sa1[0]); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(ca1[0]); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(ia1[0]); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(la1[0]); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(fa1[0]); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(da1[0]); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(oa1[12]); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 12 out-of-bounds for length 10."); + } + + try { + System.out.println(za1[0] = false); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(ba1[0] = 0); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(sa1[0] = 0); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(ca1[0] = 0); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(ia1[0] = 0); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(la1[0] = 0); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(fa1[0] = 0); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(da1[0] = 0); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + try { + System.out.println(oa1[-2] = null); + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index -2 out-of-bounds for length 10."); + } + + try { + assertTrue((ArrayGenerator.arrayReturner(false))[0] == null); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + try { + staticArray[0] = 2; + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "Index 0 out-of-bounds for length 0."); + } + + // Test all five possible messages of arraycopy exceptions thrown in ObjArrayKlass::copy_array(). + + try { + System.arraycopy(oa1, -17, oa2, 0, 5); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy source index -17 out of bounds for object array[10]."); + } + + try { + System.arraycopy(oa1, 2, oa2, -18, 5); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy destination index -18 out of bounds for object array[5]."); + } + + try { + System.arraycopy(oa1, 2, oa2, 0, -19); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy length -19 is negative."); + } + + try { + System.arraycopy(oa1, 8, oa2, 0, 5); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy: last source index 13 out of bounds for object array[10]."); + } + + try { + System.arraycopy(oa1, 1, oa2, 0, 7); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy: last destination index 7 out of bounds for object array[5]."); + } + + // Test all five possible messages of arraycopy exceptions thrown in TypeArrayKlass::copy_array(). + + try { + System.arraycopy(da2, -17, da3, 0, 5); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy source index -17 out of bounds for double[10]."); + } + + try { + System.arraycopy(da2, 2, da3, -18, 5); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy destination index -18 out of bounds for double[5]."); + } + + try { + System.arraycopy(da2, 2, da3, 0, -19); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy length -19 is negative."); + } + + try { + System.arraycopy(da2, 8, da3, 0, 5); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy: last source index 13 out of bounds for double[10]."); + } + + try { + System.arraycopy(da2, 1, da3, 0, 7); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy: last destination index 7 out of bounds for double[5]."); + } + + // Test all possible basic types in the messages of arraycopy exceptions thrown in TypeArrayKlass::copy_array(). + + try { + System.arraycopy(za2, -17, za3, 0, 5); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy source index -17 out of bounds for boolean[10]."); + } + + try { + System.arraycopy(ba2, 2, ba3, -18, 5); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy destination index -18 out of bounds for byte[5]."); + } + + try { + System.arraycopy(sa2, 2, sa3, 0, -19); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy length -19 is negative."); + } + + try { + System.arraycopy(ca2, 8, ca3, 0, 5); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy: last source index 13 out of bounds for char[10]."); + } + + try { + System.arraycopy(ia2, 2, ia3, 0, -19); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy length -19 is negative."); + } + + try { + System.arraycopy(la2, 1, la3, 0, 7); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy: last destination index 7 out of bounds for long[5]."); + } + + try { + System.arraycopy(fa2, 1, fa3, 0, 7); + fail(); + } catch (ArrayIndexOutOfBoundsException e) { + assertEquals(e.getMessage(), + "arraycopy: last destination index 7 out of bounds for float[5]."); + } + } +}