Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid sequence expression simplification #4110

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/ast/nodes/CallExpression.ts
Expand Up @@ -247,11 +247,10 @@ export default class CallExpression extends NodeBase implements DeoptimizableEnt
{ renderedParentType, renderedSurroundingElement }: NodeRenderOptions = BLANK
): void {
const surroundingELement = renderedParentType || renderedSurroundingElement;
this.callee.render(
code,
options,
surroundingELement ? { renderedSurroundingElement: surroundingELement } : BLANK
);
this.callee.render(code, options, {
isCalleeOfRenderedParent: true,
renderedSurroundingElement: surroundingELement
});
if (this.arguments.length > 0) {
if (this.arguments[this.arguments.length - 1].included) {
for (const arg of this.arguments) {
Expand Down
18 changes: 11 additions & 7 deletions src/ast/nodes/ConditionalExpression.ts
Expand Up @@ -19,7 +19,6 @@ import {
SHARED_RECURSION_TRACKER,
UNKNOWN_PATH
} from '../utils/PathTracker';
import CallExpression from './CallExpression';
import * as NodeType from './NodeType';
import SpreadElement from './SpreadElement';
import { ExpressionEntity, LiteralValueOrUnknown, UnknownValue } from './shared/Expression';
Expand Down Expand Up @@ -180,7 +179,12 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz
render(
code: MagicString,
options: RenderOptions,
{ isCalleeOfRenderedParent, renderedParentType, preventASI }: NodeRenderOptions = BLANK
{
isCalleeOfRenderedParent,
preventASI,
renderedParentType,
renderedSurroundingElement
}: NodeRenderOptions = BLANK
): void {
const usedBranch = this.getUsedBranch();
if (!this.test.included) {
Expand All @@ -200,15 +204,15 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz
}
removeAnnotations(this, code);
usedBranch!.render(code, options, {
isCalleeOfRenderedParent: renderedParentType
? isCalleeOfRenderedParent
: (this.parent as CallExpression).callee === this,
isCalleeOfRenderedParent,
preventASI: true,
renderedParentType: renderedParentType || this.parent.type
...(renderedSurroundingElement
? { renderedSurroundingElement }
: { renderedParentType: renderedParentType || this.parent.type })
});
} else {
this.test.render(code, options, {
renderedSurroundingElement: renderedParentType
renderedSurroundingElement: renderedParentType || renderedSurroundingElement
});
this.consequent.render(code, options);
this.alternate.render(code, options);
Expand Down
9 changes: 4 additions & 5 deletions src/ast/nodes/LogicalExpression.ts
Expand Up @@ -19,7 +19,6 @@ import {
SHARED_RECURSION_TRACKER,
UNKNOWN_PATH
} from '../utils/PathTracker';
import CallExpression from './CallExpression';
import * as NodeType from './NodeType';
import { ExpressionEntity, LiteralValueOrUnknown, UnknownValue } from './shared/Expression';
import { MultiExpression } from './shared/MultiExpression';
Expand Down Expand Up @@ -190,11 +189,11 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable
}
removeAnnotations(this, code);
this.getUsedBranch()!.render(code, options, {
isCalleeOfRenderedParent: renderedParentType
? isCalleeOfRenderedParent
: (this.parent as CallExpression).callee === this,
isCalleeOfRenderedParent,
preventASI,
renderedParentType: renderedParentType || this.parent.type
...(renderedSurroundingElement
? { renderedSurroundingElement }
: { renderedParentType: renderedParentType || this.parent.type })
});
} else {
this.left.render(code, options, {
Expand Down
6 changes: 2 additions & 4 deletions src/ast/nodes/MemberExpression.ts
Expand Up @@ -281,17 +281,15 @@ export default class MemberExpression extends NodeBase implements DeoptimizableE
renderedSurroundingElement
}: NodeRenderOptions = BLANK
): void {
const isCalleeOfDifferentParent =
renderedParentType === NodeType.CallExpression && isCalleeOfRenderedParent;
if (this.variable || this.replacement) {
let replacement = this.variable ? this.variable.getName() : this.replacement;
if (isCalleeOfDifferentParent) replacement = '0, ' + replacement;
if (renderedParentType && isCalleeOfRenderedParent) replacement = '0, ' + replacement;
code.overwrite(this.start, this.end, replacement!, {
contentOnly: true,
storeName: true
});
} else {
if (isCalleeOfDifferentParent) {
if (renderedParentType && isCalleeOfRenderedParent) {
code.appendRight(this.start, '0, ');
}
const surroundingElement = renderedParentType || renderedSurroundingElement;
Expand Down
43 changes: 22 additions & 21 deletions src/ast/nodes/SequenceExpression.ts
Expand Up @@ -10,9 +10,8 @@ import { treeshakeNode } from '../../utils/treeshakeNode';
import { CallOptions } from '../CallOptions';
import { DeoptimizableEntity } from '../DeoptimizableEntity';
import { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import { EVENT_CALLED, NodeEvent } from '../NodeEvents';
import { NodeEvent } from '../NodeEvents';
import { ObjectPath, PathTracker } from '../utils/PathTracker';
import CallExpression from './CallExpression';
import ExpressionStatement from './ExpressionStatement';
import * as NodeType from './NodeType';
import { ExpressionEntity, LiteralValueOrUnknown } from './shared/Expression';
Expand All @@ -23,7 +22,7 @@ export default class SequenceExpression extends NodeBase {
type!: NodeType.tSequenceExpression;

deoptimizePath(path: ObjectPath): void {
if (path.length > 0) this.expressions[this.expressions.length - 1].deoptimizePath(path);
this.expressions[this.expressions.length - 1].deoptimizePath(path);
}

deoptimizeThisOnEventAtPath(
Expand All @@ -32,14 +31,12 @@ export default class SequenceExpression extends NodeBase {
thisParameter: ExpressionEntity,
recursionTracker: PathTracker
): void {
if (event === EVENT_CALLED || path.length > 0) {
this.expressions[this.expressions.length - 1].deoptimizeThisOnEventAtPath(
event,
path,
thisParameter,
recursionTracker
);
}
this.expressions[this.expressions.length - 1].deoptimizeThisOnEventAtPath(
event,
path,
thisParameter,
recursionTracker
);
}

getLiteralValueAtPath(
Expand Down Expand Up @@ -69,9 +66,9 @@ export default class SequenceExpression extends NodeBase {
}

hasEffectsWhenAssignedAtPath(path: ObjectPath, context: HasEffectsContext): boolean {
return (
path.length === 0 ||
this.expressions[this.expressions.length - 1].hasEffectsWhenAssignedAtPath(path, context)
return this.expressions[this.expressions.length - 1].hasEffectsWhenAssignedAtPath(
path,
context
);
}

Expand Down Expand Up @@ -123,13 +120,17 @@ export default class SequenceExpression extends NodeBase {
if (includedNodes === 1 && preventASI) {
removeLineBreaks(code, start, node.start);
}
if (node === lastNode && includedNodes === 1) {
node.render(code, options, {
isCalleeOfRenderedParent: renderedParentType
? isCalleeOfRenderedParent
: (this.parent as CallExpression).callee === this,
renderedParentType: renderedParentType || this.parent.type
});
if (includedNodes === 1) {
if (node === lastNode) {
node.render(code, options, {
isCalleeOfRenderedParent,
renderedParentType: renderedParentType || this.parent.type
});
} else {
node.render(code, options, {
renderedSurroundingElement: renderedParentType || this.parent.type
});
}
} else {
node.render(code, options);
}
Expand Down
18 changes: 7 additions & 11 deletions src/ast/nodes/TaggedTemplateExpression.ts
@@ -1,3 +1,5 @@
import MagicString from 'magic-string';
import { RenderOptions } from '../../utils/renderHelpers';
import { CallOptions, NO_ARGS } from '../CallOptions';
import { HasEffectsContext } from '../ExecutionContext';
import { EMPTY_PATH } from '../utils/PathTracker';
Expand Down Expand Up @@ -28,17 +30,6 @@ export default class TaggedTemplateExpression extends NodeBase {
this.start
);
}

if (name === 'eval') {
this.context.warn(
{
code: 'EVAL',
message: `Use of eval is strongly discouraged, as it poses security risks and may cause issues with minification`,
url: 'https://rollupjs.org/guide/en/#avoiding-eval'
},
this.start
);
}
}
}

Expand All @@ -56,4 +47,9 @@ export default class TaggedTemplateExpression extends NodeBase {
withNew: false
};
}

render(code: MagicString, options: RenderOptions): void {
this.tag.render(code, options, { isCalleeOfRenderedParent: true });
this.quasi.render(code, options);
}
}
4 changes: 2 additions & 2 deletions test/form/samples/conditional-expression/_expected.js
Expand Up @@ -10,8 +10,8 @@ unknownValue ? 1 : foo();

// known side-effect
foo() ;
(function () {this.x = 1;} )();
((function () {this.x = 1;}) )();
(() => () => console.log( 'effect' ) )()();
foo();
(function () {this.x = 1;})();
((function () {this.x = 1;}))();
(() => () => console.log( 'effect' ))()();
@@ -0,0 +1,3 @@
module.exports = {
description: 'uses corrrect "this" for tagged template expressions in simplified sequences'
};
13 changes: 13 additions & 0 deletions test/function/samples/sequence-expressions-template-tag/main.js
@@ -0,0 +1,13 @@
let o = {
f() {
assert.ok(this !== o);
}
};
(1, o.f)();
(1, o.f)``;

(true && o.f)();
(true && o.f)``;

(true ? o.f : false)();
(true ? o.f : false)``;
@@ -0,0 +1,4 @@
module.exports = {
description:
'correctly simplify sequence expressions if the new leading element would require parentheses'
};
31 changes: 31 additions & 0 deletions test/function/samples/simplify-sequence-expressions/main.js
@@ -0,0 +1,31 @@
let value = 0;

1, function(){ value = 1 }();
assert.strictEqual(value, 1);

1, function(){ value = 2 }(), 2;
assert.strictEqual(value, 2);

1, {foo: value = 3 };
assert.strictEqual(value, 3);

1, {foo: value = 4 }, 2;
assert.strictEqual(value, 4);

1, true ? function(){ value = 5 }() : false;
assert.strictEqual(value, 5);

1, true ? function(){ value = 6 }() : false, 2;
assert.strictEqual(value, 6);

1, function(){ value = 7; return true }() ? true : false;
assert.strictEqual(value, 7);

1, function(){ value = 8; return true }() ? true : false, 2;
assert.strictEqual(value, 8);

1, false || function(){ value = 9 }();
assert.strictEqual(value, 9);

1, false || function(){ value = 10 }(), 2;
assert.strictEqual(value, 10);