github.com/igggame/nebulas-go@v2.1.0+incompatible/nf/nvm/v8/lib/1.1.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 function is_block_statement(node) { 164 if (!node || !node.type) { 165 // not a valid node, ignore 166 return false; 167 } 168 169 if (!(node.type in { 170 BlockStatement: "", 171 IfStatement: "", 172 })) { 173 return true; 174 } 175 return false 176 } 177 178 var ast = esprima.parseScript(source, { 179 range: true, 180 loc: true 181 }); 182 183 var source_line_offset = 0; 184 185 traverse(ast, function (node, parents, injection_context_from_parent) { 186 // throw error when "_instruction_counter" was redefined in source. 187 disallowRedefineOfInstructionCounter(node, parents, strictDisallowUsage); 188 189 // 1. flag find the injection point, eg a Expression/Statement can inject code directly. 190 if (node.type == "IfStatement") { 191 ensure_block_statement(node.consequent); 192 ensure_block_statement(node.alternate); 193 return { 194 "test": new InjectionContext(node.test, InjectionType.INNER_BEGINNING), 195 }; 196 } else if (node.type == "ForStatement") { 197 debugger 198 ensure_block_statement(node.body); 199 200 var pos = node.body.range[0]; 201 if (node.body.type === 'BlockStatement') { 202 pos += 1; 203 } 204 record_injection(pos, 1, InjectionCodeGenerators.CounterIncrFunc); 205 return { 206 "init": new InjectionContext(node, InjectionType.BEFORE_NODE), 207 "test": new InjectionContext(node.test, InjectionType.INNER_BEGINNING), 208 "update": new InjectionContext(node.update, InjectionType.INNER_BEGINNING), 209 }; 210 } else if (node.type == "ForInStatement") { 211 ensure_block_statement(node.body); 212 213 // because for in just call right once and iterate internal, 214 // to keep inst const consistency with others, we manually add 1. 215 var body = node.body; 216 var pos = body.range[0]; 217 if (body.type === 'BlockStatement') { 218 pos = body.range[0] + 1; 219 } 220 record_injection(pos, 2, InjectionCodeGenerators.CounterIncrFunc); 221 222 return { 223 "left": new InjectionContext(node, InjectionType.BEFORE_NODE), 224 "right": new InjectionContext(node, InjectionType.BEFORE_NODE), 225 }; 226 } else if (node.type == "ForOfStatement") { 227 ensure_block_statement(node.body); 228 229 // because for in just call right once and iterate internal, 230 // to keep inst const consistency with others, we manually add 1. 231 var body = node.body; 232 var pos = body.range[0]; 233 if (body.type === 'BlockStatement') { 234 pos = body.range[0] + 1; 235 } 236 record_injection(pos, 2, InjectionCodeGenerators.CounterIncrFunc); 237 238 return { 239 "left": new InjectionContext(node, InjectionType.BEFORE_NODE), 240 "right": new InjectionContext(node, InjectionType.BEFORE_NODE), 241 }; 242 } else if (node.type == "WhileStatement") { 243 ensure_block_statement(node.body); 244 var pos = node.body.range[0]; 245 if (node.body.type === 'BlockStatement') { 246 pos += 1; 247 } 248 record_injection(pos, 1, InjectionCodeGenerators.CounterIncrFunc); 249 return { 250 "test": new InjectionContext(node.test, InjectionType.INNER_BEGINNING), 251 }; 252 } else if (node.type == "DoWhileStatement") { 253 ensure_block_statement(node.body); 254 var pos = node.body.range[0]; 255 if (node.body.type === 'BlockStatement') { 256 pos += 1; 257 } 258 record_injection(pos, 1, InjectionCodeGenerators.CounterIncrFunc); 259 return { 260 "test": new InjectionContext(node.test, InjectionType.INNER_BEGINNING), 261 }; 262 } else if (node.type == "WithStatement") { 263 ensure_block_statement(node.body); 264 return { 265 "object": new InjectionContext(node, InjectionType.BEFORE_NODE), 266 }; 267 } else if (node.type == "SwitchStatement") { 268 return { 269 "discriminant": new InjectionContext(node, InjectionType.BEFORE_NODE), 270 }; 271 } else if (node.type == "ArrowFunctionExpression") { 272 var body = node.body; 273 if (body.type !== 'BlockStatement') { 274 record_injection(body.range[0], 0, InjectionCodeGenerators.BlockStatementBeginAndCounterIncrFuncAndReturn); 275 record_injection(body.range[1], 0, InjectionCodeGenerators.BlockStatementEndAndCounterIncrFunc); 276 277 // only return injection context when body is not in {}; 278 return { 279 "body": new InjectionContext(body, InjectionType.BEFORE_NODE), 280 }; 281 } 282 } else if (node.type == "ConditionalExpression") { 283 return { 284 "test": new InjectionContext(node.test, InjectionType.INNER_BEGINNING_NOT_AND_OR), 285 "consequent": new InjectionContext(node.consequent, InjectionType.INNER_BEGINNING_NOT_AND_OR), 286 "alternate": new InjectionContext(node.alternate, InjectionType.INNER_BEGINNING_NOT_AND_OR), 287 }; 288 } else { 289 290 // Other Expressions. 291 var tracing_val = TrackingExpressions[node.type]; 292 if (!tracing_val) { 293 // not the tracking expression, ignore. 294 return; 295 } 296 297 // If no parent, apply default rule: BEFORE_NODE. 298 var parent_node = parents[0].node; 299 if (!parent_node) { 300 record_injection(node.range[0], tracing_val, InjectionCodeGenerators.CounterIncrFunc); 301 return; 302 } 303 304 var injection_type = null; 305 var target_node = null; 306 307 if (injection_context_from_parent) { 308 target_node = injection_context_from_parent.node; 309 injection_type = injection_context_from_parent.type; 310 } else { 311 injection_type = InjectionType.BEFORE_NODE; 312 } 313 314 if (!target_node) { 315 if (node.type in InjectableExpressions) { 316 target_node = node; 317 } else { 318 // searching parent to find the injection position. 319 for (var i = 0; i < parents.length; i++) { 320 var ancestor = parents[i]; 321 if (ancestor.node.type in InjectableExpressions) { 322 target_node = ancestor.node; 323 break; 324 } 325 } 326 } 327 } 328 329 var pos = -1; 330 var generator = InjectionCodeGenerators.CounterIncrFunc; 331 332 switch (injection_type) { 333 case InjectionType.BEFORE_NODE: 334 pos = target_node.range[0]; 335 break; 336 case InjectionType.AT_BEGINNING: 337 if (target_node.type === 'BlockStatement') { 338 pos = target_node.range[0] + 1; // after "{". 339 } else { 340 pos = target_node.range[0]; // before statement start. 341 } 342 break; 343 case InjectionType.INNER_BEGINNING: 344 pos = -1; 345 record_injection(target_node.range[0], tracing_val, InjectionCodeGenerators.BeginInnerCounterIncrFunc); 346 record_injection(target_node.range[1], tracing_val, InjectionCodeGenerators.EndInnerCounterIncrFunc); 347 break; 348 case InjectionType.INNER_BEGINNING_NOT_AND_OR: 349 pos = target_node.range[0]; 350 generator = InjectionCodeGenerators.CounterIncrFuncUsingNotAndLogicalOrFunc; 351 break; 352 default: 353 throw new Error("Unknown Injection Type " + injection_type); 354 } 355 356 if (pos >= 0) { 357 record_injection(pos, tracing_val, generator); 358 } 359 } 360 }); 361 362 // generate traceable source. 363 var ordered_records = Array.from(injection_records.values()); 364 ordered_records.sort(function (a, b) { 365 return a.pos - b.pos; 366 }); 367 368 369 var start_offset = 0, 370 traceable_source = ""; 371 ordered_records.forEach(function (record) { 372 traceable_source += source.slice(start_offset, record.pos); 373 traceable_source += record.func(record.value); 374 start_offset = record.pos; 375 }); 376 traceable_source += source.slice(start_offset); 377 378 return { 379 traceableSource: traceable_source, 380 lineOffset: source_line_offset 381 }; 382 }; 383 384 // throw error when "_instruction_counter" was redefined. 385 function disallowRedefineOfInstructionCounter(node, parents, strictDisallowUsage) { 386 if (node.type == 'Identifier') { 387 if (node.name != '_instruction_counter') { 388 return; 389 } 390 } else if (node.type == 'Literal') { 391 if (node.value != '_instruction_counter') { 392 return; 393 } 394 } else { 395 return; 396 } 397 398 if (strictDisallowUsage) { 399 throw new Error("redefine or use _instruction_counter are now allowed."); 400 } 401 402 var parent_node = parents[0].node; 403 if (!parent_node) { 404 return; 405 } 406 407 if (parent_node.type in { 408 VariableDeclarator: "", 409 FunctionDeclaration: "", 410 FunctionExpression: "", 411 ArrayPattern: "", 412 }) { 413 throw new Error("redefine _instruction_counter is now allowed."); 414 } 415 }; 416 417 418 exports["parseScript"] = esprima.parseScript; 419 exports["processScript"] = processScript;