# HG changeset patch # User rfield # Date 1471640126 25200 # Node ID b9359154240c8df63de956014fbb5615c08c8f65 # Parent 1b3c502e0bdc9a0457e7874665fee350e2ca81b2 8158906: JShell: crashes with extremely long result value Reviewed-by: jlahoda, shinyafox diff -r 1b3c502e0bdc -r b9359154240c langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/ExecutionControlForwarder.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/ExecutionControlForwarder.java Fri Aug 19 12:54:02 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/ExecutionControlForwarder.java Fri Aug 19 13:55:26 2016 -0700 @@ -44,6 +44,13 @@ */ class ExecutionControlForwarder { + /** + * Maximum number of characters for writeUTF(). Byte maximum is 65535, at + * maximum three bytes per character that is 65535 / 3 == 21845. Minus one + * for safety. + */ + private static final int MAX_UTF_CHARS = 21844; + private final ExecutionControl ec; private final ObjectInput in; private final ObjectOutput out; @@ -89,6 +96,9 @@ private void writeUTF(String s) throws IOException { if (s == null) { s = ""; + } else if (s.length() > MAX_UTF_CHARS) { + // Truncate extremely long strings to prevent writeUTF from crashing the VM + s = s.substring(0, MAX_UTF_CHARS); } out.writeUTF(s); } diff -r 1b3c502e0bdc -r b9359154240c langtools/test/jdk/jshell/SimpleRegressionTest.java --- a/langtools/test/jdk/jshell/SimpleRegressionTest.java Fri Aug 19 12:54:02 2016 -0700 +++ b/langtools/test/jdk/jshell/SimpleRegressionTest.java Fri Aug 19 13:55:26 2016 -0700 @@ -22,7 +22,7 @@ */ /* - * @test 8130450 + * @test 8130450 8158906 * @summary simple regression test * @build KullaTesting TestingInputStream * @run testng SimpleRegressionTest @@ -39,6 +39,8 @@ import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import static jdk.jshell.Snippet.Status.OVERWRITTEN; import static jdk.jshell.Snippet.SubKind.TEMP_VAR_EXPRESSION_SUBKIND; import static jdk.jshell.Snippet.Status.VALID; @@ -100,6 +102,37 @@ assertEquals(sne.typeName(), "Integer"); } + public void testLongRemoteStrings() { //8158906 + assertEval("String m(int x) { byte[] b = new byte[x]; for (int i = 0; i < x; ++i) b[i] = (byte) 'a'; return new String(b); }"); + boolean[] shut = new boolean[1]; + getState().onShutdown(j -> { + shut[0] = true; + }); + for (String len : new String[]{"12345", "64000", "65535", "65536", "120000"}) { + List el = assertEval("m(" + len + ");"); + assertFalse(shut[0], "JShell died with long string"); + assertEquals(el.size(), 1, "Excepted one event"); + assertTrue(el.get(0).value().length() > 10000, + "Expected truncated but long String, got: " + el.get(0).value().length()); + } + } + + public void testLongRemoteJapaneseStrings() { //8158906 + assertEval("import java.util.stream.*;"); + assertEval("String m(int x) { return Stream.generate(() -> \"\u3042\").limit(x).collect(Collectors.joining()); }"); + boolean[] shut = new boolean[1]; + getState().onShutdown(j -> { + shut[0] = true; + }); + for (String len : new String[]{"12345", "21843", "21844", "21845", "21846", "64000", "65535", "65536", "120000"}) { + List el = assertEval("m(" + len + ");"); + assertFalse(shut[0], "JShell died with long string"); + assertEquals(el.size(), 1, "Excepted one event"); + assertTrue(el.get(0).value().length() > 10000, + "Expected truncated but long String, got: " + el.get(0).value().length()); + } + } + // 8130450 public void testDuplicate() { Snippet snm = methodKey(assertEval("void mm() {}", added(VALID)));