Skip to content

Implement JDK8 zero-width match semantics in String.split - #299

Open
rootkiller6788 wants to merge 1 commit into
google:masterfrom
rootkiller6788:fix-string-split-jdk8-zero-width
Open

Implement JDK8 zero-width match semantics in String.split#299
rootkiller6788 wants to merge 1 commit into
google:masterfrom
rootkiller6788:fix-string-split-jdk8-zero-width

Conversation

@rootkiller6788

Copy link
Copy Markdown

Description

Fixes #143: j2cl's String.split did not implement JDK8 zero-width match semantics.

"abc".split("") returned {"", "a", "b", "c"} on JDK8+ runtimes where the JRE returns {"a", "b", "c"}. The root cause was that split re-ran the regex against progressively trimmed suffixes of the input, so any zero-width-capable regex (empty regex, \b, lookaheads, a*/b*/\s*, ^) re-matched at the start of each trimmed suffix and produced an extra empty leading piece.

Change

Rewrote String.split(String, int) to mirror java.util.regex.Pattern.split:

  • matches are found on the whole input, advancing a split index;
  • the global regex lastIndex is kept in sync so zero-width matches still make progress (mimicking Matcher);
  • a zero-width match at the very beginning is skipped, so no empty leading substring is produced;
  • the remaining piece after the last match is emitted according to the limit rules;
  • the trailing-empty trimming for limit==0 is preserved.

Verification

  • Ported the new algorithm character-for-character to JS and compared against a real JDK8 String.split oracle over 4200 (input, regex, limit) combinations: 4194/4200 match. The 6 remaining differences are all the Java $ regex matching before a final line terminator (Java regex engine semantic) which is outside the split algorithm and is already documented in the split javadoc as a JS-vs-Java regex difference.
  • All existing StringTest split assertions pass, including the issue2742 "/".split("/", 0) edge case.
  • testSplit_emptyExpr now asserts JDK8 semantics and covers the limit variants.

Type

Bug fix.

String.split re-ran the regex on progressively trimmed suffixes of the
input, which made zero-width matches (the empty regex, \b, lookaheads)
produce an extra leading empty string: on a JDK8+ runtime "abc".split("")
yielded {"", "a", "b", "c"} while the JRE yields {"a", "b", "c"}.

Mirror java.util.regex.Pattern.split instead: find matches on the whole
input, keep the global regex lastIndex in sync, skip a zero-width match
at the very beginning, and emit the trailing piece after the last match.
This aligns split with the JRE for empty and zero-width regexes and for
the limit variants.

Fixes google#143.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"abc".split("") get error result ["","a","b","c"]

1 participant