未验证 提交 e9c74955 编写于 作者: L Lukas Taegert-Atkinson 提交者: GitHub

Tracks side effects of thenables (#4115)

上级 c0ba3ee1
import { NormalizedTreeshakingOptions } from '../../rollup/types';
import { NO_ARGS } from '../CallOptions';
import { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import ArrowFunctionExpression from './ArrowFunctionExpression';
import * as NodeType from './NodeType';
......@@ -9,7 +11,20 @@ export default class AwaitExpression extends NodeBase {
type!: NodeType.tAwaitExpression;
hasEffects(context: HasEffectsContext): boolean {
return !context.ignore.returnAwaitYield || this.argument.hasEffects(context);
const { propertyReadSideEffects } = this.context.options
.treeshake as NormalizedTreeshakingOptions;
return (
!context.ignore.returnAwaitYield ||
this.argument.hasEffects(context) ||
this.argument.hasEffectsWhenCalledAtPath(
['then'],
{ args: NO_ARGS, thisParam: null, withNew: false },
context
) ||
(propertyReadSideEffects &&
(propertyReadSideEffects === 'always' ||
this.argument.hasEffectsWhenAccessedAtPath(['then'], context)))
);
}
include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void {
......
import { CallOptions } from '../../CallOptions';
import { CallOptions, NO_ARGS } from '../../CallOptions';
import { BROKEN_FLOW_NONE, HasEffectsContext, InclusionContext } from '../../ExecutionContext';
import { EVENT_CALLED, NodeEvent } from '../../NodeEvents';
import FunctionScope from '../../scopes/FunctionScope';
......@@ -56,7 +56,7 @@ export default class FunctionNode extends NodeBase {
}
getReturnExpressionWhenCalledAtPath(path: ObjectPath): ExpressionEntity {
return path.length === 0 ? this.scope.getReturnExpression() : UNKNOWN_EXPRESSION;
return !this.async && path.length === 0 ? this.scope.getReturnExpression() : UNKNOWN_EXPRESSION;
}
hasEffects(): boolean {
......@@ -81,6 +81,18 @@ export default class FunctionNode extends NodeBase {
context: HasEffectsContext
): boolean {
if (path.length > 0) return true;
if (
this.async &&
this.scope
.getReturnExpression()
.hasEffectsWhenCalledAtPath(
['then'],
{ args: NO_ARGS, thisParam: null, withNew: false },
context
)
) {
return true;
}
for (const param of this.params) {
if (param.hasEffects(context)) return true;
}
......
......@@ -191,10 +191,11 @@ const knownGlobals: GlobalDescription = {
Promise: {
__proto__: null,
[ValueProperties]: IMPURE,
all: PF,
all: O,
prototype: O,
race: PF,
resolve: PF
race: O,
reject: O,
resolve: O
},
propertyIsEnumerable: O,
Proxy: O,
......
const path = require('path');
module.exports = {
description: 'tracks effects when awaiting thenables'
};
(async function () {
return {
then() {
console.log(1);
}
};
})();
(async function () {
await {
then: function () {
console.log(2);
}
};
return { then() {} };
})();
(async function () {
await {
get then() {
console.log(3);
return () => {};
}
};
return { then() {} };
})();
(async function () {
await {
get then() {
return () => console.log(4);
}
};
return { then() {} };
})();
(async function () {
await await {
then(resolve) {
resolve({
then() {
console.log(5);
}
});
}
};
return { then() {} };
})();
async function asyncIdentity(x) {
return x;
}
asyncIdentity({}); // no side effects - may be dropped
const promise = asyncIdentity(6);
promise.then(x => console.log(x));
asyncIdentity({
then(success, fail) {
success(console.log(7));
}
});
asyncIdentity({
then(resolve) {
resolve({
then() {
console.log(8);
}
});
}
});
(async function () {
return {
then() {
console.log(1);
}
};
})();
// removed
(async function () {
return { then() {} };
})();
(async function () {
await {
then: function () {
console.log(2);
}
};
return { then() {} };
})();
// removed
(async function () {
await {
then: function () {}
};
return { then() {} };
})();
(async function () {
await {
get then() {
console.log(3);
return () => {};
}
};
return { then() {} };
})();
(async function () {
await {
get then() {
return () => console.log(4);
}
};
return { then() {} };
})();
// removed
(async function () {
await {
get then() {
return () => {};
}
};
return { then() {} };
})();
(async function () {
await await {
then(resolve) {
resolve({
then() {
console.log(5);
}
});
}
};
return { then() {} };
})();
async function asyncIdentity(x) {
return x;
}
asyncIdentity({}); // no side effects - may be dropped
const promise = asyncIdentity(6);
promise.then(x => console.log(x));
asyncIdentity({
then(success, fail) {
success(console.log(7));
}
});
asyncIdentity({
then(resolve) {
resolve({
then() {
console.log(8);
}
});
}
});
module.exports = {
description: 'do not remove promise creations',
options: { output: { name: 'bundle' } }
description: 'do not remove promise creations'
};
Promise.resolve({
then() {
console.log(1);
}
});
Promise.resolve({
get then() {
return () => console.log(2);
}
});
Promise.reject('should be kept for uncaught rejections');
Promise.all([
{
then() {
console.log(3);
}
},
null
]);
Promise.all([
null,
{
get then() {
return () => console.log(4);
}
}
]);
Promise.race([
{
then() {
console.log(5);
}
},
null
]);
Promise.race([
null,
{
get then() {
return () => console.log(6);
}
}
]);
define(['exports'], function (exports) { 'use strict';
new Promise( () => {
console.log( 'fire & forget' );
} );
const p2 = new Promise( () => {
console.info( 'forget me as well' );
} );
const p3 = new Promise( () => {
console.info( 'and me too' );
} );
Promise.reject('should be kept for uncaught rejections');
const allExported = Promise.all([p2, p3]);
exports.allExported = allExported;
Object.defineProperty(exports, '__esModule', { value: true });
});
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
new Promise( () => {
console.log( 'fire & forget' );
} );
const p2 = new Promise( () => {
console.info( 'forget me as well' );
} );
const p3 = new Promise( () => {
console.info( 'and me too' );
} );
Promise.reject('should be kept for uncaught rejections');
const allExported = Promise.all([p2, p3]);
exports.allExported = allExported;
new Promise( () => {
console.log( 'fire & forget' );
} );
const p2 = new Promise( () => {
console.info( 'forget me as well' );
} );
const p3 = new Promise( () => {
console.info( 'and me too' );
} );
Promise.reject('should be kept for uncaught rejections');
const allExported = Promise.all([p2, p3]);
export { allExported };
var bundle = (function (exports) {
'use strict';
new Promise( () => {
console.log( 'fire & forget' );
} );
const p2 = new Promise( () => {
console.info( 'forget me as well' );
} );
const p3 = new Promise( () => {
console.info( 'and me too' );
} );
Promise.reject('should be kept for uncaught rejections');
const allExported = Promise.all([p2, p3]);
exports.allExported = allExported;
Object.defineProperty(exports, '__esModule', { value: true });
return exports;
}({}));
System.register('bundle', [], function (exports) {
'use strict';
return {
execute: function () {
new Promise( () => {
console.log( 'fire & forget' );
} );
const p2 = new Promise( () => {
console.info( 'forget me as well' );
} );
const p3 = new Promise( () => {
console.info( 'and me too' );
} );
Promise.reject('should be kept for uncaught rejections');
const allExported = exports('allExported', Promise.all([p2, p3]));
}
};
});
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.bundle = {}));
}(this, (function (exports) { 'use strict';
new Promise( () => {
console.log( 'fire & forget' );
} );
const p2 = new Promise( () => {
console.info( 'forget me as well' );
} );
const p3 = new Promise( () => {
console.info( 'and me too' );
} );
Promise.reject('should be kept for uncaught rejections');
const allExported = Promise.all([p2, p3]);
exports.allExported = allExported;
Object.defineProperty(exports, '__esModule', { value: true });
})));
const p1 = new Promise( () => {
console.log( 'fire & forget' );
} );
Promise.resolve({
then() {
console.log(1);
}
});
const p2 = new Promise( () => {
console.info( 'forget me as well' );
} );
Promise.resolve({
get then() {
return () => console.log(2);
}
});
const p3 = new Promise( () => {
console.info( 'and me too' );
} );
Promise.reject('should be kept for uncaught rejections');
const p4 = Promise.resolve('no side effect');
const p5 = Promise.reject('should be kept for uncaught rejections');
Promise.all([
{
then() {
console.log(3);
}
},
null
]);
const all = Promise.all([p2, p3]);
export const allExported = Promise.all([p2, p3]);
const race = Promise.race([p2, p3]);
Promise.all([
null,
{
get then() {
return () => console.log(4);
}
}
]);
Promise.race([
{
then() {
console.log(5);
}
},
null
]);
Promise.race([
null,
{
get then() {
return () => console.log(6);
}
}
]);
......@@ -11,9 +11,3 @@ async function hasEffects2 () {
hasEffects2();
async function isRemoved () {
await globalThis.unknown;
}
isRemoved();
......@@ -2662,7 +2662,8 @@ var es6Shim = {exports: {}};
var getsThenSynchronously = supportsDescriptors && (function () {
var count = 0;
// eslint-disable-next-line getter-return
Object.defineProperty({}, 'then', { get: function () { count += 1; } });
var thenable = Object.defineProperty({}, 'then', { get: function () { count += 1; } });
Promise.resolve(thenable);
return count === 1;
}());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册