Check bounds arguments in String#substring
... and various API depending on it
PiperOrigin-RevId: 309475943
Change-Id: I54785fe304787ced52c64dc3827977bf992b5324
diff --git a/user/super/com/google/gwt/emul/java/io/Writer.java b/user/super/com/google/gwt/emul/java/io/Writer.java
index a2e34e2..d837120 100644
--- a/user/super/com/google/gwt/emul/java/io/Writer.java
+++ b/user/super/com/google/gwt/emul/java/io/Writer.java
@@ -15,7 +15,6 @@
*/
package java.io;
-import static javaemul.internal.InternalPreconditions.checkCriticalStringBounds;
import static javaemul.internal.InternalPreconditions.checkNotNull;
import java.util.Objects;
@@ -76,15 +75,6 @@
if (csq == null) {
csq = "null";
}
- /*
- * Ensure we throw StringIndexOutOfBoundsException as String#subsequence method depends on
- * JavaScript's string#substr() which has different behaviour than original Java method.
- *
- * JavaScript behaviour of substr(start, length):
- * "gwt".substr(0, -1) returns ""
- * "gwt".substr(-1, 1) returns "t" -> Calculates substring from end.
- */
- checkCriticalStringBounds(start, end, csq.length());
write(csq.subSequence(start, end).toString());
return this;
}
diff --git a/user/super/com/google/gwt/emul/java/lang/AbstractStringBuilder.java b/user/super/com/google/gwt/emul/java/lang/AbstractStringBuilder.java
index dabfd83..159dbde 100644
--- a/user/super/com/google/gwt/emul/java/lang/AbstractStringBuilder.java
+++ b/user/super/com/google/gwt/emul/java/lang/AbstractStringBuilder.java
@@ -15,6 +15,8 @@
*/
package java.lang;
+import static javaemul.internal.InternalPreconditions.checkStringElementIndex;
+
/**
* A base class to share implementation between {@link StringBuffer} and {@link StringBuilder}.
* <p>
@@ -113,6 +115,14 @@
}
void replace0(int start, int end, String toInsert) {
+ int length = string.length();
+ if (end > length) {
+ end = length;
+ } else {
+ // Only checking for start > end; since rest is checked with substring.
+ checkStringElementIndex(start, end + 1);
+ }
+
string = string.substring(0, start) + toInsert + string.substring(end);
}
@@ -142,4 +152,4 @@
buffer[f] = buffer[s];
buffer[s] = tmp;
}
-}
\ No newline at end of file
+}
diff --git a/user/super/com/google/gwt/emul/java/lang/String.java b/user/super/com/google/gwt/emul/java/lang/String.java
index 0b58db7..45ddd0e 100644
--- a/user/super/com/google/gwt/emul/java/lang/String.java
+++ b/user/super/com/google/gwt/emul/java/lang/String.java
@@ -18,6 +18,7 @@
import static javaemul.internal.InternalPreconditions.checkCriticalStringBounds;
import static javaemul.internal.InternalPreconditions.checkNotNull;
+import static javaemul.internal.InternalPreconditions.checkStringBounds;
import static javaemul.internal.InternalPreconditions.checkStringElementIndex;
import java.io.Serializable;
@@ -27,14 +28,12 @@
import java.util.Comparator;
import java.util.Locale;
import java.util.StringJoiner;
-
import javaemul.internal.ArrayHelper;
import javaemul.internal.EmulatedCharset;
import javaemul.internal.HashCodes;
import javaemul.internal.JsUtils;
import javaemul.internal.NativeRegExp;
import javaemul.internal.annotations.DoNotInline;
-
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsNonNull;
import jsinterop.annotations.JsPackage;
@@ -560,7 +559,8 @@
// treat "$$" as "$".
// Escape regex special characters from literal replacement string.
- String regex = from.toString().replaceAll("([/\\\\\\.\\*\\+\\?\\|\\(\\)\\[\\]\\{\\}$^])", "\\\\$1");
+ String regex =
+ from.toString().replaceAll("([/\\\\\\.\\*\\+\\?\\|\\(\\)\\[\\]\\{\\}$^])", "\\\\$1");
// Escape $ since it is for match backrefs and \ since it is used to escape
// $.
String replacement = to.toString().replaceAll("\\\\", "\\\\\\\\").replaceAll("\\$", "\\\\$");
@@ -682,10 +682,12 @@
}
public String substring(int beginIndex) {
+ checkStringElementIndex(beginIndex, length() + 1);
return asNativeString().substr(beginIndex);
}
public String substring(int beginIndex, int endIndex) {
+ checkStringBounds(beginIndex, endIndex, length());
return asNativeString().substr(beginIndex, endIndex - beginIndex);
}
@@ -732,10 +734,6 @@
@Override
public String toString() {
- /*
- * Magic: this method is only used during compiler optimizations; the generated JS will instead alias
- * this method to the native String.prototype.toString() function.
- */
return checkNotNull(this);
}
diff --git a/user/super/com/google/gwt/emul/javaemul/internal/InternalPreconditions.java b/user/super/com/google/gwt/emul/javaemul/internal/InternalPreconditions.java
index a427e59..a66c330 100644
--- a/user/super/com/google/gwt/emul/javaemul/internal/InternalPreconditions.java
+++ b/user/super/com/google/gwt/emul/javaemul/internal/InternalPreconditions.java
@@ -591,6 +591,23 @@
*
* @throws StringIndexOutOfBoundsException if the range is not legal
*/
+ public static void checkStringBounds(int start, int end, int length) {
+ if (IS_BOUNDS_CHECKED) {
+ checkCriticalStringBounds(start, end, length);
+ } else if (IS_ASSERTED) {
+ try {
+ checkCriticalStringBounds(start, end, length);
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+ }
+
+ /**
+ * Checks that string bounds are correct.
+ *
+ * @throws StringIndexOutOfBoundsException if the range is not legal
+ */
public static void checkCriticalStringBounds(int start, int end, int length) {
if (start < 0 || end > length || end < start) {
throw new StringIndexOutOfBoundsException(
diff --git a/user/test/com/google/gwt/emultest/java/lang/StringTest.java b/user/test/com/google/gwt/emultest/java/lang/StringTest.java
index e940b92..4791200 100644
--- a/user/test/com/google/gwt/emultest/java/lang/StringTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/StringTest.java
@@ -811,6 +811,32 @@
assertEquals("cd", hideFromCompiler("abcdefghi").substring(2, 4));
assertEquals("bc", hideFromCompiler("abcdef").substring(1, 3));
assertEquals("bcdef", hideFromCompiler("abcdef").substring(1));
+ assertEquals("", hideFromCompiler("abcdef").substring(6));
+ assertEquals("", hideFromCompiler("abcdef").substring(6, 6));
+
+ try {
+ hideFromCompiler("abc").substring(-1);
+ fail("Should have thrown");
+ } catch (IndexOutOfBoundsException expected) {
+ }
+
+ try {
+ hideFromCompiler("abc").substring(4);
+ fail("Should have thrown");
+ } catch (IndexOutOfBoundsException expected) {
+ }
+
+ try {
+ hideFromCompiler("abc").substring(2, 4);
+ fail("Should have thrown");
+ } catch (IndexOutOfBoundsException expected) {
+ }
+
+ try {
+ hideFromCompiler("abc").substring(2, 1);
+ fail("Should have thrown");
+ } catch (IndexOutOfBoundsException expected) {
+ }
}
public void testToCharArray() {