Skip to content

Commit

Permalink
workaround for various object literal mutation bugs (#3266)
Browse files Browse the repository at this point in the history
* Workaround for various object literal mutation bugs.

Some necessary minor regressions related to previously
unsafe object literal optimizations.

fixes #2345
fixes #2473
fixes #3027
fixes #3093
fixes #3192
fixes #3264

* Remove special logic to handle deoptimizing properties, always deopt `this` by default
  • Loading branch information
kzc authored and lukastaegert committed Dec 2, 2019
1 parent c753a7f commit 3e0aa46
Show file tree
Hide file tree
Showing 31 changed files with 256 additions and 324 deletions.
4 changes: 4 additions & 0 deletions src/ast/nodes/CallExpression.ts
Expand Up @@ -17,6 +17,7 @@ import {
} from '../utils/PathTracker';
import { LiteralValueOrUnknown, UNKNOWN_EXPRESSION, UnknownValue } from '../values';
import Identifier from './Identifier';
import MemberExpression from './MemberExpression';
import * as NodeType from './NodeType';
import { ExpressionEntity } from './shared/Expression';
import { ExpressionNode, INCLUDE_PARAMETERS, IncludeChildren, NodeBase } from './shared/Node';
Expand Down Expand Up @@ -66,6 +67,9 @@ export default class CallExpression extends NodeBase implements DeoptimizableEnt
this
);
}
if (this.callee instanceof MemberExpression && !this.callee.variable) {
this.callee.object.deoptimizePath(UNKNOWN_PATH);
}
for (const argument of this.arguments) {
// This will make sure all properties of parameters behave as "unknown"
argument.deoptimizePath(UNKNOWN_PATH);
Expand Down
4 changes: 4 additions & 0 deletions test/form/samples/arrow-function-return-values/_expected.js
Expand Up @@ -6,5 +6,9 @@ retained1()();
(() => {
return () => console.log( 'effect' );
})()();

(() => ({ foo: () => {} }))().foo();
(() => ({ foo: () => console.log( 'effect' ) }))().foo();

(() => ({ foo: () => ({ bar: () => ({ baz: () => {} }) }) }))().foo().bar().baz();
(() => ({ foo: () => ({ bar: () => console.log( 'effect' ) }) }))().foo().bar();
@@ -1 +1,11 @@
const object = {};
const propertyIsEnumerable1 = object.propertyIsEnumerable( 'toString' );
const propertyIsEnumerable2 = {}.propertyIsEnumerable( 'toString' );
const propertyIsEnumerable3 = {}.propertyIsEnumerable( 'toString' ).valueOf();

const _hasOwnProperty = {}.hasOwnProperty( 'toString' ).valueOf();
const _isPrototypeOf = {}.isPrototypeOf( {} ).valueOf();
const _propertyIsEnumerable = {}.propertyIsEnumerable( 'toString' ).valueOf();
const _toLocaleString = {}.toLocaleString().trim();
const _toString = {}.toString().trim();
const _valueOf = {}.valueOf();
11 changes: 11 additions & 0 deletions test/form/samples/conditional-expression-paths/_expected.js
@@ -1,11 +1,22 @@
var unknownValue = globalThis.unknown();
var foo = { x: () => {}, y: {} };
var bar = { x: () => {}, y: {} };
var baz = { x: () => console.log('effect') };

// unknown branch without side-effects
var a1 = (unknownValue ? foo : bar).y.z;
var b1 = (unknownValue ? foo : bar).x();

// unknown branch with side-effect
var a2 = (unknownValue ? foo : baz).y.z;
var b2 = (unknownValue ? foo : baz).x();

// known branch without side-effects
var a3 = ( foo ).y.z;
var b3 = ( foo).y.z;
var c3 = ( foo ).x();
var d3 = ( foo).x();

// known branch with side-effect
var a4 = ( baz ).y.z;
var b4 = ( baz).y.z;
Expand Down
6 changes: 6 additions & 0 deletions test/form/samples/function-body-return-values/_expected.js
@@ -1,3 +1,9 @@
function removed3 () {
return { x: () => {} };
}

removed3().x();

function retained1 () {
return () => console.log( 'effect' );
}
Expand Down
@@ -1,5 +1,3 @@
'use strict';

({
get foo () {
console.log( 'effect' );
Expand All @@ -11,6 +9,12 @@
return {};
}
}).foo.bar.baz;

({
get foo () {
return () => {};
}
}).foo();
({
get foo () {
console.log( 'effect' );
Expand All @@ -22,6 +26,12 @@
return () => console.log( 'effect' );
}
}).foo();

({
get foo () {
return () => () => {};
}
}).foo()();
({
get foo () {
console.log( 'effect' );
Expand Down
37 changes: 0 additions & 37 deletions test/form/samples/getter-return-values/_expected/amd.js

This file was deleted.

33 changes: 0 additions & 33 deletions test/form/samples/getter-return-values/_expected/es.js

This file was deleted.

38 changes: 0 additions & 38 deletions test/form/samples/getter-return-values/_expected/iife.js

This file was deleted.

42 changes: 0 additions & 42 deletions test/form/samples/getter-return-values/_expected/system.js

This file was deleted.

40 changes: 0 additions & 40 deletions test/form/samples/getter-return-values/_expected/umd.js

This file was deleted.

@@ -1,3 +1,12 @@
const x = {
[globalThis.unknown]() {
console.log('effect');
},
a() {}
};

x.a();

const y = {
a() {},
[globalThis.unknown]() {
Expand All @@ -12,3 +21,4 @@ const z = {
};

z.a();
z.hasOwnProperty('a'); // removed
@@ -1,3 +1,10 @@
const x = {
[globalThis.unknown]: () => () => console.log('effect'),
a: () => () => {}
};

x.a()();

const y = {
a: () => () => {},
[globalThis.unknown]: () => () => console.log('effect')
Expand All @@ -12,6 +19,8 @@ const z = {
z.a()();

const v = {};

v.toString().charCodeAt(0); // removed
v.toString().doesNotExist(0); // retained

const w = {
Expand Down
20 changes: 20 additions & 0 deletions test/form/samples/object-literal-property-overwrites/_expected.js
@@ -1,3 +1,23 @@
const removed1 = {
foo: () => {},
foo: () => {},
['f' + 'oo']: () => {}
};
removed1.foo();

const removed2 = {
foo: () => console.log( 'effect' ),
foo: () => {}
};
removed2.foo();

const removed3 = {
['fo' + 'o']: function () {this.x = 1;},
['f' + 'oo']: () => console.log( 'effect' ),
foo: () => {}
};
removed3.foo();

const retained1 = {
foo: () => {},
foo: function () {this.x = 1;}
Expand Down
@@ -1,7 +1,16 @@
function getReturnExpressionBeforeInit() {
{
const bar = {
[foo.value()]: true
};
if (bar.baz) {
console.log('retained');
}
}

const foo = {
get value() {
return () => 'baz';
}
};

getReturnExpressionBeforeInit();

0 comments on commit 3e0aa46

Please sign in to comment.