Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c5ed394
Rename `JavaLangAccess::*NoRepl` methods
vy Jun 20, 2025
b484510
Convert IAE-throwing methods into CCE-throwing ones
vy Jul 17, 2025
10cb72c
Improve docs of touched methods and add NPE checks
vy Jul 24, 2025
6e968a7
Merge remote-tracking branch 'upstream/master' into jlaNoRepl
vy Jul 24, 2025
5f555a6
Replace `requireNonNull` with implicit null checks
vy Jul 25, 2025
ac8e342
Merge remote-tracking branch 'upstream/master' into jlaNoRepl
vy Aug 7, 2025
1fb8582
Group `String` methods by `doReplace` argument
vy Aug 8, 2025
6fe1a9f
Avoid code duplication by sprinkling some generics magic
vy Aug 12, 2025
f536a34
Simplify added null checks
vy Aug 12, 2025
c5ddfe6
Remove redundant type parameters
vy Aug 12, 2025
f753389
Merge remote-tracking branch 'upstream/master' into jlaNoRepl
vy Aug 21, 2025
26cf5d6
Cosmetic improvements
vy Aug 21, 2025
7af0f35
Javadoc fix
vy Aug 21, 2025
1719676
Rename `NoReplTest` and fix its copyright year
vy Aug 22, 2025
01d4f87
Avoid using links in the Javadoc title line
vy Aug 22, 2025
28cbfd1
Document parametrization on the exception type
vy Aug 22, 2025
09b8f50
Improve exception parametrization
vy Aug 22, 2025
a73b446
Renamed to `malformedASCII`
vy Aug 22, 2025
788dffc
Improve comment style
vy Aug 25, 2025
584e6e2
Improve "sneaky throws"
vy Aug 25, 2025
fa46a81
Improve docs
vy Aug 28, 2025
978d981
Simplify `encodeWithEncoder` and trim long lines
vy Aug 28, 2025
14c0391
Rename `NoReplacement` suffix to `OrThrow`
vy Aug 28, 2025
b120d14
Rename `exceptionClass` to `exClass`
vy Aug 29, 2025
dae04e9
Improve Javadoc of touched `encode*()` methods
vy Aug 29, 2025
b5871ab
Merge remote-tracking branch 'upstream/master' into jlaNoRepl
vy Sep 1, 2025
a69f9fd
Fix `ClassCastException` spotted by `ReadWriteString`
vy Sep 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/java.base/share/classes/java/io/DataInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ public static final String readUTF(DataInput in) throws IOException {
if (ascii == utflen) {
String str;
if (trusted) {
str = JLA.uncheckedNewStringNoRepl(bytearr, StandardCharsets.ISO_8859_1);
str = JLA.uncheckedNewString(bytearr, StandardCharsets.ISO_8859_1);
} else {
str = new String(bytearr, 0, utflen, StandardCharsets.ISO_8859_1);
}
Expand Down
131 changes: 72 additions & 59 deletions src/java.base/share/classes/java/lang/String.java
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ private String(Charset charset, byte[] bytes, int offset, int length) {
if (length == 0) {
str = "";
} else if (charset == UTF_8.INSTANCE) {
str = utf8(bytes, offset, length);
str = utf8ThrowingIae(bytes, offset, length);
} else if (charset == ISO_8859_1.INSTANCE) {
str = iso88591(bytes, offset, length);
} else if (charset == US_ASCII.INSTANCE) {
Expand All @@ -568,7 +568,35 @@ private String(Charset charset, byte[] bytes, int offset, int length) {
this(str);
}

private static String utf8(byte[] bytes, int offset, int length) {
private static String utf8ThrowingIae(byte[] bytes, int offset, int length) {
try {
return utf8(bytes, offset, length);
} catch (CharacterCodingException cce) {
throw cce2iae(cce);
}
}

private static IllegalArgumentException cce2iae(CharacterCodingException cce) {
Throwable cause = cce.getCause();
// If the CCE is caused by an IAE, it implies that IAE is injected by
// us to provide more context into CCE. Try swapping them to obtain an
// IAE caused by a CCE.
if (cause instanceof IllegalArgumentException iae) {
if (cce instanceof MalformedInputException mie) {
return new IllegalArgumentException(
iae.getMessage(),
new MalformedInputException(mie.getInputLength()));
}
if (cce instanceof UnmappableCharacterException uce) {
return new IllegalArgumentException(
iae.getMessage(),
new UnmappableCharacterException(uce.getInputLength()));
}
}
return new IllegalArgumentException(cce);
}

private static String utf8(byte[] bytes, int offset, int length) throws CharacterCodingException {
if (COMPACT_STRINGS) {
int dp = StringCoding.countPositives(bytes, offset, length);
if (dp == length) {
Expand Down Expand Up @@ -696,7 +724,7 @@ private static String decode(Charset charset, byte[] bytes, int offset, int leng
* {@code false} if the byte array can be exclusively used to construct
* the string and is not modified or used for any other purpose.
*/
static String newStringUTF8NoRepl(byte[] bytes, int offset, int length, boolean noShare) {
static String newStringUTF8(byte[] bytes, int offset, int length, boolean noShare) throws CharacterCodingException {
checkBoundsOffCount(offset, length, bytes.length);
if (length == 0) {
return "";
Expand Down Expand Up @@ -759,26 +787,13 @@ static String newStringUTF8NoRepl(byte[] bytes, int offset, int length, boolean
return new String(dst, UTF16);
}

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) {
static String newString(byte[] src, Charset cs) throws CharacterCodingException {
int len = src.length;
if (len == 0) {
return "";
}
if (cs == UTF_8.INSTANCE) {
return newStringUTF8NoRepl(src, 0, src.length, false);
return newStringUTF8(src, 0, src.length, false);
}
if (cs == ISO_8859_1.INSTANCE) {
if (COMPACT_STRINGS)
Expand Down Expand Up @@ -806,13 +821,7 @@ private static String newStringNoRepl1(byte[] src, Charset cs) {
}
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);
Expand Down Expand Up @@ -847,7 +856,7 @@ private static Charset lookupCharset(String csn) throws UnsupportedEncodingExcep
}
}

private static byte[] encode(Charset cs, byte coder, byte[] val) {
private static byte[] encode(Charset cs, byte coder, byte[] val) throws CharacterCodingException {
if (cs == UTF_8.INSTANCE) {
return encodeUTF8(coder, val, true);
}
Expand All @@ -860,7 +869,7 @@ private static byte[] encode(Charset cs, byte coder, byte[] val) {
return encodeWithEncoder(cs, coder, val, true);
}

private static byte[] encodeWithEncoder(Charset cs, byte coder, byte[] val, boolean doReplace) {
private static byte[] encodeWithEncoder(Charset cs, byte coder, byte[] val, boolean doReplace) throws CharacterCodingException {
CharsetEncoder ce = cs.newEncoder();
int len = val.length >> coder; // assume LATIN1=0/UTF16=1;
int en = scale(len, ce.maxBytesPerChar());
Expand Down Expand Up @@ -905,7 +914,7 @@ private static byte[] encodeWithEncoder(Charset cs, byte coder, byte[] val, bool
cr.throwException();
} catch (CharacterCodingException x) {
if (!doReplace) {
throw new IllegalArgumentException(x);
throw x;
} else {
throw new Error(x);
}
Expand All @@ -916,7 +925,7 @@ private static byte[] encodeWithEncoder(Charset cs, byte coder, byte[] val, bool
/*
* Throws iae, instead of replacing, if unmappable.
*/
static byte[] getBytesUTF8NoRepl(String s) {
static byte[] getBytesUTF8(String s) throws CharacterCodingException {
return encodeUTF8(s.coder(), s.value(), false);
}

Expand All @@ -927,20 +936,7 @@ private static boolean isASCII(byte[] src) {
/*
* Throws CCE, instead of replacing, if unmappable.
*/
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) {
static byte[] getBytes(String s, Charset cs) throws CharacterCodingException {
byte[] val = s.value();
byte coder = s.coder();
if (cs == UTF_8.INSTANCE) {
Expand Down Expand Up @@ -1005,11 +1001,11 @@ private static void replaceNegatives(byte[] val, int fromIndex) {
}
}

private static byte[] encode8859_1(byte coder, byte[] val) {
private static byte[] encode8859_1(byte coder, byte[] val) throws UnmappableCharacterException {
return encode8859_1(coder, val, true);
}

private static byte[] encode8859_1(byte coder, byte[] val, boolean doReplace) {
private static byte[] encode8859_1(byte coder, byte[] val, boolean doReplace) throws UnmappableCharacterException {
if (coder == LATIN1) {
return val.clone();
}
Expand Down Expand Up @@ -1118,7 +1114,8 @@ private static int decode4(int b1, int b2, int b3, int b4) {
((byte) 0x80 << 0))));
}

private static int decodeUTF8_UTF16(byte[] src, int sp, int sl, byte[] dst, int dp, boolean doReplace) {
private static int decodeUTF8_UTF16(byte[] src, int sp, int sl, byte[] dst, int dp, boolean doReplace)
throws CharacterCodingException {
while (sp < sl) {
int b1 = src[sp++];
if (b1 >= 0) {
Expand Down Expand Up @@ -1259,27 +1256,31 @@ private static int malformed4(byte[] src, int sp) {
return 3;
}

private static void throwMalformed(int off, int nb) {
private static void throwMalformed(int off, int nb) throws MalformedInputException {
MalformedInputException mie = new MalformedInputException(nb);
String msg = "malformed input off : " + off + ", length : " + nb;
throw new IllegalArgumentException(msg, new MalformedInputException(nb));
mie.initCause(new IllegalArgumentException(msg));
throw mie;
}

private static void throwMalformed(byte[] val) {
private static void throwMalformed(byte[] val) throws MalformedInputException {
int dp = StringCoding.countPositives(val, 0, val.length);
throwMalformed(dp, 1);
}

private static void throwUnmappable(int off) {
private static void throwUnmappable(int off) throws UnmappableCharacterException {
UnmappableCharacterException uce = new UnmappableCharacterException(1);
String msg = "malformed input off : " + off + ", length : 1";
throw new IllegalArgumentException(msg, new UnmappableCharacterException(1));
uce.initCause(new IllegalArgumentException(msg, uce));
throw uce;
}

private static void throwUnmappable(byte[] val) {
private static void throwUnmappable(byte[] val) throws UnmappableCharacterException {
int dp = StringCoding.countPositives(val, 0, val.length);
throwUnmappable(dp);
}

private static byte[] encodeUTF8(byte coder, byte[] val, boolean doReplace) {
private static byte[] encodeUTF8(byte coder, byte[] val, boolean doReplace) throws UnmappableCharacterException {
if (coder == UTF16) {
return encodeUTF8_UTF16(val, doReplace);
}
Expand All @@ -1304,7 +1305,7 @@ private static byte[] encodeUTF8(byte coder, byte[] val, boolean doReplace) {
return Arrays.copyOf(dst, dp);
}

private static byte[] encodeUTF8_UTF16(byte[] val, boolean doReplace) {
private static byte[] encodeUTF8_UTF16(byte[] val, boolean doReplace) throws UnmappableCharacterException {
int dp = 0;
int sp = 0;
int sl = val.length >> 1;
Expand Down Expand Up @@ -1369,7 +1370,7 @@ private static byte[] encodeUTF8_UTF16(byte[] val, boolean doReplace) {
* @param val UTF16 encoded byte array
* @param doReplace true to replace unmappable characters
*/
private static long computeSizeUTF8_UTF16(byte[] val, boolean doReplace) {
private static long computeSizeUTF8_UTF16(byte[] val, boolean doReplace) throws UnmappableCharacterException {
long dp = 0L;
int sp = 0;
int sl = val.length >> 1;
Expand Down Expand Up @@ -1823,7 +1824,11 @@ public void getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin) {
*/
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
return encode(lookupCharset(charsetName), coder(), value);
try {
return encode(lookupCharset(charsetName), coder(), value);
} catch (CharacterCodingException uce) {
throw cce2iae(uce);
}
}

/**
Expand All @@ -1846,8 +1851,12 @@ public byte[] getBytes(String charsetName)
*/
public byte[] getBytes(Charset charset) {
if (charset == null) throw new NullPointerException();
return encode(charset, coder(), value);
}
try {
return encode(charset, coder(), value);
} catch (CharacterCodingException uce) {
throw cce2iae(uce);
}
}

/**
* Encodes this {@code String} into a sequence of bytes using the
Expand All @@ -1864,7 +1873,11 @@ public byte[] getBytes(Charset charset) {
* @since 1.1
*/
public byte[] getBytes() {
return encode(Charset.defaultCharset(), coder(), value);
try {
return encode(Charset.defaultCharset(), coder(), value);
} catch (CharacterCodingException uce) {
throw cce2iae(uce);
}
}

boolean bytesCompatible(Charset charset) {
Expand Down
22 changes: 13 additions & 9 deletions src/java.base/share/classes/java/lang/System.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Supplier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
Expand Down Expand Up @@ -2121,28 +2120,33 @@ public Stream<ModuleLayer> layers(ClassLoader loader) {
public int countPositives(byte[] bytes, int offset, int length) {
return StringCoding.countPositives(bytes, offset, length);
}

public int countNonZeroAscii(String s) {
return StringCoding.countNonZeroAscii(s);
}
public String uncheckedNewStringNoRepl(byte[] bytes, Charset cs) throws CharacterCodingException {
return String.newStringNoRepl(bytes, cs);

public String uncheckedNewString(byte[] bytes, Charset cs) throws CharacterCodingException {
return String.newString(bytes, cs);
}

public char uncheckedGetUTF16Char(byte[] bytes, int index) {
return StringUTF16.getChar(bytes, index);
}

public void uncheckedPutCharUTF16(byte[] bytes, int index, int ch) {
StringUTF16.putChar(bytes, index, ch);
}
public byte[] uncheckedGetBytesNoRepl(String s, Charset cs) throws CharacterCodingException {
return String.getBytesNoRepl(s, cs);

public byte[] uncheckedGetBytes(String s, Charset cs) throws CharacterCodingException {
return String.getBytes(s, cs);
}

public String newStringUTF8NoRepl(byte[] bytes, int off, int len) {
return String.newStringUTF8NoRepl(bytes, off, len, true);
public String newStringUTF8(byte[] bytes, int off, int len) throws CharacterCodingException {
return String.newStringUTF8(bytes, off, len, true);
}

public byte[] getBytesUTF8NoRepl(String s) {
return String.getBytesUTF8NoRepl(s);
public byte[] getBytesUTF8(String s) throws CharacterCodingException {
return String.getBytesUTF8(s);
}

public void inflateBytesToChars(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
Expand Down
2 changes: 1 addition & 1 deletion src/java.base/share/classes/java/math/BigDecimal.java
Original file line number Diff line number Diff line change
Expand Up @@ -4145,7 +4145,7 @@ private String layoutChars(boolean sci) {
buf[highIntSize] = '.';
DecimalDigits.uncheckedPutPairLatin1(buf, highIntSize + 1, lowInt);
try {
return JLA.uncheckedNewStringNoRepl(buf, StandardCharsets.ISO_8859_1);
return JLA.uncheckedNewString(buf, StandardCharsets.ISO_8859_1);
} catch (CharacterCodingException cce) {
throw new AssertionError(cce);
}
Expand Down
4 changes: 2 additions & 2 deletions src/java.base/share/classes/java/nio/file/Files.java
Original file line number Diff line number Diff line change
Expand Up @@ -3043,7 +3043,7 @@ public static String readString(Path path, Charset cs) throws IOException {
byte[] ba = readAllBytes(path);
if (path.getClass().getModule() != Object.class.getModule())
ba = ba.clone();
return JLA.uncheckedNewStringNoRepl(ba, cs);
return JLA.uncheckedNewString(ba, cs);
}

/**
Expand Down Expand Up @@ -3362,7 +3362,7 @@ public static Path writeString(Path path, CharSequence csq, Charset cs, OpenOpti
Objects.requireNonNull(csq);
Objects.requireNonNull(cs);

byte[] bytes = JLA.uncheckedGetBytesNoRepl(String.valueOf(csq), cs);
byte[] bytes = JLA.uncheckedGetBytes(String.valueOf(csq), cs);
if (path.getClass().getModule() != Object.class.getModule())
bytes = bytes.clone();
write(path, bytes, options);
Expand Down
Loading