diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 6ed163bf71e10..65bdeba02648a 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -8860,6 +8860,17 @@ instruct castII(iRegI dst) ins_pipe(pipe_class_empty); %} +instruct castLL(iRegL dst) +%{ + match(Set dst (CastLL dst)); + + size(0); + format %{ "# castLL of $dst" %} + ins_encode(/* empty encoding */); + ins_cost(0); + ins_pipe(pipe_class_empty); +%} + // ============================================================================ // Atomic operation instructions // diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 1c23d72e0af01..0743063ddf087 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -5277,6 +5277,14 @@ instruct castII( iRegI dst ) %{ ins_pipe(empty); %} +instruct castLL( iRegL dst ) %{ + match(Set dst (CastLL dst)); + format %{ "! castLL of $dst" %} + ins_encode( /*empty encoding*/ ); + ins_cost(0); + ins_pipe(empty); +%} + //----------Arithmetic Instructions-------------------------------------------- // Addition Instructions // Register Addition diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 8bbbc3d4aa374..fc77d9c53716e 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -10348,6 +10348,14 @@ instruct castII(iRegIdst dst) %{ ins_pipe(pipe_class_default); %} +instruct castLL(iRegLdst dst) %{ + match(Set dst (CastLL dst)); + format %{ " -- \t// castLL of $dst" %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + instruct checkCastPP(iRegPdst dst) %{ match(Set dst (CheckCastPP dst)); format %{ " -- \t// checkcastPP of $dst" %} diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 9a574ac0854e7..ff98da7981e1b 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -5345,6 +5345,13 @@ instruct castII(iRegI dst) %{ ins_pipe(pipe_class_dummy); %} +instruct castLL(iRegL dst) %{ + match(Set dst (CastLL dst)); + size(0); + format %{ "# castLL of $dst" %} + ins_encode(/*empty*/); + ins_pipe(pipe_class_dummy); +%} //----------Conditional_store-------------------------------------------------- // Conditional-store of the updated heap-top. diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 4d815e62240aa..e6b947e1c8fd0 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -7147,6 +7147,14 @@ instruct castII( rRegI dst ) %{ ins_pipe( empty ); %} +instruct castLL( eRegL dst ) %{ + match(Set dst (CastLL dst)); + format %{ "#castLL of $dst" %} + ins_encode( /*empty encoding*/ ); + ins_cost(0); + ins_pipe( empty ); +%} + // Load-locked - same as a regular pointer load when used with compare-swap instruct loadPLocked(eRegP dst, memory mem) %{ match(Set dst (LoadPLocked mem)); diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 8ae83fb298b26..cedee4c097d5f 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -7445,6 +7445,17 @@ instruct castII(rRegI dst) ins_pipe(empty); %} +instruct castLL(rRegL dst) +%{ + match(Set dst (CastLL dst)); + + size(0); + format %{ "# castLL of $dst" %} + ins_encode(/* empty encoding */); + ins_cost(0); + ins_pipe(empty); +%} + // LoadP-locked same as a regular LoadP when used with compare-swap instruct loadPLocked(rRegP dst, memory mem) %{ diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 69b67aeaa9e65..6f678a60eb086 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -337,6 +337,8 @@ class methodHandle; \ do_intrinsic(_Preconditions_checkIndex, jdk_internal_util_Preconditions, checkIndex_name, Preconditions_checkIndex_signature, F_S) \ do_signature(Preconditions_checkIndex_signature, "(IILjava/util/function/BiFunction;)I") \ + do_intrinsic(_Preconditions_checkLongIndex, jdk_internal_util_Preconditions, checkIndex_name, Preconditions_checkLongIndex_signature, F_S) \ + do_signature(Preconditions_checkLongIndex_signature, "(JJLjava/util/function/BiFunction;)J") \ \ do_class(java_nio_Buffer, "java/nio/Buffer") \ do_intrinsic(_checkIndex, java_nio_Buffer, checkIndex_name, int_int_signature, F_R) \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index c4af0c9981ed7..5e6d03c34cb6c 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -649,6 +649,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_profileBoolean: case vmIntrinsics::_isCompileConstant: case vmIntrinsics::_Preconditions_checkIndex: + case vmIntrinsics::_Preconditions_checkLongIndex: case vmIntrinsics::_getObjectSize: break; diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 11927391907cb..f1b34da54147e 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -96,6 +96,11 @@ Node* ConstraintCastNode::make_cast(int opcode, Node* c, Node *n, const Type *t, cast->set_req(0, c); return cast; } + case Op_CastLL: { + Node* cast = new CastLLNode(n, t, carry_dependency); + cast->set_req(0, c); + return cast; + } case Op_CastPP: { Node* cast = new CastPPNode(n, t, carry_dependency); cast->set_req(0, c); @@ -108,6 +113,20 @@ Node* ConstraintCastNode::make_cast(int opcode, Node* c, Node *n, const Type *t, return NULL; } +Node* ConstraintCastNode::make(Node* c, Node *n, const Type *t, BasicType bt) { + switch(bt) { + case T_INT: { + return make_cast(Op_CastII, c, n, t, false); + } + case T_LONG: { + return make_cast(Op_CastLL, c, n, t, false); + } + default: + fatal("Bad basic type %s", type2name(bt)); + } + return NULL; +} + TypeNode* ConstraintCastNode::dominating_cast(PhaseGVN* gvn, PhaseTransform* pt) const { Node* val = in(1); Node* ctl = in(0); diff --git a/src/hotspot/share/opto/castnode.hpp b/src/hotspot/share/opto/castnode.hpp index bcc8e0f26a886..a8e3eebec915f 100644 --- a/src/hotspot/share/opto/castnode.hpp +++ b/src/hotspot/share/opto/castnode.hpp @@ -53,6 +53,7 @@ class ConstraintCastNode: public TypeNode { bool carry_dependency() const { return _carry_dependency; } TypeNode* dominating_cast(PhaseGVN* gvn, PhaseTransform* pt) const; static Node* make_cast(int opcode, Node* c, Node *n, const Type *t, bool carry_dependency); + static Node* make(Node* c, Node *n, const Type *t, BasicType bt); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; @@ -92,6 +93,16 @@ class CastIINode: public ConstraintCastNode { #endif }; +class CastLLNode: public ConstraintCastNode { +public: + CastLLNode(Node* n, const Type* t, bool carry_dependency = false) + : ConstraintCastNode(n, t, carry_dependency){ + init_class_id(Class_CastLL); + } + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegL; } +}; + //------------------------------CastPPNode------------------------------------- // cast pointer to pointer (different type) class CastPPNode: public ConstraintCastNode { diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 1aa220c5947b0..19cfa6fcd7c92 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -1970,12 +1970,14 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Wait until after parsing for the type information to propagate from the casts. assert(can_reshape, "Invalid during parsing"); const Type* phi_type = bottom_type(); - assert(phi_type->isa_int() || phi_type->isa_ptr(), "bad phi type"); + assert(phi_type->isa_int() || phi_type->isa_ptr() || phi_type->isa_long(), "bad phi type"); // Add casts to carry the control dependency of the Phi that is // going away Node* cast = NULL; if (phi_type->isa_int()) { cast = ConstraintCastNode::make_cast(Op_CastII, r, uin, phi_type, true); + } else if (phi_type->isa_long()) { + cast = ConstraintCastNode::make_cast(Op_CastLL, r, uin, phi_type, true); } else { const Type* uin_type = phase->type(uin); if (!phi_type->isa_oopptr() && !uin_type->isa_oopptr()) { diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index fa4b85fb23417..9acdad00990b4 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -61,6 +61,7 @@ macro(CallLeafNoFP) macro(CallRuntime) macro(CallStaticJava) macro(CastII) +macro(CastLL) macro(CastX2P) macro(CastP2X) macro(CastPP) diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index ff649d2cbb98f..5f9074c8e2476 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -103,6 +103,13 @@ class GraphKit : public Phase { // Create or find a constant node Node* intcon(jint con) const { return _gvn.intcon(con); } Node* longcon(jlong con) const { return _gvn.longcon(con); } + Node* integercon(jlong con, BasicType bt) const { + if (bt == T_INT) { + return intcon(checked_cast(con)); + } + assert(bt == T_LONG, "basic type not an int or long"); + return longcon(con); + } Node* makecon(const Type *t) const { return _gvn.makecon(t); } Node* zerocon(BasicType bt) const { return _gvn.zerocon(bt); } // (See also macro MakeConX in type.hpp, which uses intcon or longcon.) diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 5206e6220e556..ed1fbe7dca162 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -483,7 +483,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_copyOfRange: return inline_array_copyOf(true); case vmIntrinsics::_equalsB: return inline_array_equals(StrIntrinsicNode::LL); case vmIntrinsics::_equalsC: return inline_array_equals(StrIntrinsicNode::UU); - case vmIntrinsics::_Preconditions_checkIndex: return inline_preconditions_checkIndex(); + case vmIntrinsics::_Preconditions_checkIndex: return inline_preconditions_checkIndex(T_INT); + case vmIntrinsics::_Preconditions_checkLongIndex: return inline_preconditions_checkIndex(T_LONG); case vmIntrinsics::_clone: return inline_native_clone(intrinsic()->is_virtual()); case vmIntrinsics::_allocateUninitializedArray: return inline_unsafe_newArray(true); @@ -1001,14 +1002,15 @@ bool LibraryCallKit::inline_hasNegatives() { return true; } -bool LibraryCallKit::inline_preconditions_checkIndex() { +bool LibraryCallKit::inline_preconditions_checkIndex(BasicType bt) { Node* index = argument(0); - Node* length = argument(1); + Node* length = bt == T_INT ? argument(1) : argument(2); if (too_many_traps(Deoptimization::Reason_intrinsic) || too_many_traps(Deoptimization::Reason_range_check)) { return false; } - Node* len_pos_cmp = _gvn.transform(new CmpINode(length, intcon(0))); + // check that length is positive + Node* len_pos_cmp = _gvn.transform(CmpNode::make(length, integercon(0, bt), bt)); Node* len_pos_bol = _gvn.transform(new BoolNode(len_pos_cmp, BoolTest::ge)); { @@ -1017,11 +1019,19 @@ bool LibraryCallKit::inline_preconditions_checkIndex() { Deoptimization::Action_make_not_entrant); } + // length is now known postive, add a cast node to make this explicit + jlong upper_bound = _gvn.type(length)->is_integer(bt)->hi_as_long(); + Node* casted_length = ConstraintCastNode::make(control(), length, TypeInteger::make(0, upper_bound, Type::WidenMax, bt), bt); + casted_length = _gvn.transform(casted_length); + replace_in_map(length, casted_length); + length = casted_length; + if (stopped()) { return false; } - Node* rc_cmp = _gvn.transform(new CmpUNode(index, length)); + // Use an unsigned comparison for the range check itself + Node* rc_cmp = _gvn.transform(CmpNode::make(index, length, bt, true)); BoolTest::mask btest = BoolTest::lt; Node* rc_bool = _gvn.transform(new BoolNode(rc_cmp, btest)); RangeCheckNode* rc = new RangeCheckNode(control(), rc_bool, PROB_MAX, COUNT_UNKNOWN); @@ -1041,8 +1051,8 @@ bool LibraryCallKit::inline_preconditions_checkIndex() { return false; } - Node* result = new CastIINode(index, TypeInt::make(0, _gvn.type(length)->is_int()->_hi, Type::WidenMax)); - result->set_req(0, control()); + // index is now known to be >= 0 and < length, cast it + Node* result = ConstraintCastNode::make(control(), index, TypeInteger::make(0, upper_bound, Type::WidenMax, bt), bt); result = _gvn.transform(result); set_result(result); replace_in_map(index, result); diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 9565e319fc2a1..c51ce5b9078e0 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -241,7 +241,7 @@ class LibraryCallKit : public GraphKit { bool inline_native_getLength(); bool inline_array_copyOf(bool is_copyOfRange); bool inline_array_equals(StrIntrinsicNode::ArgEnc ae); - bool inline_preconditions_checkIndex(); + bool inline_preconditions_checkIndex(BasicType bt); void copy_to_clone(Node* obj, Node* alloc_obj, Node* obj_size, bool is_array); bool inline_native_clone(bool is_virtual); bool inline_native_Reflection_getCallerClass(); diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index d156dd2c4544d..cc017777d95a1 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -52,6 +52,7 @@ class CallNode; class CallRuntimeNode; class CallStaticJavaNode; class CastIINode; +class CastLLNode; class CatchNode; class CatchProjNode; class CheckCastPPNode; @@ -671,6 +672,7 @@ class Node { DEFINE_CLASS_ID(ConstraintCast, Type, 1) DEFINE_CLASS_ID(CastII, ConstraintCast, 0) DEFINE_CLASS_ID(CheckCastPP, ConstraintCast, 1) + DEFINE_CLASS_ID(CastLL, ConstraintCast, 2) DEFINE_CLASS_ID(CMove, Type, 3) DEFINE_CLASS_ID(SafePointScalarObject, Type, 4) DEFINE_CLASS_ID(DecodeNarrowPtr, Type, 5) @@ -819,6 +821,7 @@ class Node { DEFINE_CLASS_QUERY(CatchProj) DEFINE_CLASS_QUERY(CheckCastPP) DEFINE_CLASS_QUERY(CastII) + DEFINE_CLASS_QUERY(CastLL) DEFINE_CLASS_QUERY(ConstraintCast) DEFINE_CLASS_QUERY(ClearArray) DEFINE_CLASS_QUERY(CMove) diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index 4a5805093f9a4..30e3a81453307 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -274,6 +274,7 @@ class PhaseTransform : public Phase { // Fast int or long constant. Same as TypeInt::make(i) or TypeLong::make(l). ConINode* intcon(jint i); ConLNode* longcon(jlong l); + ConNode* integercon(jlong l, BasicType bt); // Fast zero or null constant. Same as makecon(Type::get_zero_type(bt)). ConNode* zerocon(BasicType bt); diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index eaa007d35cb45..1a545b7a7cc36 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -563,8 +563,27 @@ void CmpNode::related(GrowableArray *in_rel, GrowableArray *out_re } } } + #endif +CmpNode *CmpNode::make(Node *in1, Node *in2, BasicType bt, bool unsigned_comp) { + switch (bt) { + case T_INT: + if (unsigned_comp) { + return new CmpUNode(in1, in2); + } + return new CmpINode(in1, in2); + case T_LONG: + if (unsigned_comp) { + return new CmpULNode(in1, in2); + } + return new CmpLNode(in1, in2); + default: + fatal("Not implemented for %s", type2name(bt)); + } + return NULL; +} + //============================================================================= //------------------------------cmp-------------------------------------------- // Simplify a CmpI (compare 2 integers) node, based on local information. diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp index 99be190c1d299..02f7ca9704835 100644 --- a/src/hotspot/share/opto/subnode.hpp +++ b/src/hotspot/share/opto/subnode.hpp @@ -140,6 +140,8 @@ class CmpNode : public SubNode { const Type *bottom_type() const { return TypeInt::CC; } virtual uint ideal_reg() const { return Op_RegFlags; } + static CmpNode *make(Node *in1, Node *in2, BasicType bt, bool unsigned_comp = false); + #ifndef PRODUCT // CmpNode and subclasses include all data inputs (until hitting a control // boundary) in their related node set, as well as all outputs until and diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 22e4d8e49a31c..2a0178ee0345d 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -1338,6 +1338,14 @@ bool TypeD::empty(void) const { return false; // always exactly a singleton } +const TypeInteger* TypeInteger::make(jlong lo, jlong hi, int w, BasicType bt) { + if (bt == T_INT) { + return TypeInt::make(checked_cast(lo), checked_cast(hi), w); + } + assert(bt == T_LONG, "basic type not an int or long"); + return TypeLong::make(lo, hi, w); +} + //============================================================================= // Convience common pre-built types. const TypeInt *TypeInt::MAX; // INT_MAX @@ -1363,7 +1371,7 @@ const TypeInt *TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] const TypeInt *TypeInt::TYPE_DOMAIN; // alias for TypeInt::INT //------------------------------TypeInt---------------------------------------- -TypeInt::TypeInt( jint lo, jint hi, int w ) : Type(Int), _lo(lo), _hi(hi), _widen(w) { +TypeInt::TypeInt( jint lo, jint hi, int w ) : TypeInteger(Int), _lo(lo), _hi(hi), _widen(w) { } //------------------------------make------------------------------------------- @@ -1623,7 +1631,7 @@ const TypeLong *TypeLong::UINT; // 32-bit unsigned subrange const TypeLong *TypeLong::TYPE_DOMAIN; // alias for TypeLong::LONG //------------------------------TypeLong--------------------------------------- -TypeLong::TypeLong( jlong lo, jlong hi, int w ) : Type(Long), _lo(lo), _hi(hi), _widen(w) { +TypeLong::TypeLong(jlong lo, jlong hi, int w) : TypeInteger(Long), _lo(lo), _hi(hi), _widen(w) { } //------------------------------make------------------------------------------- diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 51a48c04e4b78..64493db2196fc 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -45,8 +45,9 @@ class Dict; class Type; class TypeD; class TypeF; -class TypeInt; -class TypeLong; +class TypeInteger; +class TypeInt; +class TypeLong; class TypeNarrowPtr; class TypeNarrowOop; class TypeNarrowKlass; @@ -283,6 +284,9 @@ class Type { const TypeInt *is_int() const; const TypeInt *isa_int() const; // Returns NULL if not an Int + const TypeInteger* isa_integer() const; + const TypeInteger* is_integer(BasicType bt) const; + const TypeInteger* isa_integer(BasicType bt) const; const TypeLong *is_long() const; const TypeLong *isa_long() const; // Returns NULL if not a Long const TypeD *isa_double() const; // Returns NULL if not a Double{Top,Con,Bot} @@ -525,10 +529,24 @@ class TypeD : public Type { #endif }; +class TypeInteger : public Type { +protected: + TypeInteger(TYPES t) : Type(t) {} + +public: + virtual jlong hi_as_long() const = 0; + virtual jlong lo_as_long() const = 0; + jlong get_con_as_long(BasicType bt) const; + + static const TypeInteger* make(jlong lo, jlong hi, int w, BasicType bt); +}; + + + //------------------------------TypeInt---------------------------------------- // Class of integer ranges, the set of integers between a lower bound and an // upper bound, inclusive. -class TypeInt : public Type { +class TypeInt : public TypeInteger { TypeInt( jint lo, jint hi, int w ); protected: virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; @@ -557,6 +575,10 @@ class TypeInt : public Type { virtual const Type *xdual() const; // Compute dual right now. virtual const Type *widen( const Type *t, const Type* limit_type ) const; virtual const Type *narrow( const Type *t ) const; + + virtual jlong hi_as_long() const { return _hi; } + virtual jlong lo_as_long() const { return _lo; } + // Do not kill _widen bits. // Convenience common pre-built types. static const TypeInt *MAX; @@ -591,7 +613,7 @@ class TypeInt : public Type { //------------------------------TypeLong--------------------------------------- // Class of long integer ranges, the set of integers between a lower bound and // an upper bound, inclusive. -class TypeLong : public Type { +class TypeLong : public TypeInteger { TypeLong( jlong lo, jlong hi, int w ); protected: // Do not kill _widen bits. @@ -620,6 +642,8 @@ class TypeLong : public Type { virtual bool is_finite() const; // Has a finite value + virtual jlong hi_as_long() const { return _hi; } + virtual jlong lo_as_long() const { return _lo; } virtual const Type *xmeet( const Type *t ) const; virtual const Type *xdual() const; // Compute dual right now. @@ -1575,6 +1599,15 @@ inline double Type::getd() const { return ((TypeD*)this)->_d; } +inline const TypeInteger *Type::is_integer(BasicType bt) const { + assert((bt == T_INT && _base == Int) || (bt == T_LONG && _base == Long), "Not an Int"); + return (TypeInteger*)this; +} + +inline const TypeInteger *Type::isa_integer(BasicType bt) const { + return (((bt == T_INT && _base == Int) || (bt == T_LONG && _base == Long)) ? (TypeInteger*)this : NULL); +} + inline const TypeInt *Type::is_int() const { assert( _base == Int, "Not an Int" ); return (TypeInt*)this; diff --git a/src/java.base/share/classes/java/lang/IndexOutOfBoundsException.java b/src/java.base/share/classes/java/lang/IndexOutOfBoundsException.java index c61d9ffadb31a..a0e338e7bd158 100644 --- a/src/java.base/share/classes/java/lang/IndexOutOfBoundsException.java +++ b/src/java.base/share/classes/java/lang/IndexOutOfBoundsException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, 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 @@ -68,4 +68,18 @@ public IndexOutOfBoundsException(String s) { public IndexOutOfBoundsException(int index) { super("Index out of range: " + index); } + + /** + * Constructs a new {@code IndexOutOfBoundsException} class with an + * argument indicating the illegal index. + * + *

