mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-28 16:50:10 +00:00
8378431: Move input validation checks to Java for java.lang.StringUTF16 intrinsics
Reviewed-by: dfenacci, rgiulietti, rriggs
This commit is contained in:
parent
d8f19bf961
commit
706fbb3044
@ -368,10 +368,10 @@ class methodHandle;
|
||||
do_intrinsic(_inflateStringB, java_lang_StringLatin1, inflate_name, inflateB_signature, F_S) \
|
||||
do_signature(inflateB_signature, "([BI[BII)V") \
|
||||
do_intrinsic(_toBytesStringU, java_lang_StringUTF16, toBytes_name, toBytesU_signature, F_S) \
|
||||
do_name( toBytes_name, "toBytes") \
|
||||
do_name( toBytes_name, "toBytes0") \
|
||||
do_signature(toBytesU_signature, "([CII)[B") \
|
||||
do_intrinsic(_getCharsStringU, java_lang_StringUTF16, getCharsU_name, getCharsU_signature, F_S) \
|
||||
do_name( getCharsU_name, "getChars") \
|
||||
do_name( getCharsU_name, "getChars0") \
|
||||
do_signature(getCharsU_signature, "([BII[CI)V") \
|
||||
do_intrinsic(_getCharStringU, java_lang_StringUTF16, getChar_name, getCharStringU_signature, F_S) \
|
||||
do_signature(getCharStringU_signature, "([BI)C") \
|
||||
|
||||
@ -843,6 +843,22 @@ void LibraryCallKit::set_result(RegionNode* region, PhiNode* value) {
|
||||
assert(value->type()->basic_type() == result()->bottom_type()->basic_type(), "sanity");
|
||||
}
|
||||
|
||||
RegionNode* LibraryCallKit::create_bailout() {
|
||||
RegionNode* bailout = new RegionNode(1);
|
||||
record_for_igvn(bailout);
|
||||
return bailout;
|
||||
}
|
||||
|
||||
bool LibraryCallKit::check_bailout(RegionNode* bailout) {
|
||||
if (bailout->req() > 1) {
|
||||
bailout = _gvn.transform(bailout)->as_Region();
|
||||
Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr));
|
||||
Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic"));
|
||||
C->root()->add_req(halt);
|
||||
}
|
||||
return stopped();
|
||||
}
|
||||
|
||||
//------------------------------generate_guard---------------------------
|
||||
// Helper function for generating guarded fast-slow graph structures.
|
||||
// The given 'test', if true, guards a slow path. If the test fails
|
||||
@ -951,36 +967,19 @@ void LibraryCallKit::generate_string_range_check(Node* array,
|
||||
Node* offset,
|
||||
Node* count,
|
||||
bool char_count,
|
||||
bool halt_on_oob) {
|
||||
RegionNode* region) {
|
||||
if (stopped()) {
|
||||
return; // already stopped
|
||||
}
|
||||
RegionNode* bailout = new RegionNode(1);
|
||||
record_for_igvn(bailout);
|
||||
if (char_count) {
|
||||
// Convert char count to byte count
|
||||
count = _gvn.transform(new LShiftINode(count, intcon(1)));
|
||||
}
|
||||
|
||||
// Offset and count must not be negative
|
||||
generate_negative_guard(offset, bailout, nullptr, halt_on_oob);
|
||||
generate_negative_guard(count, bailout, nullptr, halt_on_oob);
|
||||
generate_negative_guard(offset, region, nullptr, true);
|
||||
generate_negative_guard(count, region, nullptr, true);
|
||||
// Offset + count must not exceed length of array
|
||||
generate_limit_guard(offset, count, load_array_length(array), bailout, halt_on_oob);
|
||||
|
||||
if (bailout->req() > 1) {
|
||||
if (halt_on_oob) {
|
||||
bailout = _gvn.transform(bailout)->as_Region();
|
||||
Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr));
|
||||
Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic"));
|
||||
C->root()->add_req(halt);
|
||||
} else {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(_gvn.transform(bailout));
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_maybe_recompile);
|
||||
}
|
||||
}
|
||||
generate_limit_guard(offset, count, load_array_length(array), region, true);
|
||||
}
|
||||
|
||||
Node* LibraryCallKit::current_thread_helper(Node*& tls_output, ByteSize handle_offset,
|
||||
@ -1139,10 +1138,6 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) {
|
||||
//------------------------------inline_countPositives------------------------------
|
||||
// int java.lang.StringCoding#countPositives0(byte[] ba, int off, int len)
|
||||
bool LibraryCallKit::inline_countPositives() {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(callee()->signature()->size() == 3, "countPositives has 3 parameters");
|
||||
// no receiver since it is static method
|
||||
Node* ba = argument(0);
|
||||
@ -1150,8 +1145,9 @@ bool LibraryCallKit::inline_countPositives() {
|
||||
Node* len = argument(2);
|
||||
|
||||
ba = must_be_not_null(ba, true);
|
||||
generate_string_range_check(ba, offset, len, false, true);
|
||||
if (stopped()) {
|
||||
RegionNode* bailout = create_bailout();
|
||||
generate_string_range_check(ba, offset, len, false, bailout);
|
||||
if (check_bailout(bailout)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1283,9 +1279,6 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) {
|
||||
|
||||
//-----------------------------inline_string_indexOfI-----------------------
|
||||
bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
if (!Matcher::match_rule_supported(Op_StrIndexOf)) {
|
||||
return false;
|
||||
}
|
||||
@ -1307,9 +1300,10 @@ bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) {
|
||||
Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE);
|
||||
|
||||
// Range checks
|
||||
generate_string_range_check(src, src_offset, src_count, ae != StrIntrinsicNode::LL, true);
|
||||
generate_string_range_check(tgt, intcon(0), tgt_count, ae == StrIntrinsicNode::UU, true);
|
||||
if (stopped()) {
|
||||
RegionNode* bailout = create_bailout();
|
||||
generate_string_range_check(src, src_offset, src_count, ae != StrIntrinsicNode::LL, bailout);
|
||||
generate_string_range_check(tgt, intcon(0), tgt_count, ae == StrIntrinsicNode::UU, bailout);
|
||||
if (check_bailout(bailout)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1404,7 +1398,11 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) {
|
||||
Node* src_count = _gvn.transform(new SubINode(max, from_index));
|
||||
|
||||
// Range checks
|
||||
generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U, true);
|
||||
RegionNode* bailout = create_bailout();
|
||||
generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U, bailout);
|
||||
if (check_bailout(bailout)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for int_ch >= 0
|
||||
Node* int_ch_cmp = _gvn.transform(new CmpINode(int_ch, intcon(0)));
|
||||
@ -1454,9 +1452,6 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) {
|
||||
// void StringLatin1.inflate0(byte[] src, int srcOff, char[] dst, int dstOff, int len)
|
||||
// void StringLatin1.inflate0(byte[] src, int srcOff, byte[] dst, int dstOff, int len)
|
||||
bool LibraryCallKit::inline_string_copy(bool compress) {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
int nargs = 5; // 2 oops, 3 ints
|
||||
assert(callee()->signature()->size() == nargs, "string copy has 5 arguments");
|
||||
|
||||
@ -1495,9 +1490,10 @@ bool LibraryCallKit::inline_string_copy(bool compress) {
|
||||
}
|
||||
|
||||
// Range checks
|
||||
generate_string_range_check(src, src_offset, length, convert_src, true);
|
||||
generate_string_range_check(dst, dst_offset, length, convert_dst, true);
|
||||
if (stopped()) {
|
||||
RegionNode* bailout = create_bailout();
|
||||
generate_string_range_check(src, src_offset, length, convert_src, bailout);
|
||||
generate_string_range_check(dst, dst_offset, length, convert_dst, bailout);
|
||||
if (check_bailout(bailout)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1545,12 +1541,10 @@ bool LibraryCallKit::inline_string_copy(bool compress) {
|
||||
#endif //_LP64
|
||||
|
||||
//------------------------inline_string_toBytesU--------------------------
|
||||
// public static byte[] StringUTF16.toBytes(char[] value, int off, int len)
|
||||
// public static byte[] StringUTF16.toBytes0(char[] value, int off, int len)
|
||||
bool LibraryCallKit::inline_string_toBytesU() {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
// Get the arguments.
|
||||
assert(callee()->signature()->size() == 3, "character array encoder requires 3 arguments");
|
||||
Node* value = argument(0);
|
||||
Node* offset = argument(1);
|
||||
Node* length = argument(2);
|
||||
@ -1558,30 +1552,18 @@ bool LibraryCallKit::inline_string_toBytesU() {
|
||||
Node* newcopy = nullptr;
|
||||
|
||||
// Set the original stack and the reexecute bit for the interpreter to reexecute
|
||||
// the bytecode that invokes StringUTF16.toBytes() if deoptimization happens.
|
||||
// the bytecode that invokes StringUTF16.toBytes0() if deoptimization happens.
|
||||
{ PreserveReexecuteState preexecs(this);
|
||||
jvms()->set_should_reexecute(true);
|
||||
|
||||
// Check if a null path was taken unconditionally.
|
||||
value = null_check(value);
|
||||
|
||||
RegionNode* bailout = new RegionNode(1);
|
||||
record_for_igvn(bailout);
|
||||
|
||||
// Range checks
|
||||
generate_negative_guard(offset, bailout);
|
||||
generate_negative_guard(length, bailout);
|
||||
generate_limit_guard(offset, length, load_array_length(value), bailout);
|
||||
value = must_be_not_null(value, true);
|
||||
RegionNode* bailout = create_bailout();
|
||||
generate_negative_guard(offset, bailout, nullptr, true);
|
||||
generate_negative_guard(length, bailout, nullptr, true);
|
||||
generate_limit_guard(offset, length, load_array_length(value), bailout, true);
|
||||
// Make sure that resulting byte[] length does not overflow Integer.MAX_VALUE
|
||||
generate_limit_guard(length, intcon(0), intcon(max_jint/2), bailout);
|
||||
|
||||
if (bailout->req() > 1) {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(_gvn.transform(bailout));
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_maybe_recompile);
|
||||
}
|
||||
if (stopped()) {
|
||||
generate_limit_guard(length, intcon(0), intcon(max_jint/2), bailout, true);
|
||||
if (check_bailout(bailout)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1640,12 +1622,9 @@ bool LibraryCallKit::inline_string_toBytesU() {
|
||||
}
|
||||
|
||||
//------------------------inline_string_getCharsU--------------------------
|
||||
// public void StringUTF16.getChars(byte[] src, int srcBegin, int srcEnd, char dst[], int dstBegin)
|
||||
// public void StringUTF16.getChars0(byte[] src, int srcBegin, int srcEnd, char dst[], int dstBegin)
|
||||
bool LibraryCallKit::inline_string_getCharsU() {
|
||||
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(callee()->signature()->size() == 5, "StringUTF16.getChars0() has 5 arguments");
|
||||
// Get the arguments.
|
||||
Node* src = argument(0);
|
||||
Node* src_begin = argument(1);
|
||||
@ -1658,8 +1637,8 @@ bool LibraryCallKit::inline_string_getCharsU() {
|
||||
AllocateArrayNode* alloc = tightly_coupled_allocation(dst);
|
||||
|
||||
// Check if a null path was taken unconditionally.
|
||||
src = null_check(src);
|
||||
dst = null_check(dst);
|
||||
src = must_be_not_null(src, true);
|
||||
dst = must_be_not_null(dst, true);
|
||||
if (stopped()) {
|
||||
return true;
|
||||
}
|
||||
@ -1669,51 +1648,50 @@ bool LibraryCallKit::inline_string_getCharsU() {
|
||||
src_begin = _gvn.transform(new LShiftINode(src_begin, intcon(1)));
|
||||
|
||||
// Range checks
|
||||
generate_string_range_check(src, src_begin, length, true);
|
||||
generate_string_range_check(dst, dst_begin, length, false);
|
||||
if (stopped()) {
|
||||
RegionNode* bailout = create_bailout();
|
||||
generate_string_range_check(src, src_begin, length, true, bailout);
|
||||
generate_string_range_check(dst, dst_begin, length, false, bailout);
|
||||
if (check_bailout(bailout)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!stopped()) {
|
||||
// Calculate starting addresses.
|
||||
Node* src_start = array_element_address(src, src_begin, T_BYTE);
|
||||
Node* dst_start = array_element_address(dst, dst_begin, T_CHAR);
|
||||
// Calculate starting addresses.
|
||||
Node* src_start = array_element_address(src, src_begin, T_BYTE);
|
||||
Node* dst_start = array_element_address(dst, dst_begin, T_CHAR);
|
||||
|
||||
// Check if array addresses are aligned to HeapWordSize
|
||||
const TypeInt* tsrc = gvn().type(src_begin)->is_int();
|
||||
const TypeInt* tdst = gvn().type(dst_begin)->is_int();
|
||||
bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) &&
|
||||
tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
|
||||
// Check if array addresses are aligned to HeapWordSize
|
||||
const TypeInt* tsrc = gvn().type(src_begin)->is_int();
|
||||
const TypeInt* tdst = gvn().type(dst_begin)->is_int();
|
||||
bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) &&
|
||||
tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
|
||||
|
||||
// Figure out which arraycopy runtime method to call (disjoint, uninitialized).
|
||||
const char* copyfunc_name = "arraycopy";
|
||||
address copyfunc_addr = StubRoutines::select_arraycopy_function(T_CHAR, aligned, true, copyfunc_name, true);
|
||||
Node* call = make_runtime_call(RC_LEAF|RC_NO_FP,
|
||||
OptoRuntime::fast_arraycopy_Type(),
|
||||
copyfunc_addr, copyfunc_name, TypeRawPtr::BOTTOM,
|
||||
src_start, dst_start, ConvI2X(length) XTOP);
|
||||
// Do not let reads from the cloned object float above the arraycopy.
|
||||
if (alloc != nullptr) {
|
||||
if (alloc->maybe_set_complete(&_gvn)) {
|
||||
// "You break it, you buy it."
|
||||
InitializeNode* init = alloc->initialization();
|
||||
assert(init->is_complete(), "we just did this");
|
||||
init->set_complete_with_arraycopy();
|
||||
assert(dst->is_CheckCastPP(), "sanity");
|
||||
assert(dst->in(0)->in(0) == init, "dest pinned");
|
||||
}
|
||||
// Do not let stores that initialize this object be reordered with
|
||||
// a subsequent store that would make this object accessible by
|
||||
// other threads.
|
||||
// Record what AllocateNode this StoreStore protects so that
|
||||
// escape analysis can go from the MemBarStoreStoreNode to the
|
||||
// AllocateNode and eliminate the MemBarStoreStoreNode if possible
|
||||
// based on the escape status of the AllocateNode.
|
||||
insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress));
|
||||
} else {
|
||||
insert_mem_bar(Op_MemBarCPUOrder);
|
||||
// Figure out which arraycopy runtime method to call (disjoint, uninitialized).
|
||||
const char* copyfunc_name = "arraycopy";
|
||||
address copyfunc_addr = StubRoutines::select_arraycopy_function(T_CHAR, aligned, true, copyfunc_name, true);
|
||||
Node* call = make_runtime_call(RC_LEAF|RC_NO_FP,
|
||||
OptoRuntime::fast_arraycopy_Type(),
|
||||
copyfunc_addr, copyfunc_name, TypeRawPtr::BOTTOM,
|
||||
src_start, dst_start, ConvI2X(length) XTOP);
|
||||
// Do not let reads from the cloned object float above the arraycopy.
|
||||
if (alloc != nullptr) {
|
||||
if (alloc->maybe_set_complete(&_gvn)) {
|
||||
// "You break it, you buy it."
|
||||
InitializeNode* init = alloc->initialization();
|
||||
assert(init->is_complete(), "we just did this");
|
||||
init->set_complete_with_arraycopy();
|
||||
assert(dst->is_CheckCastPP(), "sanity");
|
||||
assert(dst->in(0)->in(0) == init, "dest pinned");
|
||||
}
|
||||
// Do not let stores that initialize this object be reordered with
|
||||
// a subsequent store that would make this object accessible by
|
||||
// other threads.
|
||||
// Record what AllocateNode this StoreStore protects so that
|
||||
// escape analysis can go from the MemBarStoreStoreNode to the
|
||||
// AllocateNode and eliminate the MemBarStoreStoreNode if possible
|
||||
// based on the escape status of the AllocateNode.
|
||||
insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress));
|
||||
} else {
|
||||
insert_mem_bar(Op_MemBarCPUOrder);
|
||||
}
|
||||
|
||||
C->set_has_split_ifs(true); // Has chance for split-if optimization
|
||||
@ -1725,9 +1703,16 @@ bool LibraryCallKit::inline_string_getCharsU() {
|
||||
// static void StringUTF16.putChar(byte[] val, int index, int c)
|
||||
// static char StringUTF16.getChar(byte[] val, int index)
|
||||
bool LibraryCallKit::inline_string_char_access(bool is_store) {
|
||||
Node* ch;
|
||||
if (is_store) {
|
||||
assert(callee()->signature()->size() == 3, "StringUTF16.putChar() has 3 arguments");
|
||||
ch = argument(2);
|
||||
} else {
|
||||
assert(callee()->signature()->size() == 2, "StringUTF16.getChar() has 2 arguments");
|
||||
ch = nullptr;
|
||||
}
|
||||
Node* value = argument(0);
|
||||
Node* index = argument(1);
|
||||
Node* ch = is_store ? argument(2) : nullptr;
|
||||
|
||||
// This intrinsic accesses byte[] array as char[] array. Computing the offsets
|
||||
// correctly requires matched array shapes.
|
||||
@ -6185,9 +6170,10 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) {
|
||||
}
|
||||
|
||||
// Check source & target bounds
|
||||
generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, true);
|
||||
generate_string_range_check(dst, dst_offset, length, false, true);
|
||||
if (stopped()) {
|
||||
RegionNode* bailout = create_bailout();
|
||||
generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, bailout);
|
||||
generate_string_range_check(dst, dst_offset, length, false, bailout);
|
||||
if (check_bailout(bailout)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -130,6 +130,8 @@ class LibraryCallKit : public GraphKit {
|
||||
virtual int reexecute_sp() { return _reexecute_sp; }
|
||||
|
||||
// Helper functions to inline natives
|
||||
RegionNode* create_bailout();
|
||||
bool check_bailout(RegionNode* bailout);
|
||||
Node* generate_guard(Node* test, RegionNode* region, float true_prob);
|
||||
Node* generate_slow_guard(Node* test, RegionNode* region);
|
||||
Node* generate_fair_guard(Node* test, RegionNode* region);
|
||||
@ -143,7 +145,7 @@ class LibraryCallKit : public GraphKit {
|
||||
bool with_opaque = false);
|
||||
void generate_string_range_check(Node* array, Node* offset,
|
||||
Node* length, bool char_count,
|
||||
bool halt_on_oob = false);
|
||||
RegionNode* region);
|
||||
Node* current_thread_helper(Node* &tls_output, ByteSize handle_offset,
|
||||
bool is_immutable);
|
||||
Node* generate_current_thread(Node* &tls_output);
|
||||
|
||||
@ -67,30 +67,61 @@ final class StringUTF16 {
|
||||
|
||||
// Check the size of a UTF16-coded string
|
||||
// Throw an exception if out of range
|
||||
static int newBytesLength(int len) {
|
||||
if (len < 0) {
|
||||
throw new NegativeArraySizeException();
|
||||
}
|
||||
if (len >= MAX_LENGTH) {
|
||||
throw new OutOfMemoryError("UTF16 String size is " + len +
|
||||
", should be less than " + MAX_LENGTH);
|
||||
}
|
||||
private static int newBytesLength(int len) {
|
||||
checkBytesLength(len);
|
||||
return len << 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided length is a valid UTF-16 string byte array length.
|
||||
*
|
||||
* @param length a UTF-16 string byte array length
|
||||
*
|
||||
* @throws NegativeArraySizeException if {@code length < 0}
|
||||
* @throws OutOfMemoryError if {@code length > (Integer.MAX_VALUE / 2)}
|
||||
*/
|
||||
private static void checkBytesLength(int length) {
|
||||
if (length < 0) {
|
||||
throw new NegativeArraySizeException();
|
||||
}
|
||||
if (length >= MAX_LENGTH) {
|
||||
throw new OutOfMemoryError("UTF16 String size is " + length +
|
||||
", should be less than " + MAX_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given code point to the specified position of the provided
|
||||
* UTF-16 string byte array.
|
||||
* <p>
|
||||
* <b>WARNING: This method does not perform any input validations.</b>
|
||||
*
|
||||
* @param val a UTF-16 string byte array
|
||||
* @param index the index of the character to write the code point to
|
||||
* @param c a code point
|
||||
*/
|
||||
// vmIntrinsics::_putCharStringU
|
||||
@IntrinsicCandidate
|
||||
// intrinsic performs no bounds checks
|
||||
static void putChar(byte[] val, int index, int c) {
|
||||
assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
|
||||
assert val != null && index >= 0 && index < length(val) : "Trusted caller violated input constraints";
|
||||
index <<= 1;
|
||||
val[index++] = (byte)(c >> HI_BYTE_SHIFT);
|
||||
val[index] = (byte)(c >> LO_BYTE_SHIFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the code point at the the specified position of the provided
|
||||
* UTF-16 string byte array}
|
||||
* <p>
|
||||
* <b>WARNING: This method does not perform any input validations.</b>
|
||||
*
|
||||
* @param val a UTF-16 string byte array
|
||||
* @param index the index of the character to get the code point from
|
||||
*/
|
||||
// vmIntrinsics::_getCharStringU
|
||||
@IntrinsicCandidate
|
||||
// intrinsic performs no bounds checks
|
||||
static char getChar(byte[] val, int index) {
|
||||
assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
|
||||
assert val != null && index >= 0 && index < length(val) : "Trusted caller violated input constraints";
|
||||
index <<= 1;
|
||||
return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
|
||||
((val[index] & 0xff) << LO_BYTE_SHIFT));
|
||||
@ -173,14 +204,27 @@ final class StringUTF16 {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return an encoded byte[] for the UTF16 characters in char[]}
|
||||
* No checking is done on the characters, some may or may not be latin1.
|
||||
* @param value a char array
|
||||
* @param off an offset
|
||||
* @param len a length
|
||||
* {@return a UTF-16 string byte array produced by encoding the characters
|
||||
* in the provided character array sub-range}
|
||||
*
|
||||
* @param value a character array to encode
|
||||
* @param off the index of the character to start encoding from
|
||||
* @param len the number of characters to encode
|
||||
*
|
||||
* @throws NegativeArraySizeException if {@code len < 0}
|
||||
* @throws NullPointerException if {@code value} is null
|
||||
* @throws OutOfMemoryError if {@code len > (Integer.MAX_VALUE / 2)}
|
||||
* @throws StringIndexOutOfBoundsException if the sub-range is out of bounds
|
||||
*/
|
||||
@IntrinsicCandidate
|
||||
static byte[] toBytes(char[] value, int off, int len) {
|
||||
checkBytesLength(len);
|
||||
String.checkBoundsOffCount(off, len, value.length); // Implicit null check on `value`
|
||||
return toBytes0(value, off, len);
|
||||
}
|
||||
|
||||
// vmIntrinsics::_toBytesStringU
|
||||
@IntrinsicCandidate
|
||||
private static byte[] toBytes0(char[] value, int off, int len) {
|
||||
byte[] val = newBytesFor(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
putChar(val, i, value[off]);
|
||||
@ -495,12 +539,28 @@ final class StringUTF16 {
|
||||
return result;
|
||||
}
|
||||
|
||||
@IntrinsicCandidate
|
||||
/**
|
||||
* Copies the specified sub-range of characters from a UTF-16 string byte
|
||||
* array to the specified character array sub-range.
|
||||
*
|
||||
* @param value the source UTF-16 string byte array to copy from
|
||||
* @param srcBegin the index (inclusive) of the first character in the source sub-range
|
||||
* @param srcEnd the index (exclusive) of the last character in the source sub-range
|
||||
* @param dst the target character array to copy to
|
||||
* @param dstBegin the index (inclusive) of the first character in the target sub-range
|
||||
*
|
||||
* @throws NullPointerException if {@code value} or {@code dst} is null
|
||||
* @throws StringIndexOutOfBoundsException if the sub-ranges are out of bounds
|
||||
*/
|
||||
static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
|
||||
// We need a range check here because 'getChar' has no checks
|
||||
if (srcBegin < srcEnd) {
|
||||
String.checkBoundsOffCount(srcBegin, srcEnd - srcBegin, length(value));
|
||||
}
|
||||
checkBoundsBeginEnd(srcBegin, srcEnd, value); // Implicit null check on `value` via `checkBoundsBeginEnd()`
|
||||
String.checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length); // Implicit null check on `dst`
|
||||
getChars0(value, srcBegin, srcEnd, dst, dstBegin);
|
||||
}
|
||||
|
||||
// vmIntrinsics::_getCharsStringU
|
||||
@IntrinsicCandidate
|
||||
private static void getChars0(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
|
||||
for (int i = srcBegin; i < srcEnd; i++) {
|
||||
dst[dstBegin++] = getChar(value, i);
|
||||
}
|
||||
@ -721,7 +781,7 @@ final class StringUTF16 {
|
||||
return -StringLatin1.compareToCI_UTF16(other, value);
|
||||
}
|
||||
|
||||
public static int compareToFC_Latin1(byte[] value, byte[] other) {
|
||||
static int compareToFC_Latin1(byte[] value, byte[] other) {
|
||||
return -StringLatin1.compareToFC_UTF16(other, value);
|
||||
}
|
||||
|
||||
@ -769,7 +829,7 @@ final class StringUTF16 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int compareToFC(byte[] value, byte[] other) {
|
||||
static int compareToFC(byte[] value, byte[] other) {
|
||||
int tlast = length(value);
|
||||
int olast = length(other);
|
||||
int lim = Math.min(tlast, olast);
|
||||
@ -1970,13 +2030,13 @@ final class StringUTF16 {
|
||||
}
|
||||
}
|
||||
|
||||
static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
|
||||
private static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
|
||||
|
||||
static void checkIndex(int off, byte[] val) {
|
||||
private static void checkIndex(int off, byte[] val) {
|
||||
String.checkIndex(off, length(val));
|
||||
}
|
||||
|
||||
static void checkOffset(int off, byte[] val) {
|
||||
private static void checkOffset(int off, byte[] val) {
|
||||
String.checkOffset(off, length(val));
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2026, 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
|
||||
@ -89,12 +89,16 @@ public class TestStringIntrinsicRangeChecks {
|
||||
for (int srcOff = 0; srcOff < SIZE; ++srcOff) {
|
||||
for (int dstOff = 0; dstOff < SIZE; ++dstOff) {
|
||||
for (int len = 0; len < SIZE; ++len) {
|
||||
int srcEnd = srcOff + len;
|
||||
int dstEnd = dstOff + len;
|
||||
// Check for potential overlows in source or destination array
|
||||
boolean srcOverflow = (srcOff + len) > SIZE;
|
||||
boolean srcOverflowB = (2*srcOff + 2*len) > SIZE;
|
||||
boolean dstOverflow = (dstOff + len) > SIZE;
|
||||
boolean dstOverflowB = (2*dstOff + 2*len) > SIZE;
|
||||
boolean getCharsOver = (srcOff < len) && ((2*(len-1) >= SIZE) || ((dstOff + len - srcOff) > SIZE));
|
||||
boolean getCharsOver = srcOff > srcEnd || (2*srcEnd) > SIZE || // src
|
||||
(2*len) > SIZE || // len
|
||||
dstOff > dstEnd || dstEnd > SIZE; // dst
|
||||
// Check if an exception is thrown and bail out if result is inconsistent with above
|
||||
// assumptions (for example, an exception was not thrown although an overflow happened).
|
||||
check(compressByte, srcOverflowB || dstOverflow, byteArray, srcOff, SIZE, dstOff, len);
|
||||
@ -102,7 +106,7 @@ public class TestStringIntrinsicRangeChecks {
|
||||
check(inflateByte, srcOverflow || dstOverflowB, byteArray, srcOff, SIZE, dstOff, len);
|
||||
check(inflateChar, srcOverflow || dstOverflow, byteArray, srcOff, SIZE, dstOff, len);
|
||||
check(toBytes, srcOverflow, charArray, srcOff, len);
|
||||
check(getChars, getCharsOver, byteArray, srcOff, len, SIZE, dstOff);
|
||||
check(getChars, getCharsOver, byteArray, srcOff, srcEnd, SIZE, dstOff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user