From caf9f705584e710bd43730c626762074829b9e72 Mon Sep 17 00:00:00 2001 From: TrickyPi <530257315@qq.com> Date: Sat, 10 Dec 2022 16:37:46 +0800 Subject: [PATCH] fix: check in hasEffectsOnInteractionAtPath --- src/ast/nodes/Literal.ts | 7 +++++ src/ast/values.ts | 12 ++++++--- .../_config.js | 3 +++ .../_expected.js | 23 ++++++++++++++++ .../main.js | 26 +++++++++++++++++++ .../builtin-prototypes/literal/_expected.js | 3 --- .../builtin-prototypes/literal/main.js | 9 ++++--- 7 files changed, 72 insertions(+), 11 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 10111ce5f76..3a479a29136 100644 --- a/src/ast/values.ts +++ b/src/ast/values.ts @@ -200,8 +200,12 @@ const literalNumberMembers: MemberDescriptions = assembleMemberDescriptions( objectMembers ); -// RegExp are stateful when they have the global or sticky flags set. -const literalParticularRegExpMembers: MemberDescriptions = assembleMemberDescriptions( +/** + * 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, test: returnsBoolean @@ -267,8 +271,8 @@ export const literalStringMembers: MemberDescriptions = assembleMemberDescriptio export function getLiteralMembersForValue( value: T ): MemberDescriptions { - if (value instanceof RegExp && !value.global && !value.sticky) { - return literalParticularRegExpMembers; + if (value instanceof RegExp) { + return literalRegExpMembers; } switch (typeof value) { case 'boolean': { 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..f96e8206f7f --- /dev/null +++ b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/_expected.js @@ -0,0 +1,23 @@ +const normalRegexp_1 = /1/; +const normalRegexp_2 = /1/; + +assert.strictEqual(normalRegexp_1.test(1), true); +assert.ok(normalRegexp_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..63c05396ee5 --- /dev/null +++ b/test/form/samples/builtin-prototypes/check-tree-shake-on-regexp-if-it-is-used/main.js @@ -0,0 +1,26 @@ +const normalRegexp_1 = /1/; +const normalRegexp_2 = /1/; + +normalRegexp_1.test(1); +normalRegexp_2.exec(1); + +assert.strictEqual(normalRegexp_1.test(1), true); +assert.ok(normalRegexp_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/_expected.js b/test/form/samples/builtin-prototypes/literal/_expected.js index 5af535bd0fc..fe60d5932c9 100644 --- a/test/form/samples/builtin-prototypes/literal/_expected.js +++ b/test/form/samples/builtin-prototypes/literal/_expected.js @@ -17,9 +17,6 @@ true.valueOf().unknown.unknown().unknown; 'ab'.charAt(1).unknown.unknown(); 'ab'.charAt(1).unknown.unknown().unknown; -/1/g.exec(1); -/1/y.exec(1); - null.unknown; 'ab'.replace('a', () => console.log(1) || 'b'); 'ab'.replaceAll('a', () => console.log(1) || 'b'); diff --git a/test/form/samples/builtin-prototypes/literal/main.js b/test/form/samples/builtin-prototypes/literal/main.js index c5bd781d2e4..4b7f11c092e 100644 --- a/test/form/samples/builtin-prototypes/literal/main.js +++ b/test/form/samples/builtin-prototypes/literal/main.js @@ -17,9 +17,6 @@ true.valueOf().unknown.unknown().unknown; 'ab'.charAt(1).unknown.unknown(); 'ab'.charAt(1).unknown.unknown().unknown; -/1/g.exec(1); -/1/y.exec(1); - null.unknown; // boolean prototype @@ -52,8 +49,12 @@ const _numberIsPrototypeOf = (1).isPrototypeOf(1).valueOf(); const _numberPropertyIsEnumerable = (1).propertyIsEnumerable('toString').valueOf(); // RegExp prototype -/1/.test(2); +/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();