From 60797793fa0f46bae4eb56df9fa35b8ba21db160 Mon Sep 17 00:00:00 2001 From: Colin Alworth Date: Sun, 8 Dec 2024 14:25:42 -0600 Subject: [PATCH 1/3] Support Java 8+ semantics for String.split() Fixes #8868 --- user/super/com/google/gwt/emul/java/lang/String.java | 12 ++++++++++++ .../google/gwt/emultest/java/lang/StringTest.java | 7 ++----- 2 files changed, 14 insertions(+), 5 deletions(-) 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 693f53bdc7..ed2179f08c 100644 --- a/user/super/com/google/gwt/emul/java/lang/String.java +++ b/user/super/com/google/gwt/emul/java/lang/String.java @@ -647,10 +647,22 @@ public String[] split(String regex, int maxMatch) { // subgroup handling NativeRegExp.Match matchObj = compiled.exec(trail); if (matchObj == null || trail == "" || (count == (maxMatch - 1) && maxMatch > 0)) { + // At the end of the string, or we have performed the maximum number of matches, + // record the remaining string and break out[count] = trail; break; } else { int matchIndex = matchObj.getIndex(); + + if (lastTrail == null && matchIndex == 0) { + // As of Java 8, we should discard the first zero-length match if it is the beginning of + // the string. Do not increment the count, and do not add to the output array. + trail = trail.substring(matchIndex + matchObj.asArray()[0].length(), trail.length()); + compiled.lastIndex = 0; + lastTrail = trail; + continue; + } + out[count] = trail.substring(0, matchIndex); trail = trail.substring(matchIndex + matchObj.asArray()[0].length(), trail.length()); // Force the compiled pattern to reset internal state 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 ea12fee74c..75c4053fe6 100644 --- a/user/test/com/google/gwt/emultest/java/lang/StringTest.java +++ b/user/test/com/google/gwt/emultest/java/lang/StringTest.java @@ -789,11 +789,8 @@ public void testSplit() { } public void testSplit_emptyExpr() { - // TODO(rluble): implement JDK8 string.split semantics and fix test. - String[] expected = (TestUtils.getJdkVersion() > 7) ? - new String[] {"a", "b", "c", "x", "x", "d", "e", "x", "f", "x"} : - new String[] {"", "a", "b", "c", "x", "x", "d", "e", "x", "f", "x"}; - compareList("emptyRegexSplit", expected, "abcxxdexfx".split("")); + String[] expected = new String[] {"a", "b", "c", "x", "x", "d", "e", "x", "f", "x"}; + compareList("emptyRegexSplit", expected, hideFromCompiler("abcxxdexfx").split("")); } public void testStartsWith() { From b38593d714c4b65c3e0c767ecaeea3496d6d24b9 Mon Sep 17 00:00:00 2001 From: Colin Alworth Date: Sun, 8 Dec 2024 20:03:40 -0600 Subject: [PATCH 2/3] Fix a bug, and fix unit tests for constant folding --- .../com/google/gwt/emul/java/lang/String.java | 2 +- .../java/lang/CompilerConstantStringTest.java | 27 ++++++++++++++----- .../gwt/emultest/java/lang/StringTest.java | 14 ++++++++-- 3 files changed, 34 insertions(+), 9 deletions(-) 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 ed2179f08c..42af2f1fe5 100644 --- a/user/super/com/google/gwt/emul/java/lang/String.java +++ b/user/super/com/google/gwt/emul/java/lang/String.java @@ -654,7 +654,7 @@ public String[] split(String regex, int maxMatch) { } else { int matchIndex = matchObj.getIndex(); - if (lastTrail == null && matchIndex == 0) { + if (lastTrail == null && matchIndex == 0 && matchObj.asArray()[0].length() == 0) { // As of Java 8, we should discard the first zero-length match if it is the beginning of // the string. Do not increment the count, and do not add to the output array. trail = trail.substring(matchIndex + matchObj.asArray()[0].length(), trail.length()); diff --git a/user/test/com/google/gwt/emultest/java/lang/CompilerConstantStringTest.java b/user/test/com/google/gwt/emultest/java/lang/CompilerConstantStringTest.java index fc58bdcbde..82aa4a8f02 100644 --- a/user/test/com/google/gwt/emultest/java/lang/CompilerConstantStringTest.java +++ b/user/test/com/google/gwt/emultest/java/lang/CompilerConstantStringTest.java @@ -245,15 +245,30 @@ public void testSplit() { "b", "", ":and:f"}); compareList("0:", "boo:and:foo".split(":", 0), new String[] { "boo", "and", "foo"}); - } + // issue 2742 + compareList("issue2742", new String[] {}, "/".split("/", 0)); + + // Splitting an empty string should result in an array containing a single + // empty string. + String[] s = "".split(","); + assertTrue(s != null); + assertTrue(s.length == 1); + assertTrue(s[0] != null); + assertTrue(s[0].length() == 0); + + s = "abcada".split("a"); + assertTrue(s != null); + assertEquals(3, s.length); + assertEquals("", s[0]); + assertEquals("bc", s[1]); + assertEquals("d", s[2]); } public void testSplit_emptyExpr() { - // TODO(rluble): implement JDK8 string.split semantics and fix test. - // See issue 8913. - String[] expected = (TestUtils.getJdkVersion() > 7) ? - new String[] {"a", "b", "c", "x", "x", "d", "e", "x", "f", "x"} : - new String[] {"", "a", "b", "c", "x", "x", "d", "e", "x", "f", "x"}; + String[] expected = new String[] {"a", "b", "c", "x", "x", "d", "e", "x", "f", "x"}; compareList("emptyRegexSplit", expected, "abcxxdexfx".split("")); + + String[] arr = ",".split(","); + assertEquals(0, arr.length); } public void testStartsWith() { 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 75c4053fe6..c1c436e4b3 100644 --- a/user/test/com/google/gwt/emultest/java/lang/StringTest.java +++ b/user/test/com/google/gwt/emultest/java/lang/StringTest.java @@ -781,20 +781,30 @@ public void testSplit() { // Splitting an empty string should result in an array containing a single // empty string. - String[] s = "".split(","); + String[] s = hideFromCompiler("").split(","); assertTrue(s != null); assertTrue(s.length == 1); assertTrue(s[0] != null); assertTrue(s[0].length() == 0); + + s = hideFromCompiler("abcada").split("a"); + assertTrue(s != null); + assertEquals(3, s.length); + assertEquals("", s[0]); + assertEquals("bc", s[1]); + assertEquals("d", s[2]); } public void testSplit_emptyExpr() { String[] expected = new String[] {"a", "b", "c", "x", "x", "d", "e", "x", "f", "x"}; compareList("emptyRegexSplit", expected, hideFromCompiler("abcxxdexfx").split("")); + + String[] arr = hideFromCompiler(",").split(","); + assertEquals(0, arr.length); } public void testStartsWith() { - String haystack = "abcdefghi"; + String haystack = hideFromCompiler("abcdefghi"); assertTrue(haystack.startsWith("abc")); assertTrue(haystack.startsWith("bc", 1)); assertTrue(haystack.startsWith(haystack)); From d9b39304a44923db5c0f02a5c92a63a367ab3953 Mon Sep 17 00:00:00 2001 From: Colin Alworth Date: Mon, 9 Dec 2024 07:06:37 -0600 Subject: [PATCH 3/3] Remove unused import --- .../gwt/emultest/java/lang/CompilerConstantStringTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/user/test/com/google/gwt/emultest/java/lang/CompilerConstantStringTest.java b/user/test/com/google/gwt/emultest/java/lang/CompilerConstantStringTest.java index 82aa4a8f02..80763a0b4f 100644 --- a/user/test/com/google/gwt/emultest/java/lang/CompilerConstantStringTest.java +++ b/user/test/com/google/gwt/emultest/java/lang/CompilerConstantStringTest.java @@ -16,7 +16,6 @@ package com.google.gwt.emultest.java.lang; import com.google.gwt.junit.client.GWTTestCase; -import com.google.gwt.testing.TestUtils; import java.util.Locale;