8218227: StringBuilder/StringBuffer constructor throws confusing NegativeArraySizeException
authorigerasim
Tue, 05 Feb 2019 17:05:40 -0800
changeset 53653 868611f0adc3
parent 53652 262afafdb266
child 53654 7054249afee5
8218227: StringBuilder/StringBuffer constructor throws confusing NegativeArraySizeException Reviewed-by: rriggs
src/java.base/share/classes/java/lang/AbstractStringBuilder.java
src/java.base/share/classes/java/lang/StringBuffer.java
src/java.base/share/classes/java/lang/StringBuilder.java
test/jdk/java/lang/StringBuffer/HugeCapacity.java
test/jdk/java/lang/StringBuilder/HugeCapacity.java
--- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java	Wed Feb 06 00:20:37 2019 +0100
+++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java	Tue Feb 05 17:05:40 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -92,6 +92,19 @@
     }
 
     /**
+     * Creates an AbstractStringBuilder with the specified coder and with
+     * the initial capacity equal to the smaller of (capacity + addition)
+     * and Integer.MAX_VALUE.
+     */
+    AbstractStringBuilder(byte coder, int capacity, int addition) {
+        this.coder = coder;
+        capacity = (capacity < Integer.MAX_VALUE - addition)
+                ? capacity + addition : Integer.MAX_VALUE;
+        value = (coder == LATIN1)
+                ? new byte[capacity] : StringUTF16.newBytesFor(capacity);
+    }
+
+    /**
      * Compares the objects of two AbstractStringBuilder implementations lexicographically.
      *
      * @since 11
--- a/src/java.base/share/classes/java/lang/StringBuffer.java	Wed Feb 06 00:20:37 2019 +0100
+++ b/src/java.base/share/classes/java/lang/StringBuffer.java	Tue Feb 05 17:05:40 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, 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
@@ -148,7 +148,7 @@
      */
     @HotSpotIntrinsicCandidate
     public StringBuffer(String str) {
-        super(str.length() + 16);
+        super(str.coder(), str.length(), 16);
         append(str);
     }
 
@@ -166,7 +166,7 @@
      * @since 1.5
      */
     public StringBuffer(CharSequence seq) {
-        this(seq.length() + 16);
+        super(String.LATIN1, seq.length(), 16);
         append(seq);
     }
 
--- a/src/java.base/share/classes/java/lang/StringBuilder.java	Wed Feb 06 00:20:37 2019 +0100
+++ b/src/java.base/share/classes/java/lang/StringBuilder.java	Tue Feb 05 17:05:40 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -121,7 +121,7 @@
      */
     @HotSpotIntrinsicCandidate
     public StringBuilder(String str) {
-        super(str.length() + 16);
+        super(str.coder(), str.length(), 16);
         append(str);
     }
 
@@ -134,7 +134,7 @@
      * @param      seq   the sequence to copy.
      */
     public StringBuilder(CharSequence seq) {
-        this(seq.length() + 16);
+        super(String.LATIN1, seq.length(), 16);
         append(seq);
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/StringBuffer/HugeCapacity.java	Tue Feb 05 17:05:40 2019 -0800
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019, 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 8218227
+ * @summary StringBuilder/StringBuffer constructor throws confusing
+ *          NegativeArraySizeException
+ * @requires os.maxMemory >= 6G
+ * @run main/othervm -Xms5G -Xmx5G HugeCapacity
+ * @ignore This test has huge memory requirements
+ */
+
+public class HugeCapacity {
+    private static int failures = 0;
+
+    public static void main(String[] args) {
+        testHugeInitialString();
+        testHugeInitialCharSequence();
+        if (failures > 0) {
+            throw new RuntimeException(failures + " tests failed");
+        }
+    }
+
+    private static void testHugeInitialString() {
+        try {
+            String str = "Z".repeat(Integer.MAX_VALUE - 8);
+            StringBuffer sb = new StringBuffer(str);
+        } catch (OutOfMemoryError ignore) {
+        } catch (Throwable unexpected) {
+            unexpected.printStackTrace();
+            failures++;
+        }
+    }
+
+    private static void testHugeInitialCharSequence() {
+        try {
+            CharSequence seq = new MyHugeCharSeq();
+            StringBuffer sb = new StringBuffer(seq);
+        } catch (OutOfMemoryError ignore) {
+        } catch (Throwable unexpected) {
+            unexpected.printStackTrace();
+            failures++;
+        }
+    }
+
+    private static class MyHugeCharSeq implements CharSequence {
+        public char charAt(int i) {
+            throw new UnsupportedOperationException();
+        }
+        public int length() { return Integer.MAX_VALUE; }
+        public CharSequence subSequence(int st, int e) {
+            throw new UnsupportedOperationException();
+        }
+        public String toString() { return ""; }
+    }
+}
--- a/test/jdk/java/lang/StringBuilder/HugeCapacity.java	Wed Feb 06 00:20:37 2019 +0100
+++ b/test/jdk/java/lang/StringBuilder/HugeCapacity.java	Tue Feb 05 17:05:40 2019 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -23,10 +23,11 @@
 
 /**
  * @test
- * @bug 8149330
+ * @bug 8149330 8218227
  * @summary Capacity should not get close to Integer.MAX_VALUE unless
  *          necessary
- * @run main/othervm -Xmx5G HugeCapacity
+ * @requires os.maxMemory >= 6G
+ * @run main/othervm -Xms5G -Xmx5G HugeCapacity
  * @ignore This test has huge memory requirements
  */
 
@@ -36,6 +37,8 @@
     public static void main(String[] args) {
         testLatin1();
         testUtf16();
+        testHugeInitialString();
+        testHugeInitialCharSequence();
         if (failures > 0) {
             throw new RuntimeException(failures + " tests failed");
         }
@@ -63,4 +66,37 @@
             failures++;
         }
     }
+
+    private static void testHugeInitialString() {
+        try {
+            String str = "Z".repeat(Integer.MAX_VALUE - 8);
+            StringBuilder sb = new StringBuilder(str);
+        } catch (OutOfMemoryError ignore) {
+        } catch (Throwable unexpected) {
+            unexpected.printStackTrace();
+            failures++;
+        }
+    }
+
+    private static void testHugeInitialCharSequence() {
+        try {
+            CharSequence seq = new MyHugeCharSeq();
+            StringBuilder sb = new StringBuilder(seq);
+        } catch (OutOfMemoryError ignore) {
+        } catch (Throwable unexpected) {
+            unexpected.printStackTrace();
+            failures++;
+        }
+    }
+
+    private static class MyHugeCharSeq implements CharSequence {
+        public char charAt(int i) {
+            throw new UnsupportedOperationException();
+        }
+        public int length() { return Integer.MAX_VALUE; }
+        public CharSequence subSequence(int st, int e) {
+            throw new UnsupportedOperationException();
+        }
+        public String toString() { return ""; }
+    }
 }