Skip to content

Commit

Permalink
Fix: ReferenceTracker
Browse files Browse the repository at this point in the history
- `iterateGlobalReferences` should not iterate modified globals.
- `iterateCjsReferences` should iterate global object members.
  (e.g. `global.require`)
  • Loading branch information
mysticatea committed Jun 28, 2018
1 parent 2fc445d commit 9c8e1bd
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 23 deletions.
45 changes: 22 additions & 23 deletions src/reference-tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ export const CALL = Symbol("call")
export const CONSTRUCT = Symbol("construct")
export const ESM = Symbol("esm")

const requireCall = { require: { [CALL]: true } }

/**
* Check whether a given variable is modified or not.
* @param {Variable} variable The variable to check.
* @returns {boolean} `true` if the variable is modified.
*/
function isModifiedGlobal(variable) {
return (
variable == null ||
variable.defs.length !== 0 ||
variable.references.some(r => r.isWrite())
)
}

/**
* The reference tracker.
*/
Expand Down Expand Up @@ -46,7 +61,7 @@ export class ReferenceTracker {
const path = [key]
const variable = this.globalScope.set.get(key)

if (variable == null || variable.defs.length !== 0) {
if (isModifiedGlobal(variable)) {
continue
}

Expand All @@ -62,7 +77,7 @@ export class ReferenceTracker {
const path = []
const variable = this.globalScope.set.get(key)

if (variable == null || variable.defs.length !== 0) {
if (isModifiedGlobal(variable)) {
continue
}

Expand All @@ -81,40 +96,24 @@ export class ReferenceTracker {
* @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
*/
*iterateCjsReferences(traceMap) {
const variable = this.globalScope.set.get("require")

if (variable == null || variable.defs.length !== 0) {
return
}

for (const reference of variable.references) {
const reqNode = reference.identifier
const callNode = reqNode.parent

if (
!reference.isRead() ||
callNode.type !== "CallExpression" ||
callNode.callee !== reqNode
) {
continue
}
const key = getStringIfConstant(callNode.arguments[0])

for (const { node } of this.iterateGlobalReferences(requireCall)) {
const key = getStringIfConstant(node.arguments[0])
if (key == null || !has(traceMap, key)) {
continue
}

const nextTraceMap = traceMap[key]
const path = [key]

if (nextTraceMap[READ]) {
yield {
node: callNode,
node,
path,
type: READ,
info: nextTraceMap[READ],
}
}
yield* this._iteratePropertyReferences(callNode, path, nextTraceMap)
yield* this._iteratePropertyReferences(node, path, nextTraceMap)
}
}

Expand Down
18 changes: 18 additions & 0 deletions test/reference-tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,24 @@ describe("The 'ReferenceTracker' class:", () => {
},
],
},
{
description:
"should not iterate the references of a given global variable if it's modified.",
code: [
"Object = {}",
"Object.a",
"Object.b()",
"new Object.c()",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [],
},
]) {
it(description, () => {
const linter = new eslint.Linter()
Expand Down

0 comments on commit 9c8e1bd

Please sign in to comment.