Summary: | String.prototype.match and replace do not clear global regexp lastIndex per ES5.1 15.5.4.10 | ||||||
---|---|---|---|---|---|---|---|
Product: | WebKit | Reporter: | John-David Dalton <john.david.dalton> | ||||
Component: | JavaScriptCore | Assignee: | Gavin Barraclough <barraclough> | ||||
Status: | RESOLVED FIXED | ||||||
Severity: | Normal | CC: | ap, barraclough, benjamin, joepeck | ||||
Priority: | P2 | ||||||
Version: | 528+ (Nightly build) | ||||||
Hardware: | PC | ||||||
OS: | OS X 10.5 | ||||||
Attachments: |
|
Description
John-David Dalton
2009-07-01 10:54:19 PDT
We definitely have a bug here, Gah, premature commit! Anyway, we definitely have a bug here, but I think the spec may actually call for slightly different expected results. The spec requires that match first clears lastIndex, then calls exec until it returns null (per 15.5.4.10 8.f.ii). When exec returns null, it also clears lastIndex (per 15.10.6.2 step 9.a.1). So after a match, the value of lastIndex must be 0. Our implementation of match ignores and does not update lastIndex. Ignoring its initial value is correct, failing to reset it isn't. var s = '0x2x4x6x8'; var p = /x/g; p.lastIndex = 3; s.match(p); print(p.lastIndex); // prints 3, should be 0 after a match. Similarly, after a replace without a callback replacer function, the lastIndex should definitely be reset. var s = '0x2x4x6x8'; var p = /x/g; p.lastIndex = 3; s.replace(p,'y'); print(p.lastIndex); // prints 3, should be 0 after a replace. The spec does not enumerate the stages of performing a replace as clearly as it does for some library functions, but it contains the following two directions: firstly: "If searchValue.global is true, then search string for all matches of the regular expression searchValue. Do the search in the same manner as in String.prototype.match, including the update of searchValue.lastIndex." secondly: "If replaceValue is a function, then for each matched substring, call the function" This clearly implies that these actions should take place in the following order: 1) Find all match results 2) Set the value of lastIndex to 0 (implicitly performed by 15.10.6.2 step 9.a.1 called via the action of the last iteration of 15.5.4.10 8.f.i). 3) Call all replacers. As such, the replace method should set lastIndex to 0 once, before any replacers are called, and then not change the value. var s = '0x1x2x3x4'; var p = /x/g; p.lastIndex = 5; s.replace(p, function() { return ++p.lastIndex; }) // should be "011223344" Created attachment 130933 [details]
Fix
|