Skip to content

Commit

Permalink
Do not track side-effect-free array methods as side effects (#4103)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed May 27, 2021
1 parent 9a50fbb commit df3b2a5
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 28 deletions.
61 changes: 35 additions & 26 deletions src/ast/nodes/shared/ArrayPrototype.ts
Expand Up @@ -16,46 +16,46 @@ const NEW_ARRAY_PROPERTIES: ObjectProperty[] = [
{ key: 'length', kind: 'init', property: UNKNOWN_LITERAL_NUMBER }
];

const METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_BOOLEAN: [ExpressionEntity] = [
const METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_BOOLEAN: [ExpressionEntity] = [
new Method({
callsArgs: [0],
mutatesSelfAsArray: true,
mutatesSelfAsArray: 'deopt-only',
returns: null,
returnsPrimitive: UNKNOWN_LITERAL_BOOLEAN
})
];

const METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_NUMBER: [ExpressionEntity] = [
const METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_NUMBER: [ExpressionEntity] = [
new Method({
callsArgs: [0],
mutatesSelfAsArray: true,
mutatesSelfAsArray: 'deopt-only',
returns: null,
returnsPrimitive: UNKNOWN_LITERAL_NUMBER
})
];

export const METHOD_RETURNS_NEW_ARRAY: [ExpressionEntity] = [
const METHOD_MUTATES_SELF_RETURNS_NEW_ARRAY: [ExpressionEntity] = [
new Method({
callsArgs: null,
mutatesSelfAsArray: false,
mutatesSelfAsArray: true,
returns: () => new ObjectEntity(NEW_ARRAY_PROPERTIES, ARRAY_PROTOTYPE),
returnsPrimitive: null
})
];

const METHOD_MUTATES_SELF_RETURNS_NEW_ARRAY: [ExpressionEntity] = [
const METHOD_DEOPTS_SELF_RETURNS_NEW_ARRAY: [ExpressionEntity] = [
new Method({
callsArgs: null,
mutatesSelfAsArray: true,
mutatesSelfAsArray: 'deopt-only',
returns: () => new ObjectEntity(NEW_ARRAY_PROPERTIES, ARRAY_PROTOTYPE),
returnsPrimitive: null
})
];

const METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_NEW_ARRAY: [ExpressionEntity] = [
const METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_NEW_ARRAY: [ExpressionEntity] = [
new Method({
callsArgs: [0],
mutatesSelfAsArray: true,
mutatesSelfAsArray: 'deopt-only',
returns: () => new ObjectEntity(NEW_ARRAY_PROPERTIES, ARRAY_PROTOTYPE),
returnsPrimitive: null
})
Expand All @@ -79,10 +79,19 @@ const METHOD_MUTATES_SELF_RETURNS_UNKNOWN: [ExpressionEntity] = [
})
];

const METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_UNKNOWN: [ExpressionEntity] = [
const METHOD_DEOPTS_SELF_RETURNS_UNKNOWN: [ExpressionEntity] = [
new Method({
callsArgs: null,
mutatesSelfAsArray: 'deopt-only',
returns: null,
returnsPrimitive: UNKNOWN_EXPRESSION
})
];

const METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_UNKNOWN: [ExpressionEntity] = [
new Method({
callsArgs: [0],
mutatesSelfAsArray: true,
mutatesSelfAsArray: 'deopt-only',
returns: null,
returnsPrimitive: UNKNOWN_EXPRESSION
})
Expand Down Expand Up @@ -110,34 +119,34 @@ export const ARRAY_PROTOTYPE = new ObjectEntity(
({
__proto__: null,
// We assume that accessors have effects as we do not track the accessed value afterwards
at: METHOD_MUTATES_SELF_RETURNS_UNKNOWN,
concat: METHOD_RETURNS_NEW_ARRAY,
at: METHOD_DEOPTS_SELF_RETURNS_UNKNOWN,
concat: METHOD_DEOPTS_SELF_RETURNS_NEW_ARRAY,
copyWithin: METHOD_MUTATES_SELF_RETURNS_SELF,
entries: METHOD_MUTATES_SELF_RETURNS_NEW_ARRAY,
every: METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_BOOLEAN,
entries: METHOD_DEOPTS_SELF_RETURNS_NEW_ARRAY,
every: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_BOOLEAN,
fill: METHOD_MUTATES_SELF_RETURNS_SELF,
filter: METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_NEW_ARRAY,
find: METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_UNKNOWN,
findIndex: METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_NUMBER,
forEach: METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_UNKNOWN,
filter: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_NEW_ARRAY,
find: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_UNKNOWN,
findIndex: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_NUMBER,
forEach: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_UNKNOWN,
includes: METHOD_RETURNS_BOOLEAN,
indexOf: METHOD_RETURNS_NUMBER,
join: METHOD_RETURNS_STRING,
keys: METHOD_RETURNS_UNKNOWN,
lastIndexOf: METHOD_RETURNS_NUMBER,
map: METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_NEW_ARRAY,
map: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_NEW_ARRAY,
pop: METHOD_MUTATES_SELF_RETURNS_UNKNOWN,
push: METHOD_MUTATES_SELF_RETURNS_NUMBER,
reduce: METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_UNKNOWN,
reduceRight: METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_UNKNOWN,
reduce: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_UNKNOWN,
reduceRight: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_UNKNOWN,
reverse: METHOD_MUTATES_SELF_RETURNS_SELF,
shift: METHOD_MUTATES_SELF_RETURNS_UNKNOWN,
slice: METHOD_MUTATES_SELF_RETURNS_NEW_ARRAY,
some: METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_BOOLEAN,
slice: METHOD_DEOPTS_SELF_RETURNS_NEW_ARRAY,
some: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_BOOLEAN,
sort: METHOD_CALLS_ARG_MUTATES_SELF_RETURNS_SELF,
splice: METHOD_MUTATES_SELF_RETURNS_NEW_ARRAY,
unshift: METHOD_MUTATES_SELF_RETURNS_NUMBER,
values: METHOD_MUTATES_SELF_RETURNS_UNKNOWN
values: METHOD_DEOPTS_SELF_RETURNS_UNKNOWN
} as unknown) as PropertyMap,
OBJECT_PROTOTYPE
);
4 changes: 2 additions & 2 deletions src/ast/nodes/shared/MethodTypes.ts
Expand Up @@ -13,7 +13,7 @@ import { ExpressionNode } from './Node';

type MethodDescription = {
callsArgs: number[] | null;
mutatesSelfAsArray: boolean;
mutatesSelfAsArray: boolean | 'deopt-only';
} & (
| {
returns: 'self' | (() => ExpressionEntity);
Expand Down Expand Up @@ -70,7 +70,7 @@ export class Method extends ExpressionEntity {
): boolean {
if (
path.length > 0 ||
(this.description.mutatesSelfAsArray &&
(this.description.mutatesSelfAsArray === true &&
callOptions.thisParam?.hasEffectsWhenAssignedAtPath(UNKNOWN_INTEGER_PATH, context))
) {
return true;
Expand Down
@@ -0,0 +1,3 @@
module.exports = {
description: 'Tree-shakes side-effect-free array functions if only their return value is unused'
};
@@ -0,0 +1,41 @@
const foo1 = [1, 2, 3];
console.log(foo1[0]);

const foo2 = [1, 2, 3];
console.log(foo2[0]);

const foo3 = [1, 2, 3];
console.log(foo3[0]);

const foo4 = [1, 2, 3];
console.log(foo4[0]);

const foo5 = [1, 2, 3];
console.log(foo5[0]);

const foo6 = [1, 2, 3];
console.log(foo6[0]);

const foo7 = [1, 2, 3];
console.log(foo7[0]);

const foo8 = [1, 2, 3];
console.log(foo8[0]);

const foo9 = [1, 2, 3];
console.log(foo9[0]);

const foo10 = [1, 2, 3];
console.log(foo10[0]);

const foo11 = [1, 2, 3];
console.log(foo11[0]);

const foo12 = [1, 2, 3];
console.log(foo12[0]);

const foo13 = [1, 2, 3];
console.log(foo13[0]);

const foo14 = [1, 2, 3];
console.log(foo14[0]);
@@ -0,0 +1,55 @@
const foo1 = [1, 2, 3];
foo1.at(0);
console.log(foo1[0]);

const foo2 = [1, 2, 3];
foo2.concat([0]);
console.log(foo2[0]);

const foo3 = [1, 2, 3];
foo3.entries();
console.log(foo3[0]);

const foo4 = [1, 2, 3];
foo4.every(v => v);
console.log(foo4[0]);

const foo5 = [1, 2, 3];
foo5.filter(v => v % 1 === 0);
console.log(foo5[0]);

const foo6 = [1, 2, 3];
foo6.find(v => v);
console.log(foo6[0]);

const foo7 = [1, 2, 3];
foo7.findIndex(v => v);
console.log(foo7[0]);

const foo8 = [1, 2, 3];
foo8.forEach(() => {});
console.log(foo8[0]);

const foo9 = [1, 2, 3];
foo9.map(v => v);
console.log(foo9[0]);

const foo10 = [1, 2, 3];
foo10.reduce((a, v) => a + v, 0);
console.log(foo10[0]);

const foo11 = [1, 2, 3];
foo11.reduceRight((a, v) => a + v, 0);
console.log(foo11[0]);

const foo12 = [1, 2, 3];
foo12.slice(1);
console.log(foo12[0]);

const foo13 = [1, 2, 3];
foo13.some(v => v);
console.log(foo13[0]);

const foo14 = [1, 2, 3];
foo14.values();
console.log(foo14[0]);

0 comments on commit df3b2a5

Please sign in to comment.