8255150: Add utility methods to check long indexes and ranges

Co-authored-by: Paul Sandoz <psandoz@openjdk.org>
Reviewed-by: jvernee, dlong, vlivanov
This commit is contained in:
Roland Westrelin 2020-11-17 10:37:27 +00:00
parent 6d878565f8
commit a7422ac2f4
30 changed files with 893 additions and 51 deletions

View File

@ -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
//

View File

@ -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

View File

@ -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" %}

View File

@ -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.

View File

@ -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));

View File

@ -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)
%{

View File

@ -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) \

View File

@ -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;

View File

@ -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);

View File

@ -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 {

View File

@ -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()) {

View File

@ -61,6 +61,7 @@ macro(CallLeafNoFP)
macro(CallRuntime)
macro(CallStaticJava)
macro(CastII)
macro(CastLL)
macro(CastX2P)
macro(CastP2X)
macro(CastPP)

View File

@ -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<jint>(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.)

View File

@ -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);

View File

@ -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();

View File

@ -52,6 +52,7 @@ class CallNode;
class CallRuntimeNode;
class CallStaticJavaNode;
class CastIINode;
class CastLLNode;
class CatchNode;
class CatchProjNode;
class CheckCastPPNode;
@ -671,6 +672,7 @@ public:
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 @@ public:
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)

View File

@ -274,6 +274,7 @@ public:
// 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);

View File

@ -563,8 +563,27 @@ void CmpNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *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.

View File

@ -140,6 +140,8 @@ public:
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

View File

@ -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<jint>(lo), checked_cast<jint>(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-------------------------------------------

View File

@ -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 @@ public:
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 @@ public:
#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 @@ public:
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 @@ public:
//------------------------------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 @@ public:
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;

View File

@ -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 class IndexOutOfBoundsException extends RuntimeException {
public IndexOutOfBoundsException(int index) {
super("Index out of range: " + index);
}
/**
* Constructs a new {@code IndexOutOfBoundsException} class with an
* argument indicating the illegal index.
*
* <p>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);
}
}

View File

@ -2146,7 +2146,7 @@ public abstract class VarHandle implements Constable {
UNSAFE.fullFence();
}
static final BiFunction<String, List<Integer>, ArrayIndexOutOfBoundsException>
static final BiFunction<String, List<Number>, ArrayIndexOutOfBoundsException>
AIOOBE_SUPPLIER = Preconditions.outOfBoundsExceptionFormatter(
new Function<String, ArrayIndexOutOfBoundsException>() {
@Override

View File

@ -411,4 +411,79 @@ public final class Objects {
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).
*
* <p>The {@code index} is defined to be out of bounds if any of the
* following inequalities is true:
* <ul>
* <li>{@code index < 0}</li>
* <li>{@code index >= length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* @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).
*
* <p>The sub-range is defined to be out of bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code fromIndex > toIndex}</li>
* <li>{@code toIndex > length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* @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).
*
* <p>The sub-range is defined to be out of bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code size < 0}</li>
* <li>{@code fromIndex + size > length}, taking into account integer overflow</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* @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);
}
}

View File

@ -54,10 +54,10 @@ public class Preconditions {
* @return the runtime exception
*/
private static RuntimeException outOfBounds(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobef,
BiFunction<String, List<Number>, ? extends RuntimeException> oobef,
String checkKind,
Integer... args) {
List<Integer> largs = List.of(args);
Number... args) {
List<Number> largs = List.of(args);
RuntimeException e = oobef == null
? null : oobef.apply(checkKind, largs);
return e == null
@ -65,23 +65,41 @@ public class Preconditions {
}
private static RuntimeException outOfBoundsCheckIndex(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
int index, int length) {
return outOfBounds(oobe, "checkIndex", index, length);
}
private static RuntimeException outOfBoundsCheckFromToIndex(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
int fromIndex, int toIndex, int length) {
return outOfBounds(oobe, "checkFromToIndex", fromIndex, toIndex, length);
}
private static RuntimeException outOfBoundsCheckFromIndexSize(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
int fromIndex, int size, int length) {
return outOfBounds(oobe, "checkFromIndexSize", fromIndex, size, length);
}
private static RuntimeException outOfBoundsCheckIndex(
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
long index, long length) {
return outOfBounds(oobe, "checkIndex", index, length);
}
private static RuntimeException outOfBoundsCheckFromToIndex(
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
long fromIndex, long toIndex, long length) {
return outOfBounds(oobe, "checkFromToIndex", fromIndex, toIndex, length);
}
private static RuntimeException outOfBoundsCheckFromIndexSize(
BiFunction<String, List<Number>, ? 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 @@ public class Preconditions {
*
* <p>The exception formatter accepts two arguments: a {@code String}
* describing the out-of-bounds range check that failed, referred to as the
* <em>check kind</em>; and a {@code List<Integer>} containing the
* out-of-bound integer values that failed the check. The list of
* <em>check kind</em>; and a {@code List<Number>} containing the
* out-of-bound integral values that failed the check. The list of
* out-of-bound values is not modified.
*
* <p>Three check kinds are supported {@code checkIndex},
@ -102,7 +120,7 @@ public class Preconditions {
* {@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 @@ public class Preconditions {
* {@code static final} field as follows:
* <pre>{@code
* static final
* BiFunction<String, List<Integer>, ArrayIndexOutOfBoundsException> AIOOBEF =
* BiFunction<String, List<Number>, ArrayIndexOutOfBoundsException> AIOOBEF =
* outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new);
* }</pre>
* The formatter instance {@code AIOOBEF} may be passed as an argument to an
@ -150,18 +168,18 @@ public class Preconditions {
* @return the out-of-bounds exception formatter
*/
public static <X extends RuntimeException>
BiFunction<String, List<Integer>, X> outOfBoundsExceptionFormatter(Function<String, X> f) {
BiFunction<String, List<Number>, X> outOfBoundsExceptionFormatter(Function<String, X> f) {
// Use anonymous class to avoid bootstrap issues if this method is
// used early in startup
return new BiFunction<String, List<Integer>, X>() {
return new BiFunction<String, List<Number>, X>() {
@Override
public X apply(String checkKind, List<Integer> args) {
public X apply(String checkKind, List<Number> args) {
return f.apply(outOfBoundsMessage(checkKind, args));
}
};
}
private static String outOfBoundsMessage(String checkKind, List<Integer> args) {
private static String outOfBoundsMessage(String checkKind, List<? extends Number> args) {
if (checkKind == null && args == null) {
return String.format("Range check failed");
} else if (checkKind == null) {
@ -213,7 +231,7 @@ public class Preconditions {
* <p>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 <X> the type of runtime exception to throw if the arguments are
@ -243,7 +261,7 @@ public class Preconditions {
@IntrinsicCandidate
public static <X extends RuntimeException>
int checkIndex(int index, int length,
BiFunction<String, List<Integer>, X> oobef) {
BiFunction<String, List<Number>, X> oobef) {
if (index < 0 || index >= length)
throw outOfBoundsCheckIndex(oobef, index, length);
return index;
@ -266,7 +284,7 @@ public class Preconditions {
* <p>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 <X> the type of runtime exception to throw if the arguments are
@ -290,7 +308,7 @@ public class Preconditions {
*/
public static <X extends RuntimeException>
int checkFromToIndex(int fromIndex, int toIndex, int length,
BiFunction<String, List<Integer>, X> oobef) {
BiFunction<String, List<Number>, X> oobef) {
if (fromIndex < 0 || fromIndex > toIndex || toIndex > length)
throw outOfBoundsCheckFromToIndex(oobef, fromIndex, toIndex, length);
return fromIndex;
@ -313,7 +331,7 @@ public class Preconditions {
* <p>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 @@ public class Preconditions {
*/
public static <X extends RuntimeException>
int checkFromIndexSize(int fromIndex, int size, int length,
BiFunction<String, List<Integer>, X> oobef) {
BiFunction<String, List<Number>, 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).
*
* <p>The {@code index} is defined to be out of bounds if any of the
* following inequalities is true:
* <ul>
* <li>{@code index < 0}</li>
* <li>{@code index >= length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>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 <X> 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 <X extends RuntimeException>
long checkIndex(long index, long length,
BiFunction<String, List<Number>, 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).
*
* <p>The sub-range is defined to be out of bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code fromIndex > toIndex}</li>
* <li>{@code toIndex > length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>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 <X> 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 <X extends RuntimeException>
long checkFromToIndex(long fromIndex, long toIndex, long length,
BiFunction<String, List<Number>, 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).
*
* <p>The sub-range is defined to be out of bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code size < 0}</li>
* <li>{@code fromIndex + size > length}, taking into account integer overflow</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>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 <X> 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 <X extends RuntimeException>
long checkFromIndexSize(long fromIndex, long size, long length,
BiFunction<String, List<Number>, X> oobef) {
if ((length | fromIndex | size) < 0 || size > length - fromIndex)
throw outOfBoundsCheckFromIndexSize(oobef, fromIndex, size, length);
return fromIndex;

View File

@ -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 @@ import jdk.internal.util.Preconditions;
public final class ArrayUtil {
private static final BiFunction<String, List<Integer>,
private static final BiFunction<String, List<Number>,
ArrayIndexOutOfBoundsException> AIOOBE_SUPPLIER =
Preconditions.outOfBoundsExceptionFormatter
(ArrayIndexOutOfBoundsException::new);

View File

@ -436,6 +436,7 @@ public class CheckGraalIntrinsics extends GraalTest {
"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

View File

@ -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);
}
}

View File

@ -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 class CheckIndex {
}
}
static BiFunction<String, List<Integer>, AssertingOutOfBoundsException> assertingOutOfBounds(
static BiFunction<String, List<Number>, AssertingOutOfBoundsException> assertingOutOfBounds(
String message, String expCheckKind, Integer... expArgs) {
return (checkKind, args) -> {
assertEquals(checkKind, expCheckKind);
@ -64,7 +64,7 @@ public class CheckIndex {
};
}
static BiFunction<String, List<Integer>, AssertingOutOfBoundsException> assertingOutOfBoundsReturnNull(
static BiFunction<String, List<Number>, AssertingOutOfBoundsException> assertingOutOfBoundsReturnNull(
String expCheckKind, Integer... expArgs) {
return (checkKind, args) -> {
assertEquals(checkKind, expCheckKind);
@ -86,11 +86,7 @@ public class CheckIndex {
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 @@ public class CheckIndex {
}
}
}
return l.toArray(new Object[0][0]);
return l.toArray(Object[][]::new);
}
@Test(dataProvider = "checkFromToIndexProvider")
@ -221,7 +217,7 @@ public class CheckIndex {
}
}
}
return l.toArray(new Object[0][0]);
return l.toArray(Object[][]::new);
}
@Test(dataProvider = "checkFromIndexSizeProvider")
@ -269,7 +265,7 @@ public class CheckIndex {
@Test
public void uniqueMessagesForCheckKinds() {
BiFunction<String, List<Integer>, IndexOutOfBoundsException> f =
BiFunction<String, List<Number>, IndexOutOfBoundsException> f =
Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new);
List<String> messages = new ArrayList<>();

View File

@ -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<String, List<Number>, 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<Long> argument should be unmodifiable");
} catch (Exception e) {
}
return new AssertingOutOfBoundsException(message);
};
}
static BiFunction<String, List<Number>, 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<Object[]> 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<Class<? extends RuntimeException>, 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<Object[]> 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<Class<? extends RuntimeException>, 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<Object[]> 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<Class<? extends RuntimeException>, 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<String, List<Number>, IndexOutOfBoundsException> f =
Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new);
List<String> 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());
}
}