babel / babel/minify

Constant with regular expression literal with global modifier incorrectly replace by literal

Open
#1,026 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.