-
Notifications
You must be signed in to change notification settings - Fork 0
/
js_editor.js
155 lines (131 loc) · 4.9 KB
/
js_editor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
* @description Class that is responsible for editing JS files.
* Thus contains the instrumentation code aswell.
*
* @version 0.1
* @author Kishan Nirghin
* @Date 10-02-2019
*/
const fs = require("fs"),
esprima = require("esprima"),
path = require("path");
require("./prototype_extension");
const ESPRIMA_FUNCTION_TYPES = ['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'];
module.exports = class JsEditor {
constructor(filePath = null) {
if (filePath) { this.loadFile(filePath); }
}
loadSource(source, filePath = null) {
this.source = this.originalSource = source;
this.filePath = filePath;
this._onLoadedHook();
return this;
}
loadFile(filePath) {
this.filePath = filePath;
this.source = this.originalSource = fs.readFileSync(filePath).toString();
this._onLoadedHook();
return this;
}
loadFunctionData() {
var functionData = [];
var index = 0;
esprima.parse(this.source, { range: true }, (node) => {
if (ESPRIMA_FUNCTION_TYPES.includes(node.type)) {
functionData.push({
type: node.type,
bodyRange: node.body.range,
range: node.range,
file: this.filePath,
index: index++
});
}
});
return functionData;
}
instrumentFunctions(options) {
var functionData = this.loadFunctionData();
/* obviously needed when working with offsets */
functionData.sort((a, b) => { return a.range[0] - b.range[0]});
var instrumentationLogFunction = this._getInstrumentationLogFunction(options);
this.source = this.source.insert(0, instrumentationLogFunction);
var offset = instrumentationLogFunction.length;
functionData.forEach((funcData, index) => {
if (options && options["label"]) { funcData.label = options["label"]; } /* Add a fixed label to every function */
/* The code that has to be inserted at the top of the function */
var instrumentationCode = this._instrumentationBuilder(funcData, index);
let sourceIndex = funcData.bodyRange[0] + offset + 1; // +1 to skip the opening bracket
this.source = this.source.insert(sourceIndex, instrumentationCode);
offset += instrumentationCode.length;
});
return functionData;
}
/**
* The function that will catch all instrumentation logs
* Note that this function goes hand-in-hand with the _instrumentationBuilder
* annd with the instrumentation_server
*
* Currently this function does a call to the instrumentation server with
* the function information of the alive function.
*/
_getInstrumentationLogFunction(options) {
var ensureUniqueLogs = "";
if (options && options["unique"]) {
ensureUniqueLogs = `
function exists(e) { return e.file == jData.file && e.range[0] == jData.range[0] && e.range[1] == jData.range[1]; }
var jData = JSON.parse(data);
if (logHistory.some(exists)){ return; }
logHistory.push(jData);`
}
var instrumentationLog = null;
if (options && options["console"]) {
instrumentationLog = `console.log(data);`;
} else {
instrumentationLog = `fetch("http://127.0.0.1:8004/alivefunction", {
method: "POST",
headers: { "Accept": "application/json", "Content-Type": "application/json" },
body: data
}).then((response) => { });`;
}
return `/** (github.com/dynamic-deadfunction-detector)
* Instrumentation log function used by the instrumenter
* note that the data object is already stringified.
*/
var logHistory = [];
function instrumentation_log(data) {
${ensureUniqueLogs}
${instrumentationLog}
}
\n\n`
}
/**
* Creates the code that should be ran at the top of any function
* Currently makes a call to instrumentation_log with 3 params
*/
_instrumentationBuilder(funcData, index) {
var data = JSON.stringify(funcData);
return `instrumentation_log('${data}');`;
}
/**
* Note: this function should only be called when its the js file that gets
* overwritten. (should not be the html file)
*/
saveFile() {
if(this.filePath == null) {
return console.log("js_editor save error: No file loaded");
}
if (path.extname(this.filePath) != ".js") {
return console.log("js_editor save error: Invalid file");
}
fs.writeFileSync( this.filePath, this.source );
}
_onLoadedHook() {
// doesnt do anything at the moment.
}
getSource() {
return this.source;
}
getOriginalSource() {
return this.originalSource;
}
}