The index is included in this exception's detail message. The + * exact presentation format of the detail message is unspecified. + * + * @param index the illegal index. + * @since 16 + */ + public IndexOutOfBoundsException(long index) { + super("Index out of range: " + index); + } } diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandle.java b/src/java.base/share/classes/java/lang/invoke/VarHandle.java index 698eb8374a132..1639a75e9d936 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandle.java @@ -2146,7 +2146,7 @@ final void updateVarForm(VarForm newVForm) { UNSAFE.fullFence(); } - static final BiFunction, ArrayIndexOutOfBoundsException> + static final BiFunction, ArrayIndexOutOfBoundsException> AIOOBE_SUPPLIER = Preconditions.outOfBoundsExceptionFormatter( new Function() { @Override diff --git a/src/java.base/share/classes/java/util/Objects.java b/src/java.base/share/classes/java/util/Objects.java index 8cd0dddf18c1f..c32a67441da9d 100644 --- a/src/java.base/share/classes/java/util/Objects.java +++ b/src/java.base/share/classes/java/util/Objects.java @@ -411,4 +411,79 @@ int checkFromIndexSize(int fromIndex, int size, int length) { return Preconditions.checkFromIndexSize(fromIndex, size, length, null); } + /** + * Checks if the {@code index} is within the bounds of the range from + * {@code 0} (inclusive) to {@code length} (exclusive). + * + *

The {@code index} is defined to be out of bounds if any of the + * following inequalities is true: + *

    + *
  • {@code index < 0}
  • + *
  • {@code index >= length}
  • + *
  • {@code length < 0}, which is implied from the former inequalities
  • + *
+ * + * @param index the index + * @param length the upper-bound (exclusive) of the range + * @return {@code index} if it is within bounds of the range + * @throws IndexOutOfBoundsException if the {@code index} is out of bounds + * @since 16 + */ + @ForceInline + public static + long checkIndex(long index, long length) { + return Preconditions.checkIndex(index, length, null); + } + + /** + * Checks if the sub-range from {@code fromIndex} (inclusive) to + * {@code toIndex} (exclusive) is within the bounds of range from {@code 0} + * (inclusive) to {@code length} (exclusive). + * + *

The sub-range is defined to be out of bounds if any of the following + * inequalities is true: + *

    + *
  • {@code fromIndex < 0}
  • + *
  • {@code fromIndex > toIndex}
  • + *
  • {@code toIndex > length}
  • + *
  • {@code length < 0}, which is implied from the former inequalities
  • + *
+ * + * @param fromIndex the lower-bound (inclusive) of the sub-range + * @param toIndex the upper-bound (exclusive) of the sub-range + * @param length the upper-bound (exclusive) the range + * @return {@code fromIndex} if the sub-range within bounds of the range + * @throws IndexOutOfBoundsException if the sub-range is out of bounds + * @since 16 + */ + public static + long checkFromToIndex(long fromIndex, long toIndex, long length) { + return Preconditions.checkFromToIndex(fromIndex, toIndex, length, null); + } + + /** + * Checks if the sub-range from {@code fromIndex} (inclusive) to + * {@code fromIndex + size} (exclusive) is within the bounds of range from + * {@code 0} (inclusive) to {@code length} (exclusive). + * + *

The sub-range is defined to be out of bounds if any of the following + * inequalities is true: + *

    + *
  • {@code fromIndex < 0}
  • + *
  • {@code size < 0}
  • + *
  • {@code fromIndex + size > length}, taking into account integer overflow
  • + *
  • {@code length < 0}, which is implied from the former inequalities
  • + *
+ * + * @param fromIndex the lower-bound (inclusive) of the sub-interval + * @param size the size of the sub-range + * @param length the upper-bound (exclusive) of the range + * @return {@code fromIndex} if the sub-range within bounds of the range + * @throws IndexOutOfBoundsException if the sub-range is out of bounds + * @since 16 + */ + public static + long checkFromIndexSize(long fromIndex, long size, long length) { + return Preconditions.checkFromIndexSize(fromIndex, size, length, null); + } } diff --git a/src/java.base/share/classes/jdk/internal/util/Preconditions.java b/src/java.base/share/classes/jdk/internal/util/Preconditions.java index 2854b15743194..75777a6d1f086 100644 --- a/src/java.base/share/classes/jdk/internal/util/Preconditions.java +++ b/src/java.base/share/classes/jdk/internal/util/Preconditions.java @@ -54,10 +54,10 @@ public class Preconditions { * @return the runtime exception */ private static RuntimeException outOfBounds( - BiFunction, ? extends RuntimeException> oobef, + BiFunction, ? extends RuntimeException> oobef, String checkKind, - Integer... args) { - List largs = List.of(args); + Number... args) { + List largs = List.of(args); RuntimeException e = oobef == null ? null : oobef.apply(checkKind, largs); return e == null @@ -65,23 +65,41 @@ private static RuntimeException outOfBounds( } private static RuntimeException outOfBoundsCheckIndex( - BiFunction, ? extends RuntimeException> oobe, + BiFunction, ? extends RuntimeException> oobe, int index, int length) { return outOfBounds(oobe, "checkIndex", index, length); } private static RuntimeException outOfBoundsCheckFromToIndex( - BiFunction, ? extends RuntimeException> oobe, + BiFunction, ? extends RuntimeException> oobe, int fromIndex, int toIndex, int length) { return outOfBounds(oobe, "checkFromToIndex", fromIndex, toIndex, length); } private static RuntimeException outOfBoundsCheckFromIndexSize( - BiFunction, ? extends RuntimeException> oobe, + BiFunction, ? extends RuntimeException> oobe, int fromIndex, int size, int length) { return outOfBounds(oobe, "checkFromIndexSize", fromIndex, size, length); } + private static RuntimeException outOfBoundsCheckIndex( + BiFunction, ? extends RuntimeException> oobe, + long index, long length) { + return outOfBounds(oobe, "checkIndex", index, length); + } + + private static RuntimeException outOfBoundsCheckFromToIndex( + BiFunction, ? extends RuntimeException> oobe, + long fromIndex, long toIndex, long length) { + return outOfBounds(oobe, "checkFromToIndex", fromIndex, toIndex, length); + } + + private static RuntimeException outOfBoundsCheckFromIndexSize( + BiFunction, ? extends RuntimeException> oobe, + long fromIndex, long size, long length) { + return outOfBounds(oobe, "checkFromIndexSize", fromIndex, size, length); + } + /** * Returns an out-of-bounds exception formatter from an given exception * factory. The exception formatter is a function that formats an @@ -90,8 +108,8 @@ private static RuntimeException outOfBoundsCheckFromIndexSize( * *

The exception formatter accepts two arguments: a {@code String} * describing the out-of-bounds range check that failed, referred to as the - * check kind; and a {@code List} containing the - * out-of-bound integer values that failed the check. The list of + * check kind; and a {@code List} containing the + * out-of-bound integral values that failed the check. The list of * out-of-bound values is not modified. * *

Three check kinds are supported {@code checkIndex}, @@ -102,7 +120,7 @@ private static RuntimeException outOfBoundsCheckFromIndexSize( * {@link #checkFromToIndex(int, int, int, BiFunction) checkFromToIndex}, and * {@link #checkFromIndexSize(int, int, int, BiFunction) checkFromIndexSize}. * Thus a supported check kind corresponds to a method name and the - * out-of-bound integer values correspond to method argument values, in + * out-of-bound integral values correspond to method argument values, in * order, preceding the exception formatter argument (similar in many * respects to the form of arguments required for a reflective invocation of * such a range check method). @@ -123,7 +141,7 @@ private static RuntimeException outOfBoundsCheckFromIndexSize( * {@code static final} field as follows: *

{@code
      * static final
-     * BiFunction, ArrayIndexOutOfBoundsException> AIOOBEF =
+     * BiFunction, ArrayIndexOutOfBoundsException> AIOOBEF =
      *     outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new);
      * }
* The formatter instance {@code AIOOBEF} may be passed as an argument to an @@ -150,18 +168,18 @@ private static RuntimeException outOfBoundsCheckFromIndexSize( * @return the out-of-bounds exception formatter */ public static - BiFunction, X> outOfBoundsExceptionFormatter(Function f) { + BiFunction, X> outOfBoundsExceptionFormatter(Function f) { // Use anonymous class to avoid bootstrap issues if this method is // used early in startup - return new BiFunction, X>() { + return new BiFunction, X>() { @Override - public X apply(String checkKind, List args) { + public X apply(String checkKind, List args) { return f.apply(outOfBoundsMessage(checkKind, args)); } }; } - private static String outOfBoundsMessage(String checkKind, List args) { + private static String outOfBoundsMessage(String checkKind, List args) { if (checkKind == null && args == null) { return String.format("Range check failed"); } else if (checkKind == null) { @@ -213,7 +231,7 @@ private static String outOfBoundsMessage(String checkKind, List args) { *

If the {@code index} is out of bounds, then a runtime exception is * thrown that is the result of applying the following arguments to the * exception formatter: the name of this method, {@code checkIndex}; - * and an unmodifiable list integers whose values are, in order, the + * and an unmodifiable list of integers whose values are, in order, the * out-of-bounds arguments {@code index} and {@code length}. * * @param the type of runtime exception to throw if the arguments are @@ -243,7 +261,7 @@ private static String outOfBoundsMessage(String checkKind, List args) { @IntrinsicCandidate public static int checkIndex(int index, int length, - BiFunction, X> oobef) { + BiFunction, X> oobef) { if (index < 0 || index >= length) throw outOfBoundsCheckIndex(oobef, index, length); return index; @@ -266,7 +284,7 @@ int checkIndex(int index, int length, *

If the sub-range is out of bounds, then a runtime exception is * thrown that is the result of applying the following arguments to the * exception formatter: the name of this method, {@code checkFromToIndex}; - * and an unmodifiable list integers whose values are, in order, the + * and an unmodifiable list of integers whose values are, in order, the * out-of-bounds arguments {@code fromIndex}, {@code toIndex}, and {@code length}. * * @param the type of runtime exception to throw if the arguments are @@ -290,7 +308,7 @@ int checkIndex(int index, int length, */ public static int checkFromToIndex(int fromIndex, int toIndex, int length, - BiFunction, X> oobef) { + BiFunction, X> oobef) { if (fromIndex < 0 || fromIndex > toIndex || toIndex > length) throw outOfBoundsCheckFromToIndex(oobef, fromIndex, toIndex, length); return fromIndex; @@ -313,7 +331,7 @@ int checkFromToIndex(int fromIndex, int toIndex, int length, *

If the sub-range is out of bounds, then a runtime exception is * thrown that is the result of applying the following arguments to the * exception formatter: the name of this method, {@code checkFromIndexSize}; - * and an unmodifiable list integers whose values are, in order, the + * and an unmodifiable list of integers whose values are, in order, the * out-of-bounds arguments {@code fromIndex}, {@code size}, and * {@code length}. * @@ -338,7 +356,153 @@ int checkFromToIndex(int fromIndex, int toIndex, int length, */ public static int checkFromIndexSize(int fromIndex, int size, int length, - BiFunction, X> oobef) { + BiFunction, X> oobef) { + if ((length | fromIndex | size) < 0 || size > length - fromIndex) + throw outOfBoundsCheckFromIndexSize(oobef, fromIndex, size, length); + return fromIndex; + } + + /** + * Checks if the {@code index} is within the bounds of the range from + * {@code 0} (inclusive) to {@code length} (exclusive). + * + *

The {@code index} is defined to be out of bounds if any of the + * following inequalities is true: + *

    + *
  • {@code index < 0}
  • + *
  • {@code index >= length}
  • + *
  • {@code length < 0}, which is implied from the former inequalities
  • + *
+ * + *

If the {@code index} is out of bounds, then a runtime exception is + * thrown that is the result of applying the following arguments to the + * exception formatter: the name of this method, {@code checkIndex}; + * and an unmodifiable list of longs whose values are, in order, the + * out-of-bounds arguments {@code index} and {@code length}. + * + * @param the type of runtime exception to throw if the arguments are + * out of bounds + * @param index the index + * @param length the upper-bound (exclusive) of the range + * @param oobef the exception formatter that when applied with this + * method name and out-of-bounds arguments returns a runtime + * exception. If {@code null} or returns {@code null} then, it is as + * if an exception formatter produced from an invocation of + * {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used + * instead (though it may be more efficient). + * Exceptions thrown by the formatter are relayed to the caller. + * @return {@code index} if it is within bounds of the range + * @throws X if the {@code index} is out of bounds and the exception + * formatter is non-{@code null} + * @throws IndexOutOfBoundsException if the {@code index} is out of bounds + * and the exception formatter is {@code null} + * @since 16 + * + * @implNote + * This method is made intrinsic in optimizing compilers to guide them to + * perform unsigned comparisons of the index and length when it is known the + * length is a non-negative value (such as that of an array length or from + * the upper bound of a loop) + */ + @IntrinsicCandidate + public static + long checkIndex(long index, long length, + BiFunction, X> oobef) { + if (index < 0 || index >= length) + throw outOfBoundsCheckIndex(oobef, index, length); + return index; + } + + /** + * Checks if the sub-range from {@code fromIndex} (inclusive) to + * {@code toIndex} (exclusive) is within the bounds of range from {@code 0} + * (inclusive) to {@code length} (exclusive). + * + *

The sub-range is defined to be out of bounds if any of the following + * inequalities is true: + *

    + *
  • {@code fromIndex < 0}
  • + *
  • {@code fromIndex > toIndex}
  • + *
  • {@code toIndex > length}
  • + *
  • {@code length < 0}, which is implied from the former inequalities
  • + *
+ * + *

If the sub-range is out of bounds, then a runtime exception is + * thrown that is the result of applying the following arguments to the + * exception formatter: the name of this method, {@code checkFromToIndex}; + * and an unmodifiable list of longs whose values are, in order, the + * out-of-bounds arguments {@code fromIndex}, {@code toIndex}, and {@code length}. + * + * @param the type of runtime exception to throw if the arguments are + * out of bounds + * @param fromIndex the lower-bound (inclusive) of the sub-range + * @param toIndex the upper-bound (exclusive) of the sub-range + * @param length the upper-bound (exclusive) the range + * @param oobef the exception formatter that when applied with this + * method name and out-of-bounds arguments returns a runtime + * exception. If {@code null} or returns {@code null} then, it is as + * if an exception formatter produced from an invocation of + * {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used + * instead (though it may be more efficient). + * Exceptions thrown by the formatter are relayed to the caller. + * @return {@code fromIndex} if the sub-range within bounds of the range + * @throws X if the sub-range is out of bounds and the exception factory + * function is non-{@code null} + * @throws IndexOutOfBoundsException if the sub-range is out of bounds and + * the exception factory function is {@code null} + * @since 16 + */ + public static + long checkFromToIndex(long fromIndex, long toIndex, long length, + BiFunction, X> oobef) { + if (fromIndex < 0 || fromIndex > toIndex || toIndex > length) + throw outOfBoundsCheckFromToIndex(oobef, fromIndex, toIndex, length); + return fromIndex; + } + + /** + * Checks if the sub-range from {@code fromIndex} (inclusive) to + * {@code fromIndex + size} (exclusive) is within the bounds of range from + * {@code 0} (inclusive) to {@code length} (exclusive). + * + *

The sub-range is defined to be out of bounds if any of the following + * inequalities is true: + *

    + *
  • {@code fromIndex < 0}
  • + *
  • {@code size < 0}
  • + *
  • {@code fromIndex + size > length}, taking into account integer overflow
  • + *
  • {@code length < 0}, which is implied from the former inequalities
  • + *
+ * + *

If the sub-range is out of bounds, then a runtime exception is + * thrown that is the result of applying the following arguments to the + * exception formatter: the name of this method, {@code checkFromIndexSize}; + * and an unmodifiable list of longs whose values are, in order, the + * out-of-bounds arguments {@code fromIndex}, {@code size}, and + * {@code length}. + * + * @param the type of runtime exception to throw if the arguments are + * out of bounds + * @param fromIndex the lower-bound (inclusive) of the sub-interval + * @param size the size of the sub-range + * @param length the upper-bound (exclusive) of the range + * @param oobef the exception formatter that when applied with this + * method name and out-of-bounds arguments returns a runtime + * exception. If {@code null} or returns {@code null} then, it is as + * if an exception formatter produced from an invocation of + * {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used + * instead (though it may be more efficient). + * Exceptions thrown by the formatter are relayed to the caller. + * @return {@code fromIndex} if the sub-range within bounds of the range + * @throws X if the sub-range is out of bounds and the exception factory + * function is non-{@code null} + * @throws IndexOutOfBoundsException if the sub-range is out of bounds and + * the exception factory function is {@code null} + * @since 16 + */ + public static + long checkFromIndexSize(long fromIndex, long size, long length, + BiFunction, X> oobef) { if ((length | fromIndex | size) < 0 || size > length - fromIndex) throw outOfBoundsCheckFromIndexSize(oobef, fromIndex, size, length); return fromIndex; diff --git a/src/java.base/share/classes/sun/security/util/ArrayUtil.java b/src/java.base/share/classes/sun/security/util/ArrayUtil.java index 331c10079cb91..cf48a82c19ab1 100644 --- a/src/java.base/share/classes/sun/security/util/ArrayUtil.java +++ b/src/java.base/share/classes/sun/security/util/ArrayUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, 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 @@ -37,7 +37,7 @@ public final class ArrayUtil { - private static final BiFunction, + private static final BiFunction, ArrayIndexOutOfBoundsException> AIOOBE_SUPPLIER = Preconditions.outOfBoundsExceptionFormatter (ArrayIndexOutOfBoundsException::new); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java index 285a8732685ed..4c8b85af2a12a 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java @@ -436,6 +436,7 @@ public CheckGraalIntrinsics() { "java/lang/Math.copySign(FF)F", "java/lang/Math.signum(D)D", "java/lang/Math.signum(F)F", + "jdk/internal/util/Preconditions.checkIndex(JJLjava/util/function/BiFunction;)J", "sun/security/provider/MD5.implCompress0([BI)V"); if (config.useBase64Intrinsics()) { // Currently implemented on ppc64le only, but could be implemented on others diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestCheckIndex.java b/test/hotspot/jtreg/compiler/intrinsics/TestCheckIndex.java new file mode 100644 index 0000000000000..7f630b1e119bb --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/TestCheckIndex.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2020, Red Hat, Inc. 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 + * @bug 8255150 + * @summary Add utility methods to check long indexes and ranges + * @requires vm.compiler2.enabled + * @requires vm.compMode != "Xcomp" + * @library /test/lib / + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * + * @run main/othervm -ea -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-BackgroundCompilation TestCheckIndex + * + */ + +import java.util.Objects; +import sun.hotspot.WhiteBox; +import java.lang.reflect.Method; +import compiler.whitebox.CompilerWhiteBoxTest; + +public class TestCheckIndex { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + public static void main(String[] args) throws NoSuchMethodException { + Objects.checkIndex(0, 10); // Load class + Method m1 = TestCheckIndex.class.getDeclaredMethod("test1", int.class, int.class); + Method m2 = TestCheckIndex.class.getDeclaredMethod("test2", long.class, long.class); + Method m3 = TestCheckIndex.class.getDeclaredMethod("test3", int.class, int.class); + Method m4 = TestCheckIndex.class.getDeclaredMethod("test4", long.class, long.class); + assert m1 != null && m2 != null && m3 != null && m4 != null; + WHITE_BOX.enqueueMethodForCompilation(m1, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!WHITE_BOX.isMethodCompiled(m1)) { + throw new RuntimeException("should be compiled"); + } + WHITE_BOX.enqueueMethodForCompilation(m2, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!WHITE_BOX.isMethodCompiled(m2)) { + throw new RuntimeException("should be compiled"); + } + WHITE_BOX.enqueueMethodForCompilation(m3, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!WHITE_BOX.isMethodCompiled(m3)) { + throw new RuntimeException("should be compiled"); + } + WHITE_BOX.enqueueMethodForCompilation(m4, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!WHITE_BOX.isMethodCompiled(m4)) { + throw new RuntimeException("should be compiled"); + } + + if (test1(0, 10) != 0) { + throw new RuntimeException("incorrect result"); + } + if (!WHITE_BOX.isMethodCompiled(m1)) { + throw new RuntimeException("should still be compiled"); + } + if (test2(0, 10) != 0) { + throw new RuntimeException("incorrect result"); + } + if (!WHITE_BOX.isMethodCompiled(m2)) { + throw new RuntimeException("should still be compiled"); + } + + try { + test1(0, -10); + throw new RuntimeException("exception not thrown"); + } catch (IndexOutOfBoundsException ioobe) { + } + if (WHITE_BOX.isMethodCompiled(m1)) { + throw new RuntimeException("should have deoptimized"); + } + try { + test2(0, -10); + throw new RuntimeException("exception not thrown"); + } catch (IndexOutOfBoundsException ioobe) { + } + if (WHITE_BOX.isMethodCompiled(m2)) { + throw new RuntimeException("should have deoptimized"); + } + + try { + test3(42, 10); + throw new RuntimeException("exception not thrown"); + } catch (IndexOutOfBoundsException ioobe) { + } + if (WHITE_BOX.isMethodCompiled(m3)) { + throw new RuntimeException("should have deoptimized"); + } + try { + test4(42, 10); + throw new RuntimeException("exception not thrown"); + } catch (IndexOutOfBoundsException ioobe) { + } + if (WHITE_BOX.isMethodCompiled(m4)) { + throw new RuntimeException("should have deoptimized"); + } + } + + static int test1(int index, int length) { + return Objects.checkIndex(index, length); + } + + static long test2(long index, long length) { + return Objects.checkIndex(index, length); + } + + static int test3(int index, int length) { + return Objects.checkIndex(index, length); + } + + static long test4(long index, long length) { + return Objects.checkIndex(index, length); + } +} diff --git a/test/jdk/java/util/Objects/CheckIndex.java b/test/jdk/java/util/Objects/CheckIndex.java index 594e806dcd0e0..8baa290b08e92 100644 --- a/test/jdk/java/util/Objects/CheckIndex.java +++ b/test/jdk/java/util/Objects/CheckIndex.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, 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 @@ -23,7 +23,7 @@ /** * @test - * @summary Objects.checkIndex/jdk.internal.util.Preconditions.checkIndex tests + * @summary Objects.checkIndex/jdk.internal.util.Preconditions.checkIndex tests for int values * @run testng CheckIndex * @bug 8135248 8142493 8155794 * @modules java.base/jdk.internal.util @@ -50,7 +50,7 @@ public AssertingOutOfBoundsException(String message) { } } - static BiFunction, AssertingOutOfBoundsException> assertingOutOfBounds( + static BiFunction, AssertingOutOfBoundsException> assertingOutOfBounds( String message, String expCheckKind, Integer... expArgs) { return (checkKind, args) -> { assertEquals(checkKind, expCheckKind); @@ -64,7 +64,7 @@ static BiFunction, AssertingOutOfBoundsException> assertin }; } - static BiFunction, AssertingOutOfBoundsException> assertingOutOfBoundsReturnNull( + static BiFunction, AssertingOutOfBoundsException> assertingOutOfBoundsReturnNull( String expCheckKind, Integer... expArgs) { return (checkKind, args) -> { assertEquals(checkKind, expCheckKind); @@ -86,11 +86,7 @@ static Object[][] checkIndexProvider() { l.add(new Object[]{index, length, withinBounds}); } } - return l.toArray(new Object[0][0]); - } - - interface X { - int apply(int a, int b, int c); + return l.toArray(Object[][]::new); } @Test(dataProvider = "checkIndexProvider") @@ -152,7 +148,7 @@ static Object[][] checkFromToIndexProvider() { } } } - return l.toArray(new Object[0][0]); + return l.toArray(Object[][]::new); } @Test(dataProvider = "checkFromToIndexProvider") @@ -221,7 +217,7 @@ static Object[][] checkFromIndexSizeProvider() { } } } - return l.toArray(new Object[0][0]); + return l.toArray(Object[][]::new); } @Test(dataProvider = "checkFromIndexSizeProvider") @@ -269,7 +265,7 @@ public void testCheckFromIndexSize(int fromIndex, int size, int length, boolean @Test public void uniqueMessagesForCheckKinds() { - BiFunction, IndexOutOfBoundsException> f = + BiFunction, IndexOutOfBoundsException> f = Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new); List messages = new ArrayList<>(); diff --git a/test/jdk/java/util/Objects/CheckLongIndex.java b/test/jdk/java/util/Objects/CheckLongIndex.java new file mode 100644 index 0000000000000..1b7694c5ef1d6 --- /dev/null +++ b/test/jdk/java/util/Objects/CheckLongIndex.java @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2020, 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 + * 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 Objects.checkIndex/jdk.internal.util.Preconditions.checkIndex tests for long values + * @run testng CheckLongIndex + * @modules java.base/jdk.internal.util + */ + +import jdk.internal.util.Preconditions; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.LongSupplier; + +import static org.testng.Assert.*; + +public class CheckLongIndex { + + static class AssertingOutOfBoundsException extends RuntimeException { + public AssertingOutOfBoundsException(String message) { + super(message); + } + } + + static BiFunction, AssertingOutOfBoundsException> assertingOutOfBounds( + String message, String expCheckKind, Long... expArgs) { + return (checkKind, args) -> { + assertEquals(checkKind, expCheckKind); + assertEquals(args, List.of(expArgs)); + try { + args.clear(); + fail("Out of bounds List argument should be unmodifiable"); + } catch (Exception e) { + } + return new AssertingOutOfBoundsException(message); + }; + } + + static BiFunction, AssertingOutOfBoundsException> assertingOutOfBoundsReturnNull( + String expCheckKind, Long... expArgs) { + return (checkKind, args) -> { + assertEquals(checkKind, expCheckKind); + assertEquals(args, List.of(expArgs)); + return null; + }; + } + + static final long[] VALUES = {0, 1, Long.MAX_VALUE - 1, Long.MAX_VALUE, -1, Long.MIN_VALUE + 1, Long.MIN_VALUE}; + + @DataProvider + static Object[][] checkIndexProvider() { + List l = new ArrayList<>(); + for (long index : VALUES) { + for (long length : VALUES) { + boolean withinBounds = index >= 0 && + length >= 0 && + index < length; + l.add(new Object[]{index, length, withinBounds}); + } + } + return l.toArray(Object[][]::new); + } + + @Test(dataProvider = "checkIndexProvider") + public void testCheckIndex(long index, long length, boolean withinBounds) { + String expectedMessage = withinBounds + ? null + : Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new). + apply("checkIndex", List.of(index, length)).getMessage(); + + BiConsumer, LongSupplier> checker = (ec, s) -> { + try { + long rIndex = s.getAsLong(); + if (!withinBounds) + fail(String.format( + "Index %d is out of bounds of [0, %d), but was reported to be within bounds", index, length)); + assertEquals(rIndex, index); + } + catch (RuntimeException e) { + assertTrue(ec.isInstance(e)); + if (withinBounds) + fail(String.format( + "Index %d is within bounds of [0, %d), but was reported to be out of bounds", index, length)); + else + assertEquals(e.getMessage(), expectedMessage); + } + }; + + checker.accept(AssertingOutOfBoundsException.class, + () -> Preconditions.checkIndex(index, length, + assertingOutOfBounds(expectedMessage, "checkIndex", index, length))); + checker.accept(IndexOutOfBoundsException.class, + () -> Preconditions.checkIndex(index, length, + assertingOutOfBoundsReturnNull("checkIndex", index, length))); + checker.accept(IndexOutOfBoundsException.class, + () -> Preconditions.checkIndex(index, length, null)); + checker.accept(IndexOutOfBoundsException.class, + () -> Objects.checkIndex(index, length)); + checker.accept(ArrayIndexOutOfBoundsException.class, + () -> Preconditions.checkIndex(index, length, + Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new))); + checker.accept(StringIndexOutOfBoundsException.class, + () -> Preconditions.checkIndex(index, length, + Preconditions.outOfBoundsExceptionFormatter(StringIndexOutOfBoundsException::new))); + } + + + @DataProvider + static Object[][] checkFromToIndexProvider() { + List l = new ArrayList<>(); + for (long fromIndex : VALUES) { + for (long toIndex : VALUES) { + for (long length : VALUES) { + boolean withinBounds = fromIndex >= 0 && + toIndex >= 0 && + length >= 0 && + fromIndex <= toIndex && + toIndex <= length; + l.add(new Object[]{fromIndex, toIndex, length, withinBounds}); + } + } + } + return l.toArray(Object[][]::new); + } + + @Test(dataProvider = "checkFromToIndexProvider") + public void testCheckFromToIndex(long fromIndex, long toIndex, long length, boolean withinBounds) { + String expectedMessage = withinBounds + ? null + : Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new). + apply("checkFromToIndex", List.of(fromIndex, toIndex, length)).getMessage(); + + BiConsumer, LongSupplier> check = (ec, s) -> { + try { + long rIndex = s.getAsLong(); + if (!withinBounds) + fail(String.format( + "Range [%d, %d) is out of bounds of [0, %d), but was reported to be withing bounds", fromIndex, toIndex, length)); + assertEquals(rIndex, fromIndex); + } + catch (RuntimeException e) { + assertTrue(ec.isInstance(e)); + if (withinBounds) + fail(String.format( + "Range [%d, %d) is within bounds of [0, %d), but was reported to be out of bounds", fromIndex, toIndex, length)); + else + assertEquals(e.getMessage(), expectedMessage); + } + }; + + check.accept(AssertingOutOfBoundsException.class, + () -> Preconditions.checkFromToIndex(fromIndex, toIndex, length, + assertingOutOfBounds(expectedMessage, "checkFromToIndex", fromIndex, toIndex, length))); + check.accept(IndexOutOfBoundsException.class, + () -> Preconditions.checkFromToIndex(fromIndex, toIndex, length, + assertingOutOfBoundsReturnNull("checkFromToIndex", fromIndex, toIndex, length))); + check.accept(IndexOutOfBoundsException.class, + () -> Preconditions.checkFromToIndex(fromIndex, toIndex, length, null)); + check.accept(IndexOutOfBoundsException.class, + () -> Objects.checkFromToIndex(fromIndex, toIndex, length)); + check.accept(ArrayIndexOutOfBoundsException.class, + () -> Preconditions.checkFromToIndex(fromIndex, toIndex, length, + Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new))); + check.accept(StringIndexOutOfBoundsException.class, + () -> Preconditions.checkFromToIndex(fromIndex, toIndex, length, + Preconditions.outOfBoundsExceptionFormatter(StringIndexOutOfBoundsException::new))); + } + + + @DataProvider + static Object[][] checkFromIndexSizeProvider() { + List l = new ArrayList<>(); + for (long fromIndex : VALUES) { + for (long size : VALUES) { + for (long length : VALUES) { + long toIndex = fromIndex + size; + + boolean withinBounds = fromIndex >= 0L && + size >= 0L && + length >= 0L && + fromIndex <= toIndex && // overflow + toIndex <= length; + l.add(new Object[]{fromIndex, size, length, withinBounds}); + } + } + } + return l.toArray(Object[][]::new); + } + + @Test(dataProvider = "checkFromIndexSizeProvider") + public void testCheckFromIndexSize(long fromIndex, long size, long length, boolean withinBounds) { + String expectedMessage = withinBounds + ? null + : Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new). + apply("checkFromIndexSize", List.of(fromIndex, size, length)).getMessage(); + + BiConsumer, LongSupplier> check = (ec, s) -> { + try { + long rIndex = s.getAsLong(); + if (!withinBounds) + fail(String.format( + "Range [%d, %d + %d) is out of bounds of [0, %d), but was reported to be withing bounds", fromIndex, fromIndex, size, length)); + assertEquals(rIndex, fromIndex); + } + catch (RuntimeException e) { + assertTrue(ec.isInstance(e)); + if (withinBounds) + fail(String.format( + "Range [%d, %d + %d) is within bounds of [0, %d), but was reported to be out of bounds", fromIndex, fromIndex, size, length)); + else + assertEquals(e.getMessage(), expectedMessage); + } + }; + + check.accept(AssertingOutOfBoundsException.class, + () -> Preconditions.checkFromIndexSize(fromIndex, size, length, + assertingOutOfBounds(expectedMessage, "checkFromIndexSize", fromIndex, size, length))); + check.accept(IndexOutOfBoundsException.class, + () -> Preconditions.checkFromIndexSize(fromIndex, size, length, + assertingOutOfBoundsReturnNull("checkFromIndexSize", fromIndex, size, length))); + check.accept(IndexOutOfBoundsException.class, + () -> Preconditions.checkFromIndexSize(fromIndex, size, length, null)); + check.accept(IndexOutOfBoundsException.class, + () -> Objects.checkFromIndexSize(fromIndex, size, length)); + check.accept(ArrayIndexOutOfBoundsException.class, + () -> Preconditions.checkFromIndexSize(fromIndex, size, length, + Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new))); + check.accept(StringIndexOutOfBoundsException.class, + () -> Preconditions.checkFromIndexSize(fromIndex, size, length, + Preconditions.outOfBoundsExceptionFormatter(StringIndexOutOfBoundsException::new))); + } + + @Test + public void uniqueMessagesForCheckKinds() { + BiFunction, IndexOutOfBoundsException> f = + Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new); + + List messages = new ArrayList<>(); + // Exact arguments + messages.add(f.apply("checkIndex", List.of(-1L, 0L)).getMessage()); + messages.add(f.apply("checkFromToIndex", List.of(-1L, 0L, 0L)).getMessage()); + messages.add(f.apply("checkFromIndexSize", List.of(-1L, 0L, 0L)).getMessage()); + // Unknown check kind + messages.add(f.apply("checkUnknown", List.of(-1L, 0L, 0L)).getMessage()); + // Known check kind with more arguments + messages.add(f.apply("checkIndex", List.of(-1L, 0L, 0L)).getMessage()); + messages.add(f.apply("checkFromToIndex", List.of(-1L, 0L, 0L, 0L)).getMessage()); + messages.add(f.apply("checkFromIndexSize", List.of(-1L, 0L, 0L, 0L)).getMessage()); + // Known check kind with fewer arguments + messages.add(f.apply("checkIndex", List.of(-1L)).getMessage()); + messages.add(f.apply("checkFromToIndex", List.of(-1L, 0L)).getMessage()); + messages.add(f.apply("checkFromIndexSize", List.of(-1L, 0L)).getMessage()); + // Null arguments + messages.add(f.apply(null, null).getMessage()); + messages.add(f.apply("checkNullArguments", null).getMessage()); + messages.add(f.apply(null, List.of(-1L)).getMessage()); + + assertEquals(messages.size(), messages.stream().distinct().count()); + } +}