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