From 3fdea496182029272f6783c0bc763889fb541fa2 Mon Sep 17 00:00:00 2001 From: HoLyVieR Date: Fri, 24 May 2019 17:15:37 -0400 Subject: [PATCH 1/2] Refractoring to have matcher in separate file. Added some heuristic to ignore invocation that will never lead to a match. --- classes/object-function-call.js | 4 +- constant.js | 5 + index.js | 934 +------------------------------- matchers/angular-matcher.js | 125 +++++ matchers/jquery-matcher.js | 97 ++++ matchers/native-matcher.js | 39 ++ utils.js | 795 +++++++++++++++++++++++++++ 7 files changed, 1089 insertions(+), 910 deletions(-) create mode 100644 constant.js create mode 100644 matchers/angular-matcher.js create mode 100644 matchers/jquery-matcher.js create mode 100644 matchers/native-matcher.js create mode 100644 utils.js diff --git a/classes/object-function-call.js b/classes/object-function-call.js index 20468df..018fcf3 100644 --- a/classes/object-function-call.js +++ b/classes/object-function-call.js @@ -1,5 +1,7 @@ /// class ObjectFunctionCall +var MemberExpression = require("./member-expression"); + function ObjectFunctionCall(members, args) { this.members = new MemberExpression(members); this.arguments = args; @@ -33,4 +35,4 @@ ObjectFunctionCall.prototype.equals = function (val) { return good; }; -module.exports = ObjectFunctionCall; \ No newline at end of file +module.exports = ObjectFunctionCall; diff --git a/constant.js b/constant.js new file mode 100644 index 0000000..7512422 --- /dev/null +++ b/constant.js @@ -0,0 +1,5 @@ +module.exports = { + CONST_SEPARATOR_ID : "&&&", + PLACEHOLDER_VARIABLE : "@{VAR}" +}; + diff --git a/index.js b/index.js index 1eee14d..ed8a24e 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,12 @@ var acorn = require("acorn"); +var utils = require("./utils"); /** * Constant */ -var CONST_SEPARATOR_ID = "&&&"; -var PLACEHOLDER_VARIABLE = "@{VAR}" +var CONST_SEPARATOR_ID = require("./constant").CONST_SEPARATOR_ID; +var PLACEHOLDER_VARIABLE = require("./constant").PLACEHOLDER_VARIABLE; /** * Classes to represent the stucture used in the utility. @@ -26,741 +27,15 @@ var ObjectStructure = require("./classes/object-structure"); var Reference = require("./classes/reference"); var Unknown = require("./classes/unknown"); -function debug(message) { - if (typeof console === "undefined") { - if (typeof message !== "string") { - message = JSON.stringify(message); - } - System.println(message + ""); - } else { - console.log(message); - } -} - -/** - * Analysis function - */ - -/** - * Returns the list of all the variable declaration in the scope of the - * "tree" object. - * - * @param tree - AST object - * @param collectVar - If we collect variable declared with "var". - * @param collectLet - If we collect variable declared with "let". - */ -function collectVar(tree, _collectVar, collectLet) { - var variables = []; - - for (let i=0; i 2) { - let subsetMemberExpression = [objStructure.properties.get(keyValue.value)].concat(flattenMemberExpression.slice(2)) - return resolveMemberExpression(subsetMemberExpression); - } else { - return objStructure.properties.get(keyValue.value); - } - } - } - - return new MemberExpression(flattenMemberExpression); -} - -/** - * Returns the symbolic value of the AST object "tree". - * - * @param tree - AST object - * @param context - Context object - * @param resolveIdentifier - Whether or not identifier value should be ressolved. - */ -function toSymbolic(tree, context, resolveIdentfier) { - if (typeof resolveIdentfier === "undefined") { - resolveIdentfier = true; - } - - switch (tree.type) { - case "Literal": - let constant = new Constant(tree.value); - constant.position = { start : tree.start, end : tree.end }; - return constant; - - case "Identifier": - if (resolveIdentfier) { - let result = resolveReference(tree.name, context); - if (!result.position) { - result.position = { start : tree.start, end : tree.end }; - } - return result; - } else { - if (context.scope.has(tree.name)) { - let referenceIdentifier = context.scope.get(tree.name); - let ref = new Reference(referenceIdentifier); - ref.position = { start : tree.start, end : tree.end }; - return ref; - } else { - let tmpReference = new Reference("G" + CONST_SEPARATOR_ID + tree.name); - context.scope.set(tree.name, tmpReference.name); - tmpReference.position = { start : tree.start, end : tree.end }; - return tmpReference; - } - } - - case "BinaryExpression": - if (tree.operator === "+") { - let concat = new Concatenation(toSymbolic(tree.left, context), toSymbolic(tree.right, context)); - concat.position = { start : tree.start, end : tree.end }; - return concat; - } - break; - - case "MemberExpression": - let memberExpression = flattenMemberExpression(tree, context); - - if (resolveIdentfier) { - let res = resolveMemberExpression(memberExpression, context); - if (!res.position) { - res.position = { start : tree.start, end : tree.end }; - } - return res; - } - - let memberExpr = new MemberExpression(memberExpression); - memberExpr.position = { start : tree.start, end : tree.end }; - return memberExpr; - - case "NewExpression": - let args = []; - for (let i=0; i 2) { - let subsetMemberExpression = [mainObj.properties.get(prop.value)].concat(left.parts.slice(2)); - memberExpressionAssignment(subsetMemberExpression, right, assignations); - } else { - mainObj.properties.set(prop.value, right); - } - } else { - let found = false; - - assignations.forEach(function (value, key, map) { - if (found) return; - - if (key instanceof MemberExpression && key.equals(left)) { - assignations.delete(key); - assignations.set(left, right); - found = true; - } - }); - - if (!found) { - assignations.set(left, right); - } - } -} - -/** - * Merge the content of two AnalysisResult. - */ -function mergeResult(result1, result2) { - result2.assignations.forEach(function (value, key, map) { - result1.assignations.set(key, value); - }); -} - -/** - * Perform the main analysis to retrieve : - * - The list of all the function call. - * - The list of all the variable and their symbolic value. - */ -function analysis(tree, result, scope, scopeName, partialScope, useNewScope) { - // Default value - START - if (typeof result === "undefined") { - result = new AnalysisResult(); - } - - if (typeof scope === "undefined") { - scope = new Map(); - } - - if (typeof scopeName === "undefined") { - scopeName = "G" + CONST_SEPARATOR_ID; - } - - if (typeof partialScope === "undefined") { - partialScope = false; - } - - if (typeof useNewScope === "undefined") { - useNewScope = true; - } - // Default value - END - - if (tree.body && !Array.isArray(tree.body)) { - tree.body = [tree.body]; - } - - if (tree.left || tree.right) { - tree.body = []; - tree.left && tree.body.push(tree.left); - tree.right && tree.body.push(tree.right); - } - - switch (tree.type) { - case "ExpressionStatement": - tree = { body : [tree] }; - break; - - case "SequenceExpression": - tree.body = tree.expressions; - break; - - case "CallExpression": - case "AssignmentExpression": - tree = { body : [tree] }; - break; - } - - if (!tree.body) { - return new Map(); - } - - if (useNewScope) { - var newScope = new Map(scope); - var listVar = collectVar(tree, !partialScope, true); - - for (let i=0; i 2) { - invocation.fnct = new MemberExpression(leftParts.slice(0, -1)); - } else { - invocation.fnct = leftParts[0]; - } - - invocation.arguments = []; - - for (let i=0; i 0) { - let functionReference = functionArgs[0].fnct; - let output = []; - - for (let i=0; i 2) { + let subsetMemberExpression = [objStructure.properties.get(keyValue.value)].concat(flattenMemberExpression.slice(2)) + return resolveMemberExpression(subsetMemberExpression); + } else { + return objStructure.properties.get(keyValue.value); + } + } + } + + return new MemberExpression(flattenMemberExpression); +} + +exportFnct.resolveMemberExpression = resolveMemberExpression; + +/** + * Returns the symbolic value of the AST object "tree". + * + * @param tree - AST object + * @param context - Context object + * @param resolveIdentifier - Whether or not identifier value should be ressolved. + */ +function toSymbolic(tree, context, resolveIdentfier) { + if (typeof resolveIdentfier === "undefined") { + resolveIdentfier = true; + } + + if (!context) { + throw new Exception("context is null"); + } + + switch (tree.type) { + case "Literal": + let constant = new Constant(tree.value); + constant.position = { start : tree.start, end : tree.end }; + return constant; + + case "Identifier": + if (resolveIdentfier) { + let result = resolveReference(tree.name, context); + if (!result.position) { + result.position = { start : tree.start, end : tree.end }; + } + return result; + } else { + if (context.scope.has(tree.name)) { + let referenceIdentifier = context.scope.get(tree.name); + let ref = new Reference(referenceIdentifier); + ref.position = { start : tree.start, end : tree.end }; + return ref; + } else { + let tmpReference = new Reference("G" + CONST_SEPARATOR_ID + tree.name); + context.scope.set(tree.name, tmpReference.name); + tmpReference.position = { start : tree.start, end : tree.end }; + return tmpReference; + } + } + + case "BinaryExpression": + if (tree.operator === "+") { + let concat = new Concatenation(toSymbolic(tree.left, context), toSymbolic(tree.right, context)); + concat.position = { start : tree.start, end : tree.end }; + return concat; + } + break; + + case "MemberExpression": + let memberExpression = flattenMemberExpression(tree, context); + + if (resolveIdentfier) { + let res = resolveMemberExpression(memberExpression, context); + if (!res.position) { + res.position = { start : tree.start, end : tree.end }; + } + return res; + } + + let memberExpr = new MemberExpression(memberExpression); + memberExpr.position = { start : tree.start, end : tree.end }; + return memberExpr; + + case "NewExpression": + let args = []; + for (let i=0; i 2) { + let subsetMemberExpression = [mainObj.properties.get(prop.value)].concat(left.parts.slice(2)); + memberExpressionAssignment(subsetMemberExpression, right, assignations); + } else { + mainObj.properties.set(prop.value, right); + } + } else { + let found = false; + + assignations.forEach(function (value, key, map) { + if (found) return; + + if (key instanceof MemberExpression && key.equals(left)) { + assignations.delete(key); + assignations.set(left, right); + found = true; + } + }); + + if (!found) { + assignations.set(left, right); + } + } +} + +exportFnct.memberExpressionAssignment = memberExpressionAssignment; + +/** + * Merge the content of two AnalysisResult. + */ +function mergeResult(result1, result2) { + result2.assignations.forEach(function (value, key, map) { + result1.assignations.set(key, value); + }); +} + +exportFnct.mergeResult = mergeResult; + +/** + * Perform the main analysis to retrieve : + * - The list of all the function call. + * - The list of all the variable and their symbolic value. + */ +function analysis(tree, result, scope, scopeName, partialScope, useNewScope) { + // Default value - START + if (typeof result === "undefined") { + result = new AnalysisResult(); + } + + if (typeof scope === "undefined") { + scope = new Map(); + } + + if (typeof scopeName === "undefined") { + scopeName = "G" + CONST_SEPARATOR_ID; + } + + if (typeof partialScope === "undefined") { + partialScope = false; + } + + if (typeof useNewScope === "undefined") { + useNewScope = true; + } + // Default value - END + + if (tree.body && !Array.isArray(tree.body)) { + tree.body = [tree.body]; + } + + if (tree.left || tree.right) { + tree.body = []; + tree.left && tree.body.push(tree.left); + tree.right && tree.body.push(tree.right); + } + + switch (tree.type) { + case "ExpressionStatement": + tree = { body : [tree] }; + break; + + case "SequenceExpression": + tree.body = tree.expressions; + break; + + case "CallExpression": + case "AssignmentExpression": + tree = { body : [tree] }; + break; + } + + if (!tree.body) { + return new Map(); + } + + if (useNewScope) { + var newScope = new Map(scope); + var listVar = collectVar(tree, !partialScope, true); + + for (let i=0; i 2) { + invocation.fnct = new MemberExpression(leftParts.slice(0, -1)); + } else { + invocation.fnct = leftParts[0]; + } + + invocation.arguments = []; + + for (let i=0; i 0) { + let functionReference = functionArgs[0].fnct; + let output = []; + + for (let i=0; i Date: Fri, 24 May 2019 17:35:10 -0400 Subject: [PATCH 2/2] Fixed an issue with deep assignation on object crashing the analysis. --- tests/cases/nested-object-assignation.js | 16 ++++++++++++++++ tests/spec/all-spec.js | 8 ++++++++ utils.js | 12 ++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tests/cases/nested-object-assignation.js diff --git a/tests/cases/nested-object-assignation.js b/tests/cases/nested-object-assignation.js new file mode 100644 index 0000000..4b96490 --- /dev/null +++ b/tests/cases/nested-object-assignation.js @@ -0,0 +1,16 @@ +var obj = { + "bob" : { + "alice" : { + "v" : "bb" + } + } +} + +obj.bob.alice.vv = "cc"; + +var url = "/test"; + +url += obj.bob.alice.v; +url += obj.bob.alice.vv; + +$.get(url); diff --git a/tests/spec/all-spec.js b/tests/spec/all-spec.js index 0ca7957..5641672 100644 --- a/tests/spec/all-spec.js +++ b/tests/spec/all-spec.js @@ -148,4 +148,12 @@ describe("Edge cases - ", function () { var missing = checkMissing(found, expected); expect(missing).toEqual([]); }); + + it("should support deeply nested object assignation", function () { + var expected = ["/testbbcc"]; + var found = runCaseFile("nested-object-assignation"); + var missing = checkMissing(found, expected); + expect(missing).toEqual([]); + + }); }); diff --git a/utils.js b/utils.js index ef57d51..7559a71 100644 --- a/utils.js +++ b/utils.js @@ -39,6 +39,12 @@ function debug(message) { exportFnct.debug = debug; +function clone(obj) { + return JSON.parse(JSON.stringify(obj)); +} + +exportFnct.clone = clone; + /** * Analysis function */ @@ -345,7 +351,9 @@ function memberExpressionAssignment(left, right, assignations) { // For deep structure we need to do the a recursive assignation. if (mainObj.properties.has(prop.value) && left.parts.length > 2) { let subsetMemberExpression = [mainObj.properties.get(prop.value)].concat(left.parts.slice(2)); - memberExpressionAssignment(subsetMemberExpression, right, assignations); + let leftPartRemaining = clone(left); + leftPartRemaining.parts = subsetMemberExpression; + memberExpressionAssignment(leftPartRemaining, right, assignations); } else { mainObj.properties.set(prop.value, right); } @@ -792,4 +800,4 @@ function postProcessingResolveArgument(arg, result, usePlaceHolderForUnknown) { exportFnct.postProcessingResolveArgument = postProcessingResolveArgument; -module.exports = exportFnct; \ No newline at end of file +module.exports = exportFnct;