github.com/igggame/nebulas-go@v2.1.0+incompatible/nf/nvm/v8/lib/1.0.0/instruction_counter.js (about) 1 // Copyright (C) 2017 go-nebulas authors 2 // 3 // This file is part of the go-nebulas library. 4 // 5 // the go-nebulas library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // the go-nebulas library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with the go-nebulas library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 'use strict'; 19 20 const module_path_prefix = (typeof process !== 'undefined') && (process.release.name === 'node') ? './' : ''; 21 const esprima = require(module_path_prefix + 'esprima.js'); 22 23 function traverse(object, visitor, master, injection_context_from_parent) { 24 var key, child, parent, path; 25 26 parent = (typeof master === 'undefined') ? [{ 27 node: null, 28 key: "" 29 }] : master; 30 31 var injection_context = visitor.call(null, object, parent, injection_context_from_parent); 32 if (injection_context === false) { 33 return; 34 } 35 36 for (key in object) { 37 if (object.hasOwnProperty(key)) { 38 child = object[key]; 39 if (typeof child === 'object' && child !== null) { 40 var injection_context_of_key = injection_context ? injection_context[key] : injection_context_from_parent; 41 42 if (Array.isArray(object)) { 43 // ignore Array object in parents. 44 path = []; 45 } else { 46 path = [{ 47 node: object, 48 key: key 49 }]; 50 } 51 path.push.apply(path, parent); 52 traverse(child, visitor, path, injection_context_of_key); 53 } 54 } 55 } 56 }; 57 58 // key is the Expression, value is the count of instruction of the Expression. 59 const TrackingExpressions = { 60 CallExpression: 8, 61 AssignmentExpression: 3, 62 BinaryExpression: 3, 63 UpdateExpression: 3, 64 UnaryExpression: 3, 65 LogicalExpression: 3, 66 MemberExpression: 4, 67 NewExpression: 8, 68 ThrowStatement: 6, 69 MetaProperty: 4, 70 ConditionalExpression: 3, 71 YieldExpression: 6, 72 }; 73 74 const InjectableExpressions = { 75 ExpressionStatement: 1, 76 VariableDeclaration: 1, 77 ReturnStatement: 1, 78 ThrowStatement: 1, 79 }; 80 81 const InjectionType = { 82 BEFORE_NODE: "BEFORE_NODE", 83 AT_BEGINNING: "AT_BEGINNING", 84 INNER_BEGINNING: "INNER_BEGINNING", 85 INNER_BEGINNING_NOT_AND_OR: "INNER_BEGINNING_NOT_AND_OR", 86 }; 87 88 const InjectionCodeGenerators = { 89 CounterIncrFunc: function (value) { 90 return "_instruction_counter.incr(" + value + ");"; 91 }, 92 BlockStatementBeginAndCounterIncrFunc: function (value) { 93 if (value > 0) { 94 return "{_instruction_counter.incr(" + value + ");" 95 } else { 96 return "{"; 97 } 98 }, 99 BlockStatementEndAndCounterIncrFunc: function (value) { 100 if (value > 0) { 101 return "_instruction_counter.incr(" + value + ");}" 102 } else { 103 return "}"; 104 } 105 }, 106 BlockStatementBeginAndCounterIncrFuncAndReturn: function (value) { 107 if (value > 0) { 108 return "{_instruction_counter.incr(" + value + "); return " 109 } else { 110 return "{return "; 111 } 112 }, 113 BeginInnerCounterIncrFunc: function (value) { 114 return "_instruction_counter.incr(" + value + ") && ("; 115 }, 116 EndInnerCounterIncrFunc: function (value) { 117 return ")"; 118 }, 119 CounterIncrFuncUsingNotAndLogicalOrFunc: function (value) { 120 return "!_instruction_counter.incr(" + value + ") || "; 121 }, 122 }; 123 124 function InjectionContext(node, type) { 125 this.node = node; 126 this.type = type; 127 }; 128 129 function record_injection_info(injection_records, pos, value, injection_func) { 130 var item = injection_records.get(pos); 131 if (!item) { 132 item = { 133 pos: pos, 134 value: 0, 135 func: injection_func, 136 }; 137 injection_records.set(pos, item); 138 } 139 item.value += value; 140 }; 141 142 function processScript(source, strictDisallowUsage) { 143 var injection_records = new Map(); 144 var record_injection = function (pos, value, injection_func) { 145 return record_injection_info(injection_records, pos, value, injection_func); 146 }; 147 148 function ensure_block_statement(node) { 149 if (!node || !node.type) { 150 // not a valid node, ignore 151 return; 152 } 153 154 if (!(node.type in { 155 BlockStatement: "", 156 IfStatement: "", 157 })) { 158 record_injection(node.range[0], 0, InjectionCodeGenerators.BlockStatementBeginAndCounterIncrFunc); 159 record_injection(node.range[1], 0, InjectionCodeGenerators.BlockStatementEndAndCounterIncrFunc); 160 } 161 }; 162 163 var ast = esprima.parseScript(source, { 164 range: true, 165 loc: true 166 }); 167 168 var source_line_offset = 0; 169 170 traverse(ast, function (node, parents, injection_context_from_parent) { 171 // throw error when "_instruction_counter" was redefined in source. 172 disallowRedefineOfInstructionCounter(node, parents, strictDisallowUsage); 173 174 // 1. flag find the injection point, eg a Expression/Statement can inject code directly. 175 if (node.type == "IfStatement") { 176 ensure_block_statement(node.consequent); 177 ensure_block_statement(node.alternate); 178 return { 179 "test": new InjectionContext(node.test, InjectionType.INNER_BEGINNING), 180 }; 181 } else if (node.type == "ForStatement") { 182 debugger 183 ensure_block_statement(node.body); 184 return { 185 "init": new InjectionContext(node, InjectionType.BEFORE_NODE), 186 "test": new InjectionContext(node.test, InjectionType.INNER_BEGINNING), 187 "update": new InjectionContext(node.update, InjectionType.INNER_BEGINNING), 188 }; 189 } else if (node.type == "ForInStatement") { 190 ensure_block_statement(node.body); 191 192 // because for in just call right once and iterate internal, 193 // to keep inst const consistency with others, we manually add 1. 194 var body = node.body; 195 var pos = body.range[0]; 196 if (body.type === 'BlockStatement') { 197 pos = body.range[0] + 1; 198 } 199 record_injection(pos, 1, InjectionCodeGenerators.CounterIncrFunc); 200 201 return { 202 "left": new InjectionContext(node, InjectionType.BEFORE_NODE), 203 "right": new InjectionContext(node, InjectionType.BEFORE_NODE), 204 }; 205 } else if (node.type == "ForOfStatement") { 206 ensure_block_statement(node.body); 207 208 // because for in just call right once and iterate internal, 209 // to keep inst const consistency with others, we manually add 1. 210 var body = node.body; 211 var pos = body.range[0]; 212 if (body.type === 'BlockStatement') { 213 pos = body.range[0] + 1; 214 } 215 record_injection(pos, 1, InjectionCodeGenerators.CounterIncrFunc); 216 217 return { 218 "left": new InjectionContext(node, InjectionType.BEFORE_NODE), 219 "right": new InjectionContext(node, InjectionType.BEFORE_NODE), 220 }; 221 } else if (node.type == "WhileStatement") { 222 ensure_block_statement(node.body); 223 return { 224 "test": new InjectionContext(node.test, InjectionType.INNER_BEGINNING), 225 }; 226 } else if (node.type == "DoWhileStatement") { 227 ensure_block_statement(node.body); 228 return { 229 "test": new InjectionContext(node.test, InjectionType.INNER_BEGINNING), 230 }; 231 } else if (node.type == "WithStatement") { 232 ensure_block_statement(node.body); 233 return { 234 "object": new InjectionContext(node, InjectionType.BEFORE_NODE), 235 }; 236 } else if (node.type == "SwitchStatement") { 237 return { 238 "discriminant": new InjectionContext(node, InjectionType.BEFORE_NODE), 239 }; 240 } else if (node.type == "ArrowFunctionExpression") { 241 var body = node.body; 242 if (body.type !== 'BlockStatement') { 243 record_injection(body.range[0], 0, InjectionCodeGenerators.BlockStatementBeginAndCounterIncrFuncAndReturn); 244 record_injection(body.range[1], 0, InjectionCodeGenerators.BlockStatementEndAndCounterIncrFunc); 245 246 // only return injection context when body is not in {}; 247 return { 248 "body": new InjectionContext(body, InjectionType.BEFORE_NODE), 249 }; 250 } 251 } else if (node.type == "ConditionalExpression") { 252 return { 253 "test": new InjectionContext(node.test, InjectionType.INNER_BEGINNING_NOT_AND_OR), 254 "consequent": new InjectionContext(node.consequent, InjectionType.INNER_BEGINNING_NOT_AND_OR), 255 "alternate": new InjectionContext(node.alternate, InjectionType.INNER_BEGINNING_NOT_AND_OR), 256 }; 257 } else { 258 259 // Other Expressions. 260 var tracing_val = TrackingExpressions[node.type]; 261 if (!tracing_val) { 262 // not the tracking expression, ignore. 263 return; 264 } 265 266 // If no parent, apply default rule: BEFORE_NODE. 267 var parent_node = parents[0].node; 268 if (!parent_node) { 269 record_injection(node.range[0], tracing_val, InjectionCodeGenerators.CounterIncrFunc); 270 return; 271 } 272 273 var injection_type = null; 274 var target_node = null; 275 276 if (injection_context_from_parent) { 277 target_node = injection_context_from_parent.node; 278 injection_type = injection_context_from_parent.type; 279 } else { 280 injection_type = InjectionType.BEFORE_NODE; 281 } 282 283 if (!target_node) { 284 if (node.type in InjectableExpressions) { 285 target_node = node; 286 } else { 287 // searching parent to find the injection position. 288 for (var i = 0; i < parents.length; i++) { 289 var ancestor = parents[i]; 290 if (ancestor.node.type in InjectableExpressions) { 291 target_node = ancestor.node; 292 break; 293 } 294 } 295 } 296 } 297 298 var pos = -1; 299 var generator = InjectionCodeGenerators.CounterIncrFunc; 300 301 switch (injection_type) { 302 case InjectionType.BEFORE_NODE: 303 pos = target_node.range[0]; 304 break; 305 case InjectionType.AT_BEGINNING: 306 if (target_node.type === 'BlockStatement') { 307 pos = target_node.range[0] + 1; // after "{". 308 } else { 309 pos = target_node.range[0]; // before statement start. 310 } 311 break; 312 case InjectionType.INNER_BEGINNING: 313 pos = -1; 314 record_injection(target_node.range[0], tracing_val, InjectionCodeGenerators.BeginInnerCounterIncrFunc); 315 record_injection(target_node.range[1], tracing_val, InjectionCodeGenerators.EndInnerCounterIncrFunc); 316 break; 317 case InjectionType.INNER_BEGINNING_NOT_AND_OR: 318 pos = target_node.range[0]; 319 generator = InjectionCodeGenerators.CounterIncrFuncUsingNotAndLogicalOrFunc; 320 break; 321 default: 322 throw new Error("Unknown Injection Type " + injection_type); 323 } 324 325 if (pos >= 0) { 326 record_injection(pos, tracing_val, generator); 327 } 328 } 329 }); 330 331 // generate traceable source. 332 var ordered_records = Array.from(injection_records.values()); 333 ordered_records.sort(function (a, b) { 334 return a.pos - b.pos; 335 }); 336 337 338 var start_offset = 0, 339 traceable_source = ""; 340 ordered_records.forEach(function (record) { 341 traceable_source += source.slice(start_offset, record.pos); 342 traceable_source += record.func(record.value); 343 start_offset = record.pos; 344 }); 345 traceable_source += source.slice(start_offset); 346 347 return { 348 traceableSource: traceable_source, 349 lineOffset: source_line_offset 350 }; 351 }; 352 353 // throw error when "_instruction_counter" was redefined. 354 function disallowRedefineOfInstructionCounter(node, parents, strictDisallowUsage) { 355 if (node.type == 'Identifier') { 356 if (node.name != '_instruction_counter') { 357 return; 358 } 359 } else if (node.type == 'Literal') { 360 if (node.value != '_instruction_counter') { 361 return; 362 } 363 } else { 364 return; 365 } 366 367 if (strictDisallowUsage) { 368 throw new Error("redefine or use _instruction_counter are now allowed."); 369 } 370 371 var parent_node = parents[0].node; 372 if (!parent_node) { 373 return; 374 } 375 376 if (parent_node.type in { 377 VariableDeclarator: "", 378 FunctionDeclaration: "", 379 FunctionExpression: "", 380 ArrayPattern: "", 381 }) { 382 throw new Error("redefine _instruction_counter is now allowed."); 383 } 384 }; 385 386 387 exports["parseScript"] = esprima.parseScript; 388 exports["processScript"] = processScript; 389