Skip to content

Commit

Permalink
Make sure property mutations on function arguments are not lost
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed May 15, 2018
1 parent 4bb67cb commit 0f9b30e
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 3 deletions.
10 changes: 7 additions & 3 deletions src/ast/nodes/CallExpression.ts
@@ -1,11 +1,11 @@
import CallOptions from '../CallOptions';
import ExecutionPathOptions from '../ExecutionPathOptions';
import SpreadElement from './SpreadElement';
import { isIdentifier } from './Identifier';
import Identifier from './Identifier';
import { ForEachReturnExpressionCallback, SomeReturnExpressionCallback } from './shared/Expression';
import * as NodeType from './NodeType';
import { ExpressionNode, NodeBase } from './shared/Node';
import { ObjectPath } from '../values';
import { ObjectPath, UNKNOWN_KEY } from '../values';

export default class CallExpression extends NodeBase {
type: NodeType.tCallExpression;
Expand All @@ -16,7 +16,7 @@ export default class CallExpression extends NodeBase {

bind() {
super.bind();
if (isIdentifier(this.callee)) {
if (this.callee instanceof Identifier) {
const variable = this.scope.findVariable(this.callee.name);

if (variable.isNamespace) {
Expand All @@ -40,6 +40,10 @@ export default class CallExpression extends NodeBase {
);
}
}
for (const argument of this.arguments) {
// This will make sure all properties of parameters behave as "unknown"
argument.reassignPath([UNKNOWN_KEY], ExecutionPathOptions.create());
}
}

forEachReturnExpressionWhenCalledAtPath(
Expand Down
1 change: 1 addition & 0 deletions src/ast/variables/LocalVariable.ts
Expand Up @@ -97,6 +97,7 @@ export default class LocalVariable extends Variable {
callOptions: CallOptions,
options: ExecutionPathOptions
) {
// The "included && path.length > 0" check is needed for mutating methods on arrays
if (path.length > MAX_PATH_DEPTH || (this.included && path.length > 0)) return true;
return (
this.reassignments.isPathReassigned(path) ||
Expand Down
@@ -0,0 +1,8 @@
var assert = require('assert');

module.exports = {
description: 'Associates value mutations across return values',
exports: function(exports) {
assert.equal(exports.bar, 'present');
}
};
14 changes: 14 additions & 0 deletions test/function/samples/associate-function-return-values-5/main.js
@@ -0,0 +1,14 @@
const foo = {value: false};
const exported = {};

function getFoo() {
return foo;
}

getFoo().value = true;

if (foo.value) {
exported.bar = 'present'
}

export default exported;
3 changes: 3 additions & 0 deletions test/function/samples/reassign-parameter-property/_config.js
@@ -0,0 +1,3 @@
module.exports = {
description: 'track reassignments of properties of function parameters'
};
17 changes: 17 additions & 0 deletions test/function/samples/reassign-parameter-property/main.js
@@ -0,0 +1,17 @@
const obj = {
flag: false
};

function reassign(x) {
x.flag = true;
}

reassign(obj);

let hasBeenReassigned = false;

if (obj.flag) {
hasBeenReassigned = true;
}

assert.ok(hasBeenReassigned);

0 comments on commit 0f9b30e

Please sign in to comment.