1 | var traverse = require('ast-traverse');
|
2 | var astUtils = require('sl-esprima-ast-utils');
|
3 | var globalErrorHandler = require('../global-error-handler');
|
4 | var babelParser = require("@babel/parser");
|
5 | var createNodeKey = require('./create-node-key').createNodeKey;
|
6 | var AstNodeTypes = require("./ast-node-types");
|
7 | var consoleFileLogger = require("../sl-logger.js").consoleFileLogger;
|
8 | var MethodSignature = require("./method-signature");
|
9 | var BranchSignature = require("./branch-signature");
|
10 | var globalMethodIndexer = require("./globalMethodIndexer");
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | var FileSignature = function (absolutePtah, relativePath, content, sourceMaps, opts, scmFilesContainer) {
|
23 | this.absolutePath = absolutePtah;
|
24 | this.relativePath = relativePath;
|
25 | this.methods = [];
|
26 | this.branches = [];
|
27 | this.content = content;
|
28 | this.sourceMaps = sourceMaps;
|
29 | this.opts = opts;
|
30 | this.scmFilesContainer = scmFilesContainer;
|
31 | this.hasSourceMaps = !!sourceMaps;
|
32 | this.logger = consoleFileLogger("FileSignature");
|
33 | this.positionToMethodMap = {};
|
34 | this.positionToBranchMap = {};
|
35 | this.methodIndex = 0;
|
36 | this.branchIndex = 0;
|
37 | };
|
38 |
|
39 | FileSignature.prototype._createSignature = function () {
|
40 | this.prepareContent();
|
41 | var ast = this.parseContentToAST();
|
42 | this.traverseAST(ast);
|
43 | };
|
44 |
|
45 | FileSignature.prototype.createDTO = function(){
|
46 | return {
|
47 | methods: this.methods,
|
48 | branches: this.branches,
|
49 | filename: this.relativePath,
|
50 | hasSourceMaps: this.hasSourceMaps
|
51 | }
|
52 | };
|
53 |
|
54 | FileSignature.prototype.parseContentToAST = function () {
|
55 | var sourceType = (this.opts && this.opts.es6Modules) ? 'module' : 'script';
|
56 |
|
57 | var plugins = ["*", ["decorators", {decoratorsBeforeExport: true}]];
|
58 | if (this.opts && this.opts.babylonPlugins) {
|
59 | plugins = plugins.concat(this.opts.babylonPlugins);
|
60 | }
|
61 | return babelParser.parse(this.content, {
|
62 |
|
63 | sourceType: sourceType,
|
64 | plugins: plugins
|
65 | })
|
66 | };
|
67 | FileSignature.prototype.traverseAST = function (ast) {
|
68 | var context = this;
|
69 | var nodeToParent = {};
|
70 | var activeMethodKey = null;
|
71 | var activeMethodIndex = null;
|
72 | traverse(ast, {
|
73 | pre: function (node, parentNode) {
|
74 | var key = createNodeKey(node);
|
75 | nodeToParent[key] = parentNode;
|
76 | if (AstNodeTypes.isMethodNode(node)) {
|
77 |
|
78 | if(activeMethodKey == null) {
|
79 | activeMethodKey = createNodeKey(node);
|
80 | }
|
81 | globalMethodIndexer.incrementIndex();
|
82 | |
83 |
|
84 |
|
85 |
|
86 | activeMethodIndex = globalMethodIndexer.getCurrentIndex();
|
87 | context.addMethod(node, parentNode, nodeToParent);
|
88 | context.addDefaultParameterBranch(node.params, parentNode, activeMethodIndex)
|
89 | } else if (context.opts.isBranchCoverage && AstNodeTypes.isBranchNode(node)) {
|
90 | context.addBranch(node, parentNode, activeMethodKey == null, activeMethodIndex)
|
91 | }
|
92 | },
|
93 | post: function (node) {
|
94 | if(AstNodeTypes.isMethodNode(node)){
|
95 |
|
96 | if(activeMethodKey === createNodeKey(node)) {
|
97 | activeMethodKey = null;
|
98 | }
|
99 | |
100 |
|
101 |
|
102 |
|
103 |
|
104 | activeMethodIndex --;
|
105 | }
|
106 | }
|
107 | });
|
108 | };
|
109 | FileSignature.prototype.addMethod = function (node, parentNode, nodeToParent) {
|
110 | var method = MethodSignature.create(node, parentNode, nodeToParent, this.absolutePath,
|
111 | this.relativePath, this.logger);
|
112 | method.srcData = method.generatedData;
|
113 | this.methods.push(method.createDTO());
|
114 | this.positionToMethodMap[method.generatedData.position] = this.methodIndex;
|
115 | this.methodIndex++;
|
116 | };
|
117 | FileSignature.prototype.addBranch = function (node, parentNode, isGlobalBranch, activeMethodIndex) {
|
118 | var context = this;
|
119 | var branches = [];
|
120 | if (node.type !== AstNodeTypes.LogicalExpression) {
|
121 | branches = branches.concat(this.getBranchesToAdd(node, isGlobalBranch, activeMethodIndex));
|
122 | }
|
123 |
|
124 |
|
125 | if (node.type == AstNodeTypes.LogicalExpression && (parentNode == undefined || parentNode.type !== AstNodeTypes.LogicalExpression)) {
|
126 | branches = branches.concat(this.getBranchesToAdd(node, isGlobalBranch, activeMethodIndex));
|
127 | }
|
128 |
|
129 | branches.forEach(function (branch) {
|
130 | branch.srcData = branch.generatedData;
|
131 | context.branches.push(branch.createDTO());
|
132 | context.positionToBranchMap[branch.generatedData.position] = context.branchIndex;
|
133 | context.branchIndex ++;
|
134 | })
|
135 | };
|
136 | FileSignature.prototype.prepareContent = function () {
|
137 | this.content = trimShebang(this.content);
|
138 | };
|
139 | FileSignature.prototype.getBranchesToAdd = function (node, isGlobalBranch, activeMethodIndex) {
|
140 | var branchesToAdd = [];
|
141 | var context = this;
|
142 | var nodeKey = createNodeKey(node)
|
143 | switch (node.type) {
|
144 |
|
145 | case AstNodeTypes.IfStatement:
|
146 | case AstNodeTypes.ConditionalExpression:
|
147 | var branchIndex = 0;
|
148 |
|
149 |
|
150 | if (node.consequent !== null) {
|
151 | var branch = BranchSignature.create(node.consequent, node, node.type, branchIndex++, this.absolutePath, this.relativePath,
|
152 | isGlobalBranch, activeMethodIndex, this.logger);
|
153 | branchesToAdd.push(branch);
|
154 | }
|
155 |
|
156 |
|
157 | var clonedNode = null;
|
158 | if (node.alternate == null) {
|
159 | clonedNode = createEmptyAlternate(node);
|
160 | }
|
161 | var nodeToAdd = clonedNode || node;
|
162 | var branch = BranchSignature.create(nodeToAdd.alternate, nodeToAdd, node.type, branchIndex++, this.absolutePath, this.relativePath,
|
163 | isGlobalBranch, activeMethodIndex, this.logger);
|
164 | branchesToAdd.push(branch);
|
165 | break;
|
166 | case AstNodeTypes.SwitchStatement:
|
167 | var branchIndex = 0;
|
168 |
|
169 |
|
170 | node.cases.forEach(function (caseNode) {
|
171 | if (caseNode !== null) {
|
172 | var branch = BranchSignature.create(caseNode, node, node.type, branchIndex++, context.absolutePath, context.relativePath,
|
173 | isGlobalBranch, activeMethodIndex, context.logger);
|
174 | branchesToAdd.push(branch);
|
175 | }
|
176 | });
|
177 | break;
|
178 | case AstNodeTypes.LogicalExpression:
|
179 | var branchIndex = 0;
|
180 | var leaves = [];
|
181 | findLogicalExpressionLeaves(node, leaves);
|
182 | leaves.forEach(function (leafNode) {
|
183 | if (leafNode !== null) {
|
184 | var branch = BranchSignature.create(leafNode, node, node.type, branchIndex++, context.absolutePath, context.relativePath,
|
185 | isGlobalBranch, activeMethodIndex, context.logger);
|
186 | branchesToAdd.push(branch);
|
187 | }
|
188 | });
|
189 | break;
|
190 | case AstNodeTypes.AssignmentPattern:
|
191 | |
192 |
|
193 |
|
194 |
|
195 |
|
196 | var branchIndex = 0;
|
197 | var branch = BranchSignature.create(node.right, node, AstNodeTypes.DefaultArgument, branchIndex++, this.absolutePath, this.relativePath,
|
198 | isGlobalBranch, activeMethodIndex, this.logger);
|
199 | branchesToAdd.push(branch);
|
200 | break;
|
201 | }
|
202 | return branchesToAdd;
|
203 | };
|
204 |
|
205 | FileSignature.prototype.addDefaultParameterBranch = function(functionParams, parentNode, activeMethodIndex){
|
206 | var context = this;
|
207 | functionParams = functionParams || [];
|
208 | functionParams.forEach(function (param) {
|
209 | if(param.type === AstNodeTypes.AssignmentPattern){
|
210 | context.addBranch(param, parentNode, true, activeMethodIndex)
|
211 | }
|
212 | })
|
213 | }
|
214 |
|
215 |
|
216 | FileSignature.create = function (absolutePtah, relativePath, content, sourceMaps, opts, scmFilesContainer) {
|
217 | var signature = new this(absolutePtah, relativePath, content, sourceMaps, opts, scmFilesContainer);
|
218 | signature._createSignature();
|
219 | return signature;
|
220 | }
|
221 |
|
222 |
|
223 | function findLogicalExpressionLeaves(node, leaves) {
|
224 | if (node.type === AstNodeTypes.LogicalExpression) {
|
225 | findLogicalExpressionLeaves(node.left, leaves);
|
226 | findLogicalExpressionLeaves(node.right, leaves);
|
227 | } else {
|
228 | leaves.push(node);
|
229 | }
|
230 | }
|
231 |
|
232 |
|
233 |
|
234 |
|
235 | function trimShebang(text) {
|
236 | return text.replace(/^#!([^\r\n]+)/, function (match, captured) {
|
237 | return "//" + captured;
|
238 | });
|
239 | }
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 | function _createEmptyBlock() {
|
248 | return {
|
249 | type: AstNodeTypes.BlockStatement,
|
250 | body: []
|
251 | }
|
252 | }
|
253 |
|
254 | function createEmptyAlternate(node) {
|
255 |
|
256 | var clonedNode = astUtils.clone(node);
|
257 | clonedNode.alternate = _createEmptyBlock();
|
258 |
|
259 |
|
260 | clonedNode.alternate.loc = node.consequent !== null ? node.consequent.loc : node.loc;
|
261 | return clonedNode;
|
262 | }
|
263 |
|
264 | module.exports = FileSignature;
|