mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8288589: Files.readString ignores encoding errors for UTF-16
Backport-of: 2728770e3d
This commit is contained in:
parent
4c9ea7e66a
commit
a716f7934b
4 changed files with 163 additions and 88 deletions
|
@ -658,6 +658,8 @@ public final class String
|
||||||
|
|
||||||
// decode using CharsetDecoder
|
// decode using CharsetDecoder
|
||||||
int en = scale(length, cd.maxCharsPerByte());
|
int en = scale(length, cd.maxCharsPerByte());
|
||||||
|
cd.onMalformedInput(CodingErrorAction.REPLACE)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||||
char[] ca = new char[en];
|
char[] ca = new char[en];
|
||||||
if (charset.getClass().getClassLoader0() != null &&
|
if (charset.getClass().getClassLoader0() != null &&
|
||||||
System.getSecurityManager() != null) {
|
System.getSecurityManager() != null) {
|
||||||
|
@ -665,7 +667,13 @@ public final class String
|
||||||
offset = 0;
|
offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int caLen = decodeWithDecoder(cd, ca, bytes, offset, length);
|
int caLen;
|
||||||
|
try {
|
||||||
|
caLen = decodeWithDecoder(cd, ca, bytes, offset, length);
|
||||||
|
} catch (CharacterCodingException x) {
|
||||||
|
// Substitution is enabled, so this shouldn't happen
|
||||||
|
throw new Error(x);
|
||||||
|
}
|
||||||
if (COMPACT_STRINGS) {
|
if (COMPACT_STRINGS) {
|
||||||
byte[] bs = StringUTF16.compress(ca, 0, caLen);
|
byte[] bs = StringUTF16.compress(ca, 0, caLen);
|
||||||
if (bs != null) {
|
if (bs != null) {
|
||||||
|
@ -791,7 +799,13 @@ public final class String
|
||||||
System.getSecurityManager() != null) {
|
System.getSecurityManager() != null) {
|
||||||
src = Arrays.copyOf(src, len);
|
src = Arrays.copyOf(src, len);
|
||||||
}
|
}
|
||||||
int caLen = decodeWithDecoder(cd, ca, src, 0, src.length);
|
int caLen;
|
||||||
|
try {
|
||||||
|
caLen = decodeWithDecoder(cd, ca, src, 0, src.length);
|
||||||
|
} catch (CharacterCodingException x) {
|
||||||
|
// throw via IAE
|
||||||
|
throw new IllegalArgumentException(x);
|
||||||
|
}
|
||||||
if (COMPACT_STRINGS) {
|
if (COMPACT_STRINGS) {
|
||||||
byte[] bs = StringUTF16.compress(ca, 0, caLen);
|
byte[] bs = StringUTF16.compress(ca, 0, caLen);
|
||||||
if (bs != null) {
|
if (bs != null) {
|
||||||
|
@ -1199,23 +1213,16 @@ public final class String
|
||||||
return dp;
|
return dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int decodeWithDecoder(CharsetDecoder cd, char[] dst, byte[] src, int offset, int length) {
|
private static int decodeWithDecoder(CharsetDecoder cd, char[] dst, byte[] src, int offset, int length)
|
||||||
|
throws CharacterCodingException {
|
||||||
ByteBuffer bb = ByteBuffer.wrap(src, offset, length);
|
ByteBuffer bb = ByteBuffer.wrap(src, offset, length);
|
||||||
CharBuffer cb = CharBuffer.wrap(dst, 0, dst.length);
|
CharBuffer cb = CharBuffer.wrap(dst, 0, dst.length);
|
||||||
cd.onMalformedInput(CodingErrorAction.REPLACE)
|
|
||||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
|
||||||
try {
|
|
||||||
CoderResult cr = cd.decode(bb, cb, true);
|
CoderResult cr = cd.decode(bb, cb, true);
|
||||||
if (!cr.isUnderflow())
|
if (!cr.isUnderflow())
|
||||||
cr.throwException();
|
cr.throwException();
|
||||||
cr = cd.flush(cb);
|
cr = cd.flush(cb);
|
||||||
if (!cr.isUnderflow())
|
if (!cr.isUnderflow())
|
||||||
cr.throwException();
|
cr.throwException();
|
||||||
} catch (CharacterCodingException x) {
|
|
||||||
// Substitution is always enabled,
|
|
||||||
// so this shouldn't happen
|
|
||||||
throw new Error(x);
|
|
||||||
}
|
|
||||||
return cb.position();
|
return cb.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022, 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
|
|
||||||
* @bug 8286287
|
|
||||||
* @summary Verifies newStringNoRepl() does not throw an Error.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.HexFormat;
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_16;
|
|
||||||
|
|
||||||
public class NewStringNoRepl {
|
|
||||||
private final static byte[] MALFORMED_UTF16 = {(byte)0x00, (byte)0x20, (byte)0x00};
|
|
||||||
|
|
||||||
public static void main(String... args) throws IOException {
|
|
||||||
var f = Files.createTempFile(null, null);
|
|
||||||
try (var fos = Files.newOutputStream(f)) {
|
|
||||||
fos.write(MALFORMED_UTF16);
|
|
||||||
}
|
|
||||||
System.out.println("Returned bytes: " +
|
|
||||||
HexFormat.of()
|
|
||||||
.withPrefix("x")
|
|
||||||
.withUpperCase()
|
|
||||||
.formatHex(Files.readString(f, UTF_16).getBytes(UTF_16)));
|
|
||||||
Files.delete(f);
|
|
||||||
}
|
|
||||||
}
|
|
85
test/jdk/java/lang/String/NoReplTest.java
Normal file
85
test/jdk/java/lang/String/NoReplTest.java
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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
|
||||||
|
* @bug 8286287 8288589
|
||||||
|
* @summary Tests for *NoRepl() shared secret methods.
|
||||||
|
* @run testng NoReplTest
|
||||||
|
* @modules jdk.charsets
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.CharacterCodingException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.HexFormat;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_16;
|
||||||
|
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public class NoReplTest {
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void newStringNoReplTest() throws IOException {
|
||||||
|
var f = Files.createTempFile(null, null);
|
||||||
|
try (var fos = Files.newOutputStream(f)) {
|
||||||
|
fos.write(MALFORMED_UTF16);
|
||||||
|
var read = Files.readString(f, UTF_16);
|
||||||
|
throw new RuntimeException("Exception should be thrown for a malformed input. Bytes read: " +
|
||||||
|
HexFormat.of()
|
||||||
|
.withPrefix("x")
|
||||||
|
.withUpperCase()
|
||||||
|
.formatHex(read.getBytes(UTF_16)));
|
||||||
|
} catch (CharacterCodingException cce) {
|
||||||
|
// success
|
||||||
|
} finally {
|
||||||
|
Files.delete(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies getBytesNoRepl() throws a CharacterCodingException.
|
||||||
|
* The method is invoked by `Files.writeString()` method.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void getBytesNoReplTest() throws IOException {
|
||||||
|
var f = Files.createTempFile(null, null);
|
||||||
|
try {
|
||||||
|
Files.writeString(f, MALFORMED_WINDOWS_1252, WINDOWS_1252);
|
||||||
|
throw new RuntimeException("Exception should be thrown");
|
||||||
|
} catch (CharacterCodingException cce) {
|
||||||
|
// success
|
||||||
|
} finally {
|
||||||
|
Files.delete(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,10 +24,12 @@
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.CharacterCodingException;
|
||||||
import java.nio.charset.MalformedInputException;
|
import java.nio.charset.MalformedInputException;
|
||||||
import java.nio.charset.UnmappableCharacterException;
|
import java.nio.charset.UnmappableCharacterException;
|
||||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
|
||||||
import static java.nio.charset.StandardCharsets.ISO_8859_1;
|
import static java.nio.charset.StandardCharsets.ISO_8859_1;
|
||||||
|
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_16;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.OpenOption;
|
import java.nio.file.OpenOption;
|
||||||
|
@ -46,7 +48,7 @@ import org.testng.annotations.DataProvider;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
/* @test
|
/* @test
|
||||||
* @bug 8201276 8205058 8209576 8287541
|
* @bug 8201276 8205058 8209576 8287541 8288589
|
||||||
* @build ReadWriteString PassThroughFileSystem
|
* @build ReadWriteString PassThroughFileSystem
|
||||||
* @run testng ReadWriteString
|
* @run testng ReadWriteString
|
||||||
* @summary Unit test for methods for Files readString and write methods.
|
* @summary Unit test for methods for Files readString and write methods.
|
||||||
|
@ -60,6 +62,8 @@ public class ReadWriteString {
|
||||||
final String TEXT_UNICODE = "\u201CHello\u201D";
|
final String TEXT_UNICODE = "\u201CHello\u201D";
|
||||||
final String TEXT_ASCII = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n abcdefghijklmnopqrstuvwxyz\n 1234567890\n";
|
final String TEXT_ASCII = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n abcdefghijklmnopqrstuvwxyz\n 1234567890\n";
|
||||||
private static final String JA_STRING = "\u65e5\u672c\u8a9e\u6587\u5b57\u5217";
|
private static final String JA_STRING = "\u65e5\u672c\u8a9e\u6587\u5b57\u5217";
|
||||||
|
private static final Charset WINDOWS_1252 = Charset.forName("windows-1252");
|
||||||
|
private static final Charset WINDOWS_31J = Charset.forName("windows-31j");
|
||||||
|
|
||||||
static byte[] data = getData();
|
static byte[] data = getData();
|
||||||
|
|
||||||
|
@ -88,14 +92,14 @@ public class ReadWriteString {
|
||||||
*/
|
*/
|
||||||
@DataProvider(name = "malformedWrite")
|
@DataProvider(name = "malformedWrite")
|
||||||
public Object[][] getMalformedWrite() throws IOException {
|
public Object[][] getMalformedWrite() throws IOException {
|
||||||
Path path = Files.createTempFile("malformedWrite", null);
|
Path path = Files.createFile(Path.of("malformedWrite"));
|
||||||
return new Object[][]{
|
return new Object[][]{
|
||||||
{path, "\ud800", null}, //the default Charset is UTF_8
|
{path, "\ud800", null}, //the default Charset is UTF_8
|
||||||
{path, "\u00A0\u00A1", US_ASCII},
|
{path, "\u00A0\u00A1", US_ASCII},
|
||||||
{path, "\ud800", UTF_8},
|
{path, "\ud800", UTF_8},
|
||||||
{path, JA_STRING, ISO_8859_1},
|
{path, JA_STRING, ISO_8859_1},
|
||||||
{path, "\u041e", Charset.forName("windows-1252")}, // cyrillic capital letter O
|
{path, "\u041e", WINDOWS_1252}, // cyrillic capital letter O
|
||||||
{path, "\u091c", Charset.forName("windows-31j")}, // devanagari letter ja
|
{path, "\u091c", WINDOWS_31J}, // devanagari letter ja
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,13 +109,26 @@ public class ReadWriteString {
|
||||||
*/
|
*/
|
||||||
@DataProvider(name = "illegalInput")
|
@DataProvider(name = "illegalInput")
|
||||||
public Object[][] getIllegalInput() throws IOException {
|
public Object[][] getIllegalInput() throws IOException {
|
||||||
Path path = Files.createTempFile("illegalInput", null);
|
Path path = Files.createFile(Path.of("illegalInput"));
|
||||||
return new Object[][]{
|
return new Object[][]{
|
||||||
{path, data, ISO_8859_1, null},
|
{path, data, ISO_8859_1, null},
|
||||||
{path, data, ISO_8859_1, UTF_8}
|
{path, data, ISO_8859_1, UTF_8}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DataProvider for illegal input bytes test
|
||||||
|
*/
|
||||||
|
@DataProvider(name = "illegalInputBytes")
|
||||||
|
public Object[][] getIllegalInputBytes() throws IOException {
|
||||||
|
return new Object[][]{
|
||||||
|
{new byte[] {(byte)0x00, (byte)0x20, (byte)0x00}, UTF_16, MalformedInputException.class},
|
||||||
|
{new byte[] {-50}, UTF_16, MalformedInputException.class},
|
||||||
|
{new byte[] {(byte)0x81}, WINDOWS_1252, UnmappableCharacterException.class}, // unused in Cp1252
|
||||||
|
{new byte[] {(byte)0x81, (byte)0xff}, WINDOWS_31J, UnmappableCharacterException.class}, // invalid trailing byte
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DataProvider for writeString test
|
* DataProvider for writeString test
|
||||||
* Writes the data using both the existing and new method and compares the results.
|
* Writes the data using both the existing and new method and compares the results.
|
||||||
|
@ -143,16 +160,9 @@ public class ReadWriteString {
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
void setup() throws IOException {
|
void setup() throws IOException {
|
||||||
testFiles[0] = Files.createTempFile("readWriteString", null);
|
testFiles[0] = Files.createFile(Path.of("readWriteString"));
|
||||||
testFiles[1] = Files.createTempFile("writeString_file1", null);
|
testFiles[1] = Files.createFile(Path.of("writeString_file1"));
|
||||||
testFiles[2] = Files.createTempFile("writeString_file2", null);
|
testFiles[2] = Files.createFile(Path.of("writeString_file2"));
|
||||||
}
|
|
||||||
|
|
||||||
@AfterClass
|
|
||||||
void cleanup() throws IOException {
|
|
||||||
for (Path path : testFiles) {
|
|
||||||
Files.deleteIfExists(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -241,11 +251,10 @@ public class ReadWriteString {
|
||||||
*/
|
*/
|
||||||
@Test(dataProvider = "malformedWrite", expectedExceptions = UnmappableCharacterException.class)
|
@Test(dataProvider = "malformedWrite", expectedExceptions = UnmappableCharacterException.class)
|
||||||
public void testMalformedWrite(Path path, String s, Charset cs) throws IOException {
|
public void testMalformedWrite(Path path, String s, Charset cs) throws IOException {
|
||||||
path.toFile().deleteOnExit();
|
|
||||||
if (cs == null) {
|
if (cs == null) {
|
||||||
Files.writeString(path, s, CREATE);
|
Files.writeString(path, s);
|
||||||
} else {
|
} else {
|
||||||
Files.writeString(path, s, cs, CREATE);
|
Files.writeString(path, s, cs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,9 +270,8 @@ public class ReadWriteString {
|
||||||
*/
|
*/
|
||||||
@Test(dataProvider = "illegalInput", expectedExceptions = MalformedInputException.class)
|
@Test(dataProvider = "illegalInput", expectedExceptions = MalformedInputException.class)
|
||||||
public void testMalformedRead(Path path, byte[] data, Charset csWrite, Charset csRead) throws IOException {
|
public void testMalformedRead(Path path, byte[] data, Charset csWrite, Charset csRead) throws IOException {
|
||||||
path.toFile().deleteOnExit();
|
|
||||||
String temp = new String(data, csWrite);
|
String temp = new String(data, csWrite);
|
||||||
Files.writeString(path, temp, csWrite, CREATE);
|
Files.writeString(path, temp, csWrite);
|
||||||
if (csRead == null) {
|
if (csRead == null) {
|
||||||
Files.readString(path);
|
Files.readString(path);
|
||||||
} else {
|
} else {
|
||||||
|
@ -271,6 +279,31 @@ public class ReadWriteString {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that IOException is thrown when reading a file containing
|
||||||
|
* illegal bytes
|
||||||
|
*
|
||||||
|
* @param data the data used for the test
|
||||||
|
* @param csRead the Charset to use for reading the file
|
||||||
|
* @param expected exception class
|
||||||
|
* @throws IOException when the Charset used for reading the file is incorrect
|
||||||
|
*/
|
||||||
|
@Test(dataProvider = "illegalInputBytes")
|
||||||
|
public void testMalformedReadBytes(byte[] data, Charset csRead, Class<CharacterCodingException> expected)
|
||||||
|
throws IOException {
|
||||||
|
Path path = Path.of("illegalInputBytes");
|
||||||
|
Files.write(path, data);
|
||||||
|
try {
|
||||||
|
Files.readString(path, csRead);
|
||||||
|
} catch (MalformedInputException | UnmappableCharacterException e) {
|
||||||
|
if (expected.isInstance(e)) {
|
||||||
|
// success
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException("An instance of " + expected + " should be thrown");
|
||||||
|
}
|
||||||
|
|
||||||
private void checkNullPointerException(Callable<?> c) {
|
private void checkNullPointerException(Callable<?> c) {
|
||||||
try {
|
try {
|
||||||
c.call();
|
c.call();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue