Constant with regular expression literal with global modifier incorrectly replace by literal
- Dominant language
- JavaScript
- Stars
- 4.4k
- Forks
- 217
- PR merge metrics
- No merged PRs in 30d
Description
**Describe the bug**
The code contains a constant whose value is a regular expression with the `g` (i.e. global) modifier. Minify replaces the use of this constant with the literal regular expression resulting in an infinite loop.
**To Reproduce**
Minimal code to reproduce the bug
```js
/**
* Incorrect minimization of regular expression
*
*/
function test(data) {
var re = /a/g;
let match;
let result = [];
while ((match = re.exec(data))) {
result.push(match);
}
return result;
}
console.log(test('abababab'));
```
**Actual Output**
```js
function test(a){let b,c=[];for(;b=/a/g.exec(a);)c.push(b);return c}console.log(test("abababab"));
```
The minifier generates exactly the same code if the regular expression literal is replaced by `new Regexp('a', 'g')`.
**Expected Output**
```js
function test(a){let b=/a/g,c,d=[];for(;c=b.exec(a);)c.push(c);return d}console.log(test("abababab"));
```
**Configuration**
How are you using babel-minify?
```
npx babel --no-comments js/script.js
```
babel-minify version: `0.5.0`
babel version : 7.17.6 (@babel/core 7.17.9)
babel-minify-config: none
babelrc:
```json5
{
"presets": [
[
"minify",
{
"evaluate": false
}
]
]
}
```
**Possible solution**
The while loop must use the same RegExp object through each iteration. At a minimum, the babel minimizer should recognize that the global modifier is present and make sure that the same regular expression object is used.
It turns out that the minifier does the right thing when it cannot determine the modifiers in the case where `modifiers` is a function parameter, e.g.
```
function f(modifiers) {
const re = new RegExp('a', modifiers)
```
**Additional context**
In this case the minified code results in a infinite loop at least when run in latest versions of Chrome (Version 102.0.5001.0 (Official Build) canary (64-bit)) and with nodejs (v16.14.0).
It seems that the JavaScript engines must be recreating the regular expression object through each loop iteration or at least resetting the index.
I tried each of the minimizer options that looked relevant one at a time to see if any fixed this problem.
This fix needs to happen in any situation that uses the regular expression
```js
const a = /a/g;
let b = 'ababa';
if (a.exec(b)) { ....}
if(a.exec(b)){ ... }
```
Contributor guide
Research direction
Start with the provided npx babel command and the minimal JavaScript reproduction, then trace the minifier's handling of constant regular-expression literals with the global modifier. Add a regression test covering repeated exec calls and confirm the generated code preserves one RegExp object and terminates instead of looping indefinitely.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100