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;