Skip to content

Commit

Permalink
feat(Js): Capturing files read by readFile/readFileSync and open
Browse files Browse the repository at this point in the history
  • Loading branch information
beneboy committed Sep 2, 2019
1 parent e6799f6 commit aaf3fa4
Showing 1 changed file with 78 additions and 0 deletions.
78 changes: 78 additions & 0 deletions ts/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ function parseItem(
item.assigns = parseResult.assigns
item.alters = parseResult.alters
item.uses = parseResult.uses
item.reads = parseResult.reads

if (parseResult.errors.length > 0) {
if (item.errors === undefined) {
Expand Down Expand Up @@ -180,8 +181,12 @@ class CodeChunkParseResult {

public uses: string[] = []

public reads: string[] = []

public errors: CodeError[] = []

public functionDeclarationDepth = 0

private seenIdentifiers: string[] = []

private possibleVariables: string[] = []
Expand All @@ -199,6 +204,8 @@ class CodeChunkParseResult {
}

public addImports(n: string): void {
if (this.functionDeclarationDepth > 0) return

if (!this.imports.includes(n)) this.imports.push(n)
}

Expand All @@ -207,25 +214,39 @@ class CodeChunkParseResult {
* variable or a function. So the identifier goes in here and if it wasn't computed to be a function declaration
* after parsing the rest of the code chunk, then set is as a variable.
*/
if (this.functionDeclarationDepth > 0) return

if (!this.possibleVariables.includes(n)) this.possibleVariables.push(n)
}

public addDeclares(d: Variable | Function): void {
if (this.functionDeclarationDepth > 0) return

if (!this.isNameSet(d.name)) this.declares.push(d)
}

public addAssigns(n: string): void {
if (this.functionDeclarationDepth > 0) return

if (!this.isNameSet(n)) this.assigns.push(n)
}

public addAlters(n: string): void {
if (this.functionDeclarationDepth > 0) return

if (!this.isNameSet(n)) this.alters.push(n)
}

public addUses(n: string): void {
if (this.functionDeclarationDepth > 0) return

if (!this.isNameSet(n)) this.uses.push(n)
}

public addReads(f: string): void {
if (!this.reads.includes(f)) this.reads.push(f)
}

public finalize(): void {
this.possibleVariables.forEach(n => {
if (!this.seenIdentifiers.includes(n)) this.declares.push(variable(n))
Expand Down Expand Up @@ -263,6 +284,12 @@ function parseFunctionDeclaration(
}

result.addDeclares(function_(name, { parameters }))

if (fn.body !== undefined && fn.body !== null) {
++result.functionDeclarationDepth
parseStatement(result, fn.body)
--result.functionDeclarationDepth
}
}

function parseVariableDeclaration(
Expand Down Expand Up @@ -314,10 +341,61 @@ function parseBinaryExpression(
else parseStatement(result, statement.right)
}

function isFileRead(statement: CallExpression): [boolean, string | null] {
let calleeName: string
if (statement.callee.name !== undefined) {
calleeName = statement.callee.name
} else if (
statement.callee.type === 'MemberExpression' &&
statement.callee.property.type === 'Identifier'
) {
calleeName = statement.callee.property.name
} else return [false, null]
return [
calleeName === 'readFileSync' ||
calleeName === 'readFile' ||
calleeName === 'open',
calleeName
]
}

function parseFileReadExpression(
result: CodeChunkParseResult,
statement: CallExpression,
calleeName: string
): void {
if (statement.arguments.length === 0) return

const arg = statement.arguments[0]

if (arg.type !== 'Literal' || typeof arg.value !== 'string') {
return
}
// file name is a string so we can determine the path without executing

if (calleeName === 'open' && statement.arguments.length >= 2) {
// if for some reason open() only has one arg assume it is a read. this is invalid code though.
const modeObj = statement.arguments[1]
if (modeObj.type !== 'Literal' || typeof modeObj.value !== 'string') return

const mode = modeObj.value

if (mode.indexOf('r') === -1 && mode.indexOf('+') === -1) return
}

result.addReads(arg.value)
}

function parseCallExpression(
result: CodeChunkParseResult,
statement: CallExpression
): void {
const [isRead, calleeName] = isFileRead(statement)

if (isRead) {
parseFileReadExpression(result, statement, calleeName as string) // typecasting as calleeName is always non-null if isRead is true
}

statement.arguments.forEach(arg => {
if (arg.type === 'Identifier') result.addUses(arg.name)
else parseStatement(result, arg)
Expand Down

0 comments on commit aaf3fa4

Please sign in to comment.