Add null checks to String methods.
Also did some general cleanup and
fixed String tests to not be optimized by the compiler.
Change-Id: I503aebb5b60764e74406e408985bf57437e88498
diff --git a/user/super/com/google/gwt/emul/java/lang/Character.java b/user/super/com/google/gwt/emul/java/lang/Character.java
index ca0c93d..61db5e1 100644
--- a/user/super/com/google/gwt/emul/java/lang/Character.java
+++ b/user/super/com/google/gwt/emul/java/lang/Character.java
@@ -230,7 +230,7 @@
* TODO: correct Unicode handling.
*/
public static boolean isDigit(char c) {
- return String.valueOf(c).nativeMatches(digitRegex());
+ return digitRegex().test(String.valueOf(c));
}
private static native NativeRegExp digitRegex() /*-{
@@ -245,7 +245,7 @@
* TODO: correct Unicode handling.
*/
public static boolean isLetter(char c) {
- return String.valueOf(c).nativeMatches(leterRegex());
+ return leterRegex().test(String.valueOf(c));
}
private static native NativeRegExp leterRegex() /*-{
@@ -256,7 +256,7 @@
* TODO: correct Unicode handling.
*/
public static boolean isLetterOrDigit(char c) {
- return String.valueOf(c).nativeMatches(leterOrDigitRegex());
+ return leterOrDigitRegex().test(String.valueOf(c));
}
private static native NativeRegExp leterOrDigitRegex() /*-{
@@ -296,11 +296,11 @@
}
public static boolean isWhitespace(char ch) {
- return String.valueOf(ch).nativeMatches(whitespaceRegex());
+ return whitespaceRegex().test(String.valueOf(ch));
}
public static boolean isWhitespace(int codePoint) {
- return String.fromCodePoint(codePoint).nativeMatches(whitespaceRegex());
+ return whitespaceRegex().test(String.fromCodePoint(codePoint));
}
// The regex would just be /\s/, but browsers handle non-breaking spaces inconsistently. Also,
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 1e355e3..18bd4c0 100644
--- a/user/super/com/google/gwt/emul/java/lang/String.java
+++ b/user/super/com/google/gwt/emul/java/lang/String.java
@@ -210,13 +210,6 @@
return replaceStr;
}
- private static native int compareTo(String thisStr, String otherStr) /*-{
- if (thisStr == otherStr) {
- return 0;
- }
- return thisStr < otherStr ? -1 : 1;
- }-*/;
-
private static Charset getCharset(String charsetName) throws UnsupportedEncodingException {
try {
return Charset.forName(charsetName);
@@ -382,15 +375,15 @@
@Override
public int compareTo(String other) {
- return compareTo(this, other);
+ return JsUtils.compare(checkNotNull(this), checkNotNull(other));
}
public int compareToIgnoreCase(String other) {
- return compareTo(toLowerCase(), other.toLowerCase());
+ return toLowerCase().compareTo(other.toLowerCase());
}
public String concat(String str) {
- return this + str;
+ return checkNotNull(this) + checkNotNull(str);
}
public boolean contains(CharSequence s) {
@@ -421,18 +414,15 @@
}
public boolean equalsIgnoreCase(String other) {
- return equalsIgnoreCase(this, other);
- }
-
- private static native boolean equalsIgnoreCase(String s, String other) /*-{
+ checkNotNull(this);
if (other == null) {
return false;
}
- if (s == other) {
+ if (equals(other)) {
return true;
}
- return (s.length == other.length) && (s.toLowerCase() == other.toLowerCase());
- }-*/;
+ return length() == other.length() && toLowerCase().equals(other.toLowerCase());
+ }
public byte[] getBytes() {
// default character set for GWT is UTF-8
@@ -450,7 +440,10 @@
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) {
checkCriticalStringBounds(srcBegin, srcEnd, length());
checkCriticalStringBounds(dstBegin, dstBegin + (srcEnd - srcBegin), dst.length);
+ getChars0(srcBegin, srcEnd, dst, dstBegin);
+ }
+ private void getChars0(int srcBegin, int srcEnd, char[] dst, int dstBegin) {
while (srcBegin < srcEnd) {
dst[dstBegin++] = charAt(srcBegin++);
}
@@ -476,9 +469,9 @@
public int indexOf(String str, int startIndex) {
return asNativeString().indexOf(str, startIndex);
}
-
+
public String intern() {
- return this;
+ return checkNotNull(this);
}
public boolean isEmpty() {
@@ -516,11 +509,7 @@
*/
public boolean matches(String regex) {
// We surround the regex with '^' and '$' because it must match the entire string.
- return nativeMatches(new NativeRegExp("^(" + regex + ")$"));
- }
-
- boolean nativeMatches(NativeRegExp regex) {
- return regex.test(this);
+ return new NativeRegExp("^(" + regex + ")$").test(this);
}
public int offsetByCodePoints(int index, int codePointOffset) {
@@ -529,13 +518,11 @@
public boolean regionMatches(boolean ignoreCase, int toffset, String other,
int ooffset, int len) {
- if (other == null) {
- throw new NullPointerException();
- }
+ checkNotNull(other);
if (toffset < 0 || ooffset < 0 || len <= 0) {
return false;
}
- if (toffset + len > this.length() || ooffset + len > other.length()) {
+ if (toffset + len > length() || ooffset + len > other.length()) {
return false;
}
@@ -692,11 +679,11 @@
@Override
public CharSequence subSequence(int beginIndex, int endIndex) {
- return this.substring(beginIndex, endIndex);
+ return substring(beginIndex, endIndex);
}
public String substring(int beginIndex) {
- return asNativeString().substr(beginIndex, this.length() - beginIndex);
+ return asNativeString().substr(beginIndex, length() - beginIndex);
}
public String substring(int beginIndex, int endIndex) {
@@ -704,9 +691,9 @@
}
public char[] toCharArray() {
- int n = this.length();
+ int n = length();
char[] charArr = new char[n];
- getChars(0, n, charArr, 0);
+ getChars0(0, n, charArr, 0);
return charArr;
}
@@ -833,15 +820,15 @@
}
protected static String $create(String other) {
- return other;
+ return checkNotNull(other);
}
protected static String $create(StringBuffer sb) {
- return String.valueOf(sb);
+ return sb.toString();
}
protected static String $create(StringBuilder sb) {
- return String.valueOf(sb);
+ return sb.toString();
}
@JsMethod
diff --git a/user/super/com/google/gwt/emul/java/lang/StringBuffer.java b/user/super/com/google/gwt/emul/java/lang/StringBuffer.java
index 39be45d..91f154a 100644
--- a/user/super/com/google/gwt/emul/java/lang/StringBuffer.java
+++ b/user/super/com/google/gwt/emul/java/lang/StringBuffer.java
@@ -1,12 +1,12 @@
/*
* Copyright 2008 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -15,9 +15,11 @@
*/
package java.lang;
+import static javaemul.internal.InternalPreconditions.checkNotNull;
+
/**
* A fast way to create strings using multiple appends.
- *
+ *
* This class is an exact clone of {@link StringBuilder} except for the name.
* Any change made to one should be mirrored in the other.
*/
@@ -28,7 +30,7 @@
}
public StringBuffer(CharSequence s) {
- super(String.valueOf(s));
+ super(s.toString());
}
/**
@@ -41,7 +43,7 @@
}
public StringBuffer(String s) {
- super(s);
+ super(checkNotNull(s));
}
public StringBuffer append(boolean x) {
diff --git a/user/super/com/google/gwt/emul/java/lang/StringBuilder.java b/user/super/com/google/gwt/emul/java/lang/StringBuilder.java
index 78bcd03..da705ef 100644
--- a/user/super/com/google/gwt/emul/java/lang/StringBuilder.java
+++ b/user/super/com/google/gwt/emul/java/lang/StringBuilder.java
@@ -1,12 +1,12 @@
/*
* Copyright 2008 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -15,9 +15,11 @@
*/
package java.lang;
+import static javaemul.internal.InternalPreconditions.checkNotNull;
+
/**
* A fast way to create strings using multiple appends.
- *
+ *
* This class is an exact clone of {@link StringBuffer} except for the name.
* Any change made to one should be mirrored in the other.
*/
@@ -28,7 +30,7 @@
}
public StringBuilder(CharSequence s) {
- super(String.valueOf(s));
+ super(s.toString());
}
/**
@@ -41,7 +43,7 @@
}
public StringBuilder(String s) {
- super(s);
+ super(checkNotNull(s));
}
public StringBuilder append(boolean x) {
diff --git a/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java b/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
index 981f224..8796b61 100644
--- a/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
+++ b/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
@@ -20,6 +20,10 @@
*/
public class JsUtils {
+ public static native int compare(String a, String b) /*-{
+ return a == b ? 0 : (a < b ? -1 : 1);
+ }-*/;
+
public static native boolean isFinite(double d) /*-{
return isFinite(d);
}-*/;
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 b9f06aa..3b556ce 100644
--- a/user/test/com/google/gwt/emultest/java/lang/StringTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/StringTest.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.emultest.java.lang;
+import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.testing.TestUtils;
@@ -31,22 +32,13 @@
*/
public class StringTest extends GWTTestCase {
- static volatile boolean TRUE = true;
-
- private static <T> T hideFromCompiler(T value) {
- return TRUE ? value : null;
- }
-
@Override
public String getModuleName() {
return "com.google.gwt.emultest.EmulSuite";
}
- /*
- * TODO: needs rewriting to avoid compiler optimizations.
- */
public void testCharAt() {
- assertEquals("abc".charAt(1), 'b');
+ assertEquals(hideFromCompiler("abc").charAt(1), 'b');
}
public void testCodePoint() {
@@ -103,6 +95,30 @@
assertEquals(1, nonBmpChar.codePointCount(0, 2));
}
+ public void testCompareTo() {
+ assertEquals(0, hideFromCompiler("a").compareTo("a"));
+ assertEquals(-1, hideFromCompiler("a").compareTo("b"));
+ assertEquals(1, hideFromCompiler("b").compareTo("a"));
+
+ try {
+ returnNull().compareTo("");
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
+
+ try {
+ returnNull().compareTo(returnNull());
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
+ }
+
public void testConcat() {
String abc = String.valueOf(new char[] {'a', 'b', 'c'});
String def = String.valueOf(new char[] {'d', 'e', 'f'});
@@ -118,6 +134,24 @@
assertEquals("abcd", s + c);
s += c;
assertEquals("abcd", s);
+
+ try {
+ returnNull().concat("");
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
+
+ try {
+ hideFromCompiler("").concat(returnNull());
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
}
public void testConstructor() {
@@ -147,6 +181,22 @@
sb = new StringBuilder();
sb.appendCodePoint(0x10400);
assertEquals("\uD801\uDC00", new String(sb));
+
+ try {
+ new String(returnNull());
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
+
+ try {
+ new String(hideFromCompiler((StringBuilder) null));
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
}
public void testConstructorBytes() {
@@ -265,26 +315,22 @@
}
}
- /*
- * TODO: needs rewriting to avoid compiler optimizations. (StringBuffer tests
- * are ok)
- */
public void testContains() {
// at the beginning
- assertTrue("abcdef".contains("ab"));
- assertTrue("abcdef".contains(new StringBuffer("ab")));
+ assertTrue(hideFromCompiler("abcdef").contains("ab"));
+ assertTrue(hideFromCompiler("abcdef").contains(new StringBuffer("ab")));
// at the end
- assertTrue("abcdef".contains("ef"));
- assertTrue("abcdef".contains(new StringBuffer("ef")));
+ assertTrue(hideFromCompiler("abcdef").contains("ef"));
+ assertTrue(hideFromCompiler("abcdef").contains(new StringBuffer("ef")));
// in the middle
- assertTrue("abcdef".contains("cd"));
- assertTrue("abcdef".contains(new StringBuffer("cd")));
+ assertTrue(hideFromCompiler("abcdef").contains("cd"));
+ assertTrue(hideFromCompiler("abcdef").contains(new StringBuffer("cd")));
// the same
- assertTrue("abcdef".contains("abcdef"));
- assertTrue("abcdef".contains(new StringBuffer("abcdef")));
+ assertTrue(hideFromCompiler("abcdef").contains("abcdef"));
+ assertTrue(hideFromCompiler("abcdef").contains(new StringBuffer("abcdef")));
// not present
- assertFalse("abcdef".contains("z"));
- assertFalse("abcdef".contains(new StringBuffer("z")));
+ assertFalse(hideFromCompiler("abcdef").contains("z"));
+ assertFalse(hideFromCompiler("abcdef").contains(new StringBuffer("z")));
}
public void testEndsWith() {
@@ -295,38 +341,64 @@
}
public void testEquals() {
- assertFalse("ABC".equals("abc"));
- assertFalse("abc".equals("ABC"));
- assertTrue("abc".equals("abc"));
- assertTrue("ABC".equals("ABC"));
- assertFalse("AbC".equals("aBC"));
- assertFalse("AbC".equals("aBC"));
- assertTrue("".equals(""));
- assertFalse("".equals(null));
+ assertFalse(hideFromCompiler("ABC").equals("abc"));
+ assertFalse(hideFromCompiler("abc").equals("ABC"));
+ assertTrue(hideFromCompiler("abc").equals("abc"));
+ assertTrue(hideFromCompiler("ABC").equals("ABC"));
+ assertFalse(hideFromCompiler("AbC").equals("aBC"));
+ assertFalse(hideFromCompiler("AbC").equals("aBC"));
+ assertTrue(hideFromCompiler("").equals(""));
+ assertFalse(hideFromCompiler("").equals(null));
- // Randomize the string to avoid static eval.
- double r = Math.random();
- assertTrue((r + "").equals(r + ""));
- assertFalse((r + "ABC").equals(r + "abc"));
- assertFalse((r + "abc").equals(r + "ABC"));
- assertTrue((r + "abc").equals(r + "abc"));
- assertTrue((r + "ABC").equals(r + "ABC"));
- assertFalse((r + "AbC").equals(r + "aBC"));
- assertFalse((r + "AbC").equals(r + "aBC"));
+ // TODO: String.equals does not have NPE check
+/*
+ try {
+ returnNull().equals("other");
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
+
+ try {
+ returnNull().equals(returnNull());
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
+*/
}
- /*
- * TODO: needs rewriting to avoid compiler optimizations.
- */
public void testEqualsIgnoreCase() {
- assertTrue("ABC".equalsIgnoreCase("abc"));
- assertTrue("abc".equalsIgnoreCase("ABC"));
- assertTrue("abc".equalsIgnoreCase("abc"));
- assertTrue("ABC".equalsIgnoreCase("ABC"));
- assertTrue("AbC".equalsIgnoreCase("aBC"));
- assertTrue("AbC".equalsIgnoreCase("aBC"));
- assertTrue("".equalsIgnoreCase(""));
- assertFalse("".equalsIgnoreCase(null));
+ assertTrue(hideFromCompiler("ABC").equalsIgnoreCase("abc"));
+ assertTrue(hideFromCompiler("abc").equalsIgnoreCase("ABC"));
+ assertTrue(hideFromCompiler("abc").equalsIgnoreCase("abc"));
+ assertTrue(hideFromCompiler("ABC").equalsIgnoreCase("ABC"));
+ assertTrue(hideFromCompiler("AbC").equalsIgnoreCase("aBC"));
+ assertTrue(hideFromCompiler("AbC").equalsIgnoreCase("aBC"));
+ assertTrue(hideFromCompiler("").equalsIgnoreCase(""));
+ assertFalse(hideFromCompiler("").equalsIgnoreCase(null));
+
+ try {
+ returnNull().equalsIgnoreCase("other");
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
+
+ try {
+ returnNull().equalsIgnoreCase(returnNull());
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
}
public void testGetBytesAscii() {
@@ -440,6 +512,15 @@
// get hashes again to verify the values are constant for a given string
assertEquals(expectedHash, testStrings[i].hashCode());
}
+
+ try {
+ returnNull().hashCode();
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
}
public void testIndexOf() {
@@ -451,6 +532,15 @@
assertEquals(haystack.indexOf('a', 1), -1);
assertEquals(haystack.indexOf("bc"), 1);
assertEquals(haystack.indexOf(""), 0);
+
+ try {
+ returnNull().indexOf("");
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
}
public void testIntern() {
@@ -458,6 +548,15 @@
String s2 = String.valueOf(new char[] {'a', 'b', 'c', 'd', 'e', 'f'});
assertTrue("strings not equal", s1.equals(s2));
assertSame("interns are not the same reference", s1.intern(), s2.intern());
+
+ try {
+ returnNull().intern();
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ } catch (JavaScriptException e) {
+ // expected
+ }
}
public void testLastIndexOf() {
@@ -479,21 +578,18 @@
assertEquals(4, cat.length());
}
- /*
- * TODO: needs rewriting to avoid compiler optimizations.
- */
public void testLowerCase() {
- assertEquals("abc", "AbC".toLowerCase());
- assertEquals("abc", "abc".toLowerCase());
- assertEquals("", "".toLowerCase());
+ assertEquals("abc", hideFromCompiler("AbC").toLowerCase());
+ assertEquals("abc", hideFromCompiler("abc").toLowerCase());
+ assertEquals("", hideFromCompiler("").toLowerCase());
- assertEquals("abc", "AbC".toLowerCase(Locale.US));
- assertEquals("abc", "abc".toLowerCase(Locale.US));
- assertEquals("", "".toLowerCase(Locale.US));
+ assertEquals("abc", hideFromCompiler("AbC").toLowerCase(Locale.US));
+ assertEquals("abc", hideFromCompiler("abc").toLowerCase(Locale.US));
+ assertEquals("", hideFromCompiler("").toLowerCase(Locale.US));
- assertEquals("abc", "AbC".toLowerCase(Locale.getDefault()));
- assertEquals("abc", "abc".toLowerCase(Locale.getDefault()));
- assertEquals("", "".toLowerCase(Locale.getDefault()));
+ assertEquals("abc", hideFromCompiler("AbC").toLowerCase(Locale.getDefault()));
+ assertEquals("abc", hideFromCompiler("abc").toLowerCase(Locale.getDefault()));
+ assertEquals("", hideFromCompiler("").toLowerCase(Locale.getDefault()));
}
public void testMatch() {
@@ -687,14 +783,10 @@
assertFalse(haystack.startsWith(haystack + "j"));
}
- /*
- * TODO: needs rewriting to avoid compiler optimizations.
- */
public void testSubstring() {
- String haystack = "abcdefghi";
- assertEquals("cd", haystack.substring(2, 4));
- assertEquals("bc", "abcdef".substring(1, 3));
- assertEquals("bcdef", "abcdef".substring(1));
+ assertEquals("cd", hideFromCompiler("abcdefghi").substring(2, 4));
+ assertEquals("bc", hideFromCompiler("abcdef").substring(1, 3));
+ assertEquals("bcdef", hideFromCompiler("abcdef").substring(1));
}
public void testToCharArray() {
@@ -712,7 +804,7 @@
assertEquals("concat with str.toString()", "0abcd", "0" + str.toString() + "d");
// issue 4301
- Object s = TRUE ? "abc" : createJavaScriptObject();
+ Object s = hideFromCompiler("abc");
assertSame("s same as s.toString()", s, s.toString());
}
@@ -751,21 +843,18 @@
trimRightAssertEquals("\u2029abc\u00a0","\u2029abc\u00a0");
}
- /*
- * TODO: needs rewriting to avoid compiler optimizations.
- */
public void testUpperCase() {
- assertEquals("ABC", "AbC".toUpperCase());
- assertEquals("ABC", "abc".toUpperCase());
- assertEquals("", "".toUpperCase());
+ assertEquals("ABC", hideFromCompiler("AbC").toUpperCase());
+ assertEquals("ABC", hideFromCompiler("abc").toUpperCase());
+ assertEquals("", hideFromCompiler("").toUpperCase());
- assertEquals("ABC", "AbC".toUpperCase(Locale.US));
- assertEquals("ABC", "abc".toUpperCase(Locale.US));
- assertEquals("", "".toUpperCase(Locale.US));
+ assertEquals("ABC", hideFromCompiler("AbC").toUpperCase(Locale.US));
+ assertEquals("ABC", hideFromCompiler("abc").toUpperCase(Locale.US));
+ assertEquals("", hideFromCompiler("").toUpperCase(Locale.US));
- assertEquals("ABC", "AbC".toUpperCase(Locale.getDefault()));
- assertEquals("ABC", "abc".toUpperCase(Locale.getDefault()));
- assertEquals("", "".toUpperCase(Locale.getDefault()));
+ assertEquals("ABC", hideFromCompiler("AbC").toUpperCase(Locale.getDefault()));
+ assertEquals("ABC", hideFromCompiler("abc").toUpperCase(Locale.getDefault()));
+ assertEquals("", hideFromCompiler("").toUpperCase(Locale.getDefault()));
}
/*
@@ -811,28 +900,20 @@
}
}
- private String returnNull() {
+ private <T> T hideFromCompiler(T value) {
if (Math.random() < -1) {
// Can never happen, but fools the compiler enough not to optimize this call.
fail();
- return "";
}
- return null;
+ return value;
+ }
+
+ private String returnNull() {
+ return hideFromCompiler(null);
}
private String toS(char from) {
return Character.toString(from);
}
- private static Object createJavaScriptObject() {
- if (TestUtils.isJvm()) {
- return new Object();
- }
- return createJavaScriptObject0();
- }
-
- private static native Object createJavaScriptObject0() /*-{
- return {};
- }-*/;
-
}