From 704845c0c0d38019b905f82fbe8739d2edf1d669 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 26 Jul 2023 15:13:49 +0900 Subject: [PATCH] tree shake Object.entries/values --- src/ast/nodes/shared/knownGlobals.ts | 33 ++++++++++++++++--- .../_config.js | 0 .../_expected.js | 13 ++++++++ .../main.js | 27 +++++++++++++++ .../main.js | 13 -------- 5 files changed, 69 insertions(+), 17 deletions(-) rename test/{function => form}/samples/tree-shake-object-with-sideeffctful-getter/_config.js (100%) create mode 100644 test/form/samples/tree-shake-object-with-sideeffctful-getter/_expected.js create mode 100644 test/form/samples/tree-shake-object-with-sideeffctful-getter/main.js delete mode 100644 test/function/samples/tree-shake-object-with-sideeffctful-getter/main.js diff --git a/src/ast/nodes/shared/knownGlobals.ts b/src/ast/nodes/shared/knownGlobals.ts index 5b97c60426b..13e8294a635 100644 --- a/src/ast/nodes/shared/knownGlobals.ts +++ b/src/ast/nodes/shared/knownGlobals.ts @@ -3,7 +3,10 @@ import { doNothing } from '../../../utils/doNothing'; import type { HasEffectsContext } from '../../ExecutionContext'; import type { NodeInteractionCalled } from '../../NodeInteractions'; -import { NODE_INTERACTION_UNKNOWN_ASSIGNMENT } from '../../NodeInteractions'; +import { + NODE_INTERACTION_UNKNOWN_ACCESS, + NODE_INTERACTION_UNKNOWN_ASSIGNMENT +} from '../../NodeInteractions'; import type { ObjectPath } from '../../utils/PathTracker'; import { SymbolToStringTag, @@ -12,7 +15,7 @@ import { } from '../../utils/PathTracker'; import ArrayExpression from '../ArrayExpression'; import type { LiteralValueOrUnknown } from './Expression'; -import { UnknownTruthyValue } from './Expression'; +import { ExpressionEntity, UnknownTruthyValue } from './Expression'; const ValueProperties = Symbol('Value Properties'); @@ -52,6 +55,22 @@ const PURE_WITH_ARRAY: ValueDescription = { } }; +const GETTER_ACCESS: ValueDescription = { + deoptimizeArgumentsOnCall: doNothing, + getLiteralValue: getTruthyLiteralValue, + hasEffectsWhenCalled({ args }, context) { + const [_thisArgument, firstArgument] = args; + return ( + !(firstArgument instanceof ExpressionEntity) || + firstArgument.hasEffectsOnInteractionAtPath( + UNKNOWN_PATH, + NODE_INTERACTION_UNKNOWN_ACCESS, + context + ) + ); + } +}; + // We use shortened variables to reduce file size here /* OBJECT */ const O: GlobalDescription = { @@ -65,6 +84,12 @@ const PF: GlobalDescription = { [ValueProperties]: PURE }; +/* PURE FUNCTION IF FIRST ARG DOES NOT CONTAIN A GETTER */ +const PF_NO_GETTER: GlobalDescription = { + __proto__: null, + [ValueProperties]: GETTER_ACCESS +}; + /* FUNCTION THAT MUTATES FIRST ARG WITHOUT TRIGGERING ACCESSORS */ const MUTATES_ARG_WITHOUT_ACCESSOR: GlobalDescription = { __proto__: null, @@ -253,8 +278,8 @@ const knownGlobals: GlobalDescription = { isSealed: PF, keys: PF, fromEntries: O, - entries: O, - values: O, + entries: PF_NO_GETTER, + values: PF_NO_GETTER, prototype: O }, parseFloat: PF, diff --git a/test/function/samples/tree-shake-object-with-sideeffctful-getter/_config.js b/test/form/samples/tree-shake-object-with-sideeffctful-getter/_config.js similarity index 100% rename from test/function/samples/tree-shake-object-with-sideeffctful-getter/_config.js rename to test/form/samples/tree-shake-object-with-sideeffctful-getter/_config.js diff --git a/test/form/samples/tree-shake-object-with-sideeffctful-getter/_expected.js b/test/form/samples/tree-shake-object-with-sideeffctful-getter/_expected.js new file mode 100644 index 00000000000..a21f171ed64 --- /dev/null +++ b/test/form/samples/tree-shake-object-with-sideeffctful-getter/_expected.js @@ -0,0 +1,13 @@ +let effects = 0; + +const objWithEffect = { + get foo() { + effects++; + return 'foo'; + } +}; + +Object.values(objWithEffect); +Object.entries(objWithEffect); + +assert.equal(effects, 2); diff --git a/test/form/samples/tree-shake-object-with-sideeffctful-getter/main.js b/test/form/samples/tree-shake-object-with-sideeffctful-getter/main.js new file mode 100644 index 00000000000..d5a774cf8b1 --- /dev/null +++ b/test/form/samples/tree-shake-object-with-sideeffctful-getter/main.js @@ -0,0 +1,27 @@ +let effects = 0; + +const objWithEffect = { + get foo() { + effects++; + return 'foo'; + } +}; + +const objWithoutEffect1 = { + get foo() { + return 'foo'; + } +}; +const objWithoutEffect2 = { + foo: 'foo' +}; + +Object.values(objWithEffect); +Object.entries(objWithEffect); + +Object.values(objWithoutEffect1); +Object.entries(objWithoutEffect1); +Object.values(objWithoutEffect2); +Object.entries(objWithoutEffect2); + +assert.equal(effects, 2); diff --git a/test/function/samples/tree-shake-object-with-sideeffctful-getter/main.js b/test/function/samples/tree-shake-object-with-sideeffctful-getter/main.js deleted file mode 100644 index 43c8c4499cc..00000000000 --- a/test/function/samples/tree-shake-object-with-sideeffctful-getter/main.js +++ /dev/null @@ -1,13 +0,0 @@ -let effects = 0; - -const obj = { - get foo() { - effects++; - return "foo"; - } -}; - -Object.values(obj); -Object.entries(obj); - -assert.equal(effects, 2);