From bd3522b33f18001372638263aeb704b76edbf48c Mon Sep 17 00:00:00 2001 From: TrickyPi <530257315@qq.com> Date: Tue, 13 Dec 2022 13:22:53 +0800 Subject: [PATCH] fix: check whether RegExp have the global or sticky flags set (#4742) * fix: check whether RegExp have the global or sticky flags set * fix: check in hasEffectsOnInteractionAtPath * chore: tweak * chore: tweak variable names --- src/ast/nodes/Literal.ts | 7 +++++ src/ast/values.ts | 5 ++++ .../_config.js | 3 +++ .../_expected.js | 23 ++++++++++++++++ .../main.js | 26 +++++++++++++++++++ .../builtin-prototypes/literal/main.js | 8 ++++-- 6 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_config.js create mode 100644 test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_expected.js create mode 100644 test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/main.js diff --git a/src/ast/nodes/Literal.ts b/src/ast/nodes/Literal.ts index 5259ada5ef2..97bab48e2bf 100644 --- a/src/ast/nodes/Literal.ts +++ b/src/ast/nodes/Literal.ts @@ -70,6 +70,13 @@ export default class Literal extends Node return true; } case INTERACTION_CALLED: { + if ( + this.included && + this.value instanceof RegExp && + (this.value.global || this.value.sticky) + ) { + return true; + } return ( path.length !== 1 || hasMemberEffectWhenCalled(this.members, path[0], interaction, context) diff --git a/src/ast/values.ts b/src/ast/values.ts index 4d91b067f99..3a479a29136 100644 --- a/src/ast/values.ts +++ b/src/ast/values.ts @@ -200,6 +200,11 @@ const literalNumberMembers: MemberDescriptions = assembleMemberDescriptions( objectMembers ); +/** + * RegExp are stateful when they have the global or sticky flags set. + * But if we actually don't use them, the side effect does not matter. + * the check logic in `hasEffectsOnInteractionAtPath`. + */ const literalRegExpMembers: MemberDescriptions = assembleMemberDescriptions( { exec: returnsUnknown, diff --git a/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_config.js b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_config.js new file mode 100644 index 00000000000..6001c39d55d --- /dev/null +++ b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'check tree-shake on RegExp if it is used' +}; diff --git a/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_expected.js b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_expected.js new file mode 100644 index 00000000000..72a94a069d1 --- /dev/null +++ b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_expected.js @@ -0,0 +1,23 @@ +const commonRegexp_1 = /1/; +const commonRegexp_2 = /1/; + +assert.strictEqual(commonRegexp_1.test('1'), true); +assert.ok(commonRegexp_2.exec('1')); + +const globalRegexp_1 = /1/g; +const globalRegexp_2 = /1/g; + +globalRegexp_1.test('1'); +globalRegexp_2.exec('1'); + +assert.strictEqual(globalRegexp_1.test('1'), null); +assert.strictEqual(globalRegexp_2.exec('1'), null); + +const stickyRegexp_1 = /1/y; +const stickyRegexp_2 = /1/y; + +stickyRegexp_1.test('1'); +stickyRegexp_2.exec('1'); + +assert.strictEqual(stickyRegexp_1.test('1'), null); +assert.strictEqual(stickyRegexp_2.exec('1'), null); diff --git a/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/main.js b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/main.js new file mode 100644 index 00000000000..4124af7072d --- /dev/null +++ b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/main.js @@ -0,0 +1,26 @@ +const commonRegexp_1 = /1/; +const commonRegexp_2 = /1/; + +commonRegexp_1.test('1'); +commonRegexp_2.exec('1'); + +assert.strictEqual(commonRegexp_1.test('1'), true); +assert.ok(commonRegexp_2.exec('1')); + +const globalRegexp_1 = /1/g; +const globalRegexp_2 = /1/g; + +globalRegexp_1.test('1'); +globalRegexp_2.exec('1'); + +assert.strictEqual(globalRegexp_1.test('1'), null); +assert.strictEqual(globalRegexp_2.exec('1'), null); + +const stickyRegexp_1 = /1/y; +const stickyRegexp_2 = /1/y; + +stickyRegexp_1.test('1'); +stickyRegexp_2.exec('1'); + +assert.strictEqual(stickyRegexp_1.test('1'), null); +assert.strictEqual(stickyRegexp_2.exec('1'), null); diff --git a/test/form/samples/builtin-prototypes/literal/main.js b/test/form/samples/builtin-prototypes/literal/main.js index e079316f4d7..cb99661a2ed 100644 --- a/test/form/samples/builtin-prototypes/literal/main.js +++ b/test/form/samples/builtin-prototypes/literal/main.js @@ -49,8 +49,12 @@ const _numberIsPrototypeOf = (1).isPrototypeOf(1).valueOf(); const _numberPropertyIsEnumerable = (1).propertyIsEnumerable('toString').valueOf(); // RegExp prototype -/1/.test(2); -/1/.exec(1); +/1/.test('1'); +/1/.exec('1'); +/1/y.test('1'); +/1/y.exec('1'); +/1/g.test('1'); +/1/g.exec('1'); // string prototype 'ab'.valueOf();