diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 15b8e98369e..24ead22e283 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -604,14 +604,14 @@ public final class String } byte[] utf16 = StringUTF16.newBytesFor(length); StringLatin1.inflate(latin1, 0, utf16, 0, dp); - dp = decodeUTF8_UTF16(latin1, sp, length, utf16, dp, true); + dp = decodeUTF8_UTF16(latin1, sp, length, utf16, dp); if (dp != length) { utf16 = Arrays.copyOf(utf16, dp << 1); } return new String(utf16, UTF16); } else { // !COMPACT_STRINGS byte[] dst = StringUTF16.newBytesFor(length); - int dp = decodeUTF8_UTF16(bytes, offset, offset + length, dst, 0, true); + int dp = decodeUTF8_UTF16(bytes, offset, offset + length, dst, 0); if (dp != length) { dst = Arrays.copyOf(dst, dp << 1); } @@ -688,13 +688,25 @@ public final class String } } - /* - * Throws iae, instead of replacing, if malformed or unmappable. - * The byte array can be exclusively used to construct - * the string and is not modified or used for any other purpose. + /** + * {@return a new string by decoding from the given UTF-8 bytes array} + *
+ * WARNING: The caller of this method is assumed to have relinquished
+ * and transferred the ownership of the byte array. It can thus be
+ * exclusively used to construct the {@code String}.
+ *
+ * @param bytes byte array containing UTF-8 encoded characters
+ * @param offset the index of the first byte to decode
+ * @param length the number of bytes to decode
+ * @throws NullPointerException If {@code bytes} is null
+ * @throws StringIndexOutOfBoundsException If {@code offset} is negative,
+ * {@code length} is negative, or {@code offset} is greater than
+ * {@code bytes.length - length}
+ * @throws CharacterCodingException for malformed input or unmappable characters
*/
- private static String newStringUTF8NoRepl(byte[] bytes, int offset, int length) {
- checkBoundsOffCount(offset, length, bytes.length);
+ private static String newStringUTF8OrThrow(byte[] bytes, int offset, int length)
+ throws CharacterCodingException {
+ checkBoundsOffCount(offset, length, bytes.length); // Implicit null check on `bytes`
if (length == 0) {
return "";
}
@@ -745,10 +757,10 @@ public final class String
StringLatin1.inflate(dst, 0, buf, 0, dp);
dst = buf;
}
- dp = decodeUTF8_UTF16(bytes, offset, sl, dst, dp, false);
+ dp = decodeUTF8_UTF16OrThrow(bytes, offset, sl, dst, dp);
} else { // !COMPACT_STRINGS
dst = StringUTF16.newBytesFor(length);
- dp = decodeUTF8_UTF16(bytes, offset, offset + length, dst, 0, false);
+ dp = decodeUTF8_UTF16OrThrow(bytes, offset, offset + length, dst, 0);
}
if (dp != length) {
dst = Arrays.copyOf(dst, dp << 1);
@@ -783,27 +795,16 @@ public final class String
* @param cs charset the byte array encoded in
*
* @throws CharacterCodingException for malformed input or unmappable characters
+ * @throws NullPointerException If {@code src} or {@code cs} is null
*/
- static String newStringNoRepl(byte[] src, Charset cs) throws CharacterCodingException {
- try {
- return newStringNoRepl1(src, cs);
- } catch (IllegalArgumentException e) {
- //newStringNoRepl1 throws IAE with MalformedInputException or CCE as the cause
- Throwable cause = e.getCause();
- if (cause instanceof MalformedInputException mie) {
- throw mie;
- }
- throw (CharacterCodingException)cause;
- }
- }
-
- private static String newStringNoRepl1(byte[] src, Charset cs) {
- int len = src.length;
+ static String newStringOrThrow(byte[] src, Charset cs) throws CharacterCodingException {
+ Objects.requireNonNull(cs);
+ int len = src.length; // Implicit null check on `src`
if (len == 0) {
return "";
}
if (cs == UTF_8.INSTANCE) {
- return newStringUTF8NoRepl(src, 0, src.length);
+ return newStringUTF8OrThrow(src, 0, src.length);
}
if (cs == ISO_8859_1.INSTANCE) {
if (COMPACT_STRINGS)
@@ -816,7 +817,7 @@ public final class String
return new String(src, LATIN1);
return new String(StringLatin1.inflate(src, 0, src.length), UTF16);
} else {
- throwMalformed(src);
+ throw malformedASCII(src);
}
}
@@ -831,13 +832,7 @@ public final class String
}
int en = scale(len, cd.maxCharsPerByte());
char[] ca = new char[en];
- int caLen;
- try {
- caLen = decodeWithDecoder(cd, ca, src, 0, src.length);
- } catch (CharacterCodingException x) {
- // throw via IAE
- throw new IllegalArgumentException(x);
- }
+ int caLen = decodeWithDecoder(cd, ca, src, 0, src.length);
if (COMPACT_STRINGS) {
byte[] val = StringUTF16.compress(ca, 0, caLen);
byte coder = StringUTF16.coderFromArrayLen(val, caLen);
@@ -874,7 +869,7 @@ public final class String
private static byte[] encode(Charset cs, byte coder, byte[] val) {
if (cs == UTF_8.INSTANCE) {
- return encodeUTF8(coder, val, true);
+ return encodeUTF8(coder, val);
}
if (cs == ISO_8859_1.INSTANCE) {
return encode8859_1(coder, val);
@@ -882,13 +877,30 @@ public final class String
if (cs == US_ASCII.INSTANCE) {
return encodeASCII(coder, val);
}
- return encodeWithEncoder(cs, coder, val, true);
+ return encodeWithEncoder(cs, coder, val, null);
}
- private static byte[] encodeWithEncoder(Charset cs, byte coder, byte[] val, boolean doReplace) {
+ /**
+ * {@return the byte array obtained by first decoding {@code val} with
+ * {@code coder}, and then encoding the result with the encoder of {@code
+ * cs}}
+ *
+ * @param cs a charset to obtain the encoder from
+ * @param coder a coder to decode {@code val} with
+ * @param val a string byte array encoded with {@code coder}
+ * @param exClass The exception class where any non-null value indicates
+ * malformed or unmappable bytes will result in an exception
+ * to be thrown instead of getting replaced.
+ * @param
+ * WARNING: This method returns the {@code byte[]} backing the provided
+ * {@code String}, if the input is ASCII. Hence, the returned byte array
+ * must not be modified.
+ *
+ * @param s the string to encode
+ * @param cs the charset
+ * @throws NullPointerException If {@code s} or {@code cs} is null
+ * @throws CharacterCodingException For malformed input or unmappable characters
*/
- static byte[] getBytesNoRepl(String s, Charset cs) throws CharacterCodingException {
- try {
- return getBytesNoRepl1(s, cs);
- } catch (IllegalArgumentException e) {
- //getBytesNoRepl1 throws IAE with UnmappableCharacterException or CCE as the cause
- Throwable cause = e.getCause();
- if (cause instanceof UnmappableCharacterException) {
- throw (UnmappableCharacterException)cause;
- }
- throw (CharacterCodingException)cause;
- }
- }
-
- private static byte[] getBytesNoRepl1(String s, Charset cs) {
- byte[] val = s.value();
+ static byte[] getBytesOrThrow(String s, Charset cs) throws CharacterCodingException {
+ Objects.requireNonNull(cs);
+ byte[] val = s.value(); // Implicit null check on `s`
byte coder = s.coder();
if (cs == UTF_8.INSTANCE) {
if (coder == LATIN1 && isASCII(val)) {
return val;
}
- return encodeUTF8(coder, val, false);
+ return encodeUTF8OrThrow(coder, val);
}
if (cs == ISO_8859_1.INSTANCE) {
if (coder == LATIN1) {
return val;
}
- return encode8859_1(coder, val, false);
+ return encode8859_1OrThrow(coder, val);
}
if (cs == US_ASCII.INSTANCE) {
if (coder == LATIN1) {
if (isASCII(val)) {
return val;
} else {
- throwUnmappable(val);
+ throw unmappableASCII(val);
}
}
}
- return encodeWithEncoder(cs, coder, val, false);
+ return encodeWithEncoder(cs, coder, val, CharacterCodingException.class);
}
+ /**
+ * {@return the byte array obtained by first decoding {@code val} with
+ * {@code coder}, and then encoding the result with US-ASCII}
+ *
+ * @param coder a coder to decode {@code val} with
+ * @param val a string byte array encoded with {@code coder}
+ */
private static byte[] encodeASCII(byte coder, byte[] val) {
if (coder == LATIN1) {
int positives = StringCoding.countPositives(val, 0, val.length);
@@ -1031,10 +1054,26 @@ public final class String
}
private static byte[] encode8859_1(byte coder, byte[] val) {
- return encode8859_1(coder, val, true);
+ return encode8859_1(coder, val, null);
}
- private static byte[] encode8859_1(byte coder, byte[] val, boolean doReplace) {
+ private static byte[] encode8859_1OrThrow(byte coder, byte[] val) throws UnmappableCharacterException {
+ return encode8859_1(coder, val, UnmappableCharacterException.class);
+ }
+
+ /**
+ * {@return the byte array obtained by first decoding {@code val} with
+ * {@code coder}, and then encoding the result with ISO-8859-1}
+ *
+ * @param coder a coder to decode {@code val} with
+ * @param val a string byte array encoded with {@code coder}
+ * @param exClass The exception class where any non-null value indicates
+ * malformed or unmappable bytes will result in an exception
+ * to be thrown instead of getting replaced.
+ * @param
* WARNING: The caller of this method shall relinquish and transfer the
* ownership of the byte array to the callee, since the latter will not
@@ -342,26 +341,24 @@ public interface JavaLangAccess {
* @param cs the Charset
* @return the newly created string
* @throws CharacterCodingException for malformed or unmappable bytes
+ * @throws NullPointerException If {@code bytes} or {@code cs} is null
*/
- String uncheckedNewStringNoRepl(byte[] bytes, Charset cs) throws CharacterCodingException;
+ String uncheckedNewStringOrThrow(byte[] bytes, Charset cs) throws CharacterCodingException;
/**
- * Encode the given string into a sequence of bytes using the specified
- * {@linkplain java.nio.charset.Charset charset}.
+ * {@return the sequence of bytes obtained by encoding the given string in
+ * the specified {@code Charset}}
*
* WARNING: This method returns the {@code byte[]} backing the provided
* {@code String}, if the input is ASCII. Hence, the returned byte array
* must not be modified.
- *
- * This method throws {@code CharacterCodingException} instead of replacing
- * when malformed input or unmappable characters are encountered.
*
* @param s the string to encode
* @param cs the charset
- * @return the encoded bytes
+ * @throws NullPointerException If {@code s} or {@code cs} is null
* @throws CharacterCodingException for malformed input or unmappable characters
*/
- byte[] uncheckedGetBytesNoRepl(String s, Charset cs) throws CharacterCodingException;
+ byte[] uncheckedGetBytesOrThrow(String s, Charset cs) throws CharacterCodingException;
/**
* Get the {@code char} at {@code index} in a {@code byte[]} in internal
@@ -387,13 +384,13 @@ public interface JavaLangAccess {
void uncheckedPutCharUTF16(byte[] bytes, int index, int ch);
/**
- * Encode the given string into a sequence of bytes using utf8.
+ * {@return the sequence of bytes obtained by encoding the given string in UTF-8}
*
* @param s the string to encode
- * @return the encoded bytes in utf8
- * @throws IllegalArgumentException for malformed surrogates
+ * @throws NullPointerException If {@code s} is null
+ * @throws CharacterCodingException For malformed input or unmappable characters
*/
- byte[] getBytesUTF8NoRepl(String s);
+ byte[] getBytesUTF8OrThrow(String s) throws CharacterCodingException;
/**
* Inflated copy from {@code byte[]} to {@code char[]}, as defined by
diff --git a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java
index 2f842810aa7..bb6cb2d3915 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java
@@ -73,7 +73,7 @@ public final class StringSupport {
final byte[] bytes = new byte[len];
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
try {
- return JAVA_LANG_ACCESS.uncheckedNewStringNoRepl(bytes, charset);
+ return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
} catch (CharacterCodingException _) {
// use replacement characters for malformed input
return new String(bytes, charset);
@@ -92,7 +92,7 @@ public final class StringSupport {
byte[] bytes = new byte[len];
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
try {
- return JAVA_LANG_ACCESS.uncheckedNewStringNoRepl(bytes, charset);
+ return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
} catch (CharacterCodingException _) {
// use replacement characters for malformed input
return new String(bytes, charset);
@@ -111,7 +111,7 @@ public final class StringSupport {
byte[] bytes = new byte[len];
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
try {
- return JAVA_LANG_ACCESS.uncheckedNewStringNoRepl(bytes, charset);
+ return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
} catch (CharacterCodingException _) {
// use replacement characters for malformed input
return new String(bytes, charset);
diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixPath.java b/src/java.base/unix/classes/sun/nio/fs/UnixPath.java
index 5dfc73f57aa..5a77bb0b935 100644
--- a/src/java.base/unix/classes/sun/nio/fs/UnixPath.java
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixPath.java
@@ -126,7 +126,7 @@ class UnixPath implements Path {
private static byte[] encode(UnixFileSystem fs, String input) {
input = fs.normalizeNativePath(input);
try {
- return JLA.uncheckedGetBytesNoRepl(input, Util.jnuEncoding());
+ return JLA.uncheckedGetBytesOrThrow(input, Util.jnuEncoding());
} catch (CharacterCodingException cce) {
throw new InvalidPathException(input,
"Malformed input or input contains unmappable characters");
diff --git a/test/jdk/java/lang/String/NoReplTest.java b/test/jdk/java/lang/String/OrThrowTest.java
similarity index 79%
rename from test/jdk/java/lang/String/NoReplTest.java
rename to test/jdk/java/lang/String/OrThrowTest.java
index 1817a1ffe73..340a190b4eb 100644
--- a/test/jdk/java/lang/String/NoReplTest.java
+++ b/test/jdk/java/lang/String/OrThrowTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025, 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
@@ -24,8 +24,8 @@
/*
* @test
* @bug 8286287 8288589
- * @summary Tests for *NoRepl() shared secret methods.
- * @run testng NoReplTest
+ * @summary Tests for *OrThrow() shared secret methods.
+ * @run testng OrThrowTest
* @modules jdk.charsets
*/
@@ -39,17 +39,17 @@ import static java.nio.charset.StandardCharsets.UTF_16;
import org.testng.annotations.Test;
@Test
-public class NoReplTest {
+public class OrThrowTest {
private final static byte[] MALFORMED_UTF16 = {(byte)0x00, (byte)0x20, (byte)0x00};
private final static String MALFORMED_WINDOWS_1252 = "\u0080\u041e";
private final static Charset WINDOWS_1252 = Charset.forName("windows-1252");
/**
- * Verifies newStringNoRepl() throws a CharacterCodingException.
- * The method is invoked by `Files.readString()` method.
+ * Verifies {@code uncheckedNewStringOrThrow()} throws a {@link CharacterCodingException}.
+ * The method is invoked by {@code Files.readString()} method.
*/
@Test
- public void newStringNoReplTest() throws IOException {
+ public void uncheckedNewStringOrThrowTest() throws IOException {
var f = Files.createTempFile(null, null);
try (var fos = Files.newOutputStream(f)) {
fos.write(MALFORMED_UTF16);
@@ -67,11 +67,11 @@ public class NoReplTest {
}
/**
- * Verifies getBytesNoRepl() throws a CharacterCodingException.
- * The method is invoked by `Files.writeString()` method.
+ * Verifies {@code uncheckedGetBytesOrThrow()} throws a {@link CharacterCodingException}.
+ * The method is invoked by {@code Files.writeString()} method.
*/
@Test
- public void getBytesNoReplTest() throws IOException {
+ public void uncheckedGetBytesOrThrowTest() throws IOException {
var f = Files.createTempFile(null, null);
try {
Files.writeString(f, MALFORMED_WINDOWS_1252, WINDOWS_1252);