Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 # Copyright 2014 the V8 project authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 import js2c
      7 import os
      8 import re
      9 import sys
     10 
     11 FILENAME = "src/runtime.cc"
     12 FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)")
     13 FUNCTIONEND = "}\n"
     14 MACRO = re.compile(r"^#define ([^ ]+)\(([^)]*)\) *([^\\]*)\\?\n$")
     15 FIRST_WORD = re.compile("^\s*(.*?)[\s({\[]")
     16 
     17 # Expand these macros, they define further runtime functions.
     18 EXPAND_MACROS = [
     19   "BUFFER_VIEW_GETTER",
     20   "DATA_VIEW_GETTER",
     21   "DATA_VIEW_SETTER",
     22   "ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION",
     23   "FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION",
     24   "RUNTIME_UNARY_MATH",
     25   "TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION",
     26 ]
     27 
     28 
     29 class Function(object):
     30   def __init__(self, match):
     31     self.name = match.group(1)
     32 
     33 
     34 class Macro(object):
     35   def __init__(self, match):
     36     self.name = match.group(1)
     37     self.args = [s.strip() for s in match.group(2).split(",")]
     38     self.lines = []
     39     self.indentation = 0
     40     self.AddLine(match.group(3))
     41 
     42   def AddLine(self, line):
     43     if not line: return
     44     if not self.lines:
     45       # This is the first line, detect indentation.
     46       self.indentation = len(line) - len(line.lstrip())
     47     line = line.rstrip("\\\n ")
     48     if not line: return
     49     assert len(line[:self.indentation].strip()) == 0, \
     50         ("expected whitespace: '%s', full line: '%s'" %
     51          (line[:self.indentation], line))
     52     line = line[self.indentation:]
     53     if not line: return
     54     self.lines.append(line + "\n")
     55 
     56   def Finalize(self):
     57     for arg in self.args:
     58       pattern = re.compile(r"(##|\b)%s(##|\b)" % arg)
     59       for i in range(len(self.lines)):
     60         self.lines[i] = re.sub(pattern, "%%(%s)s" % arg, self.lines[i])
     61 
     62   def FillIn(self, arg_values):
     63     filler = {}
     64     assert len(arg_values) == len(self.args)
     65     for i in range(len(self.args)):
     66       filler[self.args[i]] = arg_values[i]
     67     result = []
     68     for line in self.lines:
     69       result.append(line % filler)
     70     return result
     71 
     72 
     73 def ReadFileAndExpandMacros(filename):
     74   found_macros = {}
     75   expanded_lines = []
     76   with open(filename, "r") as f:
     77     found_macro = None
     78     for line in f:
     79       if found_macro is not None:
     80         found_macro.AddLine(line)
     81         if not line.endswith("\\\n"):
     82           found_macro.Finalize()
     83           found_macro = None
     84         continue
     85 
     86       match = MACRO.match(line)
     87       if match:
     88         found_macro = Macro(match)
     89         if found_macro.name in EXPAND_MACROS:
     90           found_macros[found_macro.name] = found_macro
     91         else:
     92           found_macro = None
     93         continue
     94 
     95       match = FIRST_WORD.match(line)
     96       if match:
     97         first_word = match.group(1)
     98         if first_word in found_macros:
     99           MACRO_CALL = re.compile("%s\(([^)]*)\)" % first_word)
    100           match = MACRO_CALL.match(line)
    101           assert match
    102           args = [s.strip() for s in match.group(1).split(",")]
    103           expanded_lines += found_macros[first_word].FillIn(args)
    104           continue
    105 
    106       expanded_lines.append(line)
    107   return expanded_lines
    108 
    109 
    110 # Detects runtime functions by parsing FILENAME.
    111 def FindRuntimeFunctions():
    112   functions = []
    113   expanded_lines = ReadFileAndExpandMacros(FILENAME)
    114   function = None
    115   partial_line = ""
    116   for line in expanded_lines:
    117     # Multi-line definition support, ignoring macros.
    118     if line.startswith("RUNTIME_FUNCTION") and not line.endswith("{\n"):
    119       if line.endswith("\\\n"): continue
    120       partial_line = line.rstrip()
    121       continue
    122     if partial_line:
    123       partial_line += " " + line.strip()
    124       if partial_line.endswith("{"):
    125         line = partial_line
    126         partial_line = ""
    127       else:
    128         continue
    129 
    130     match = FUNCTION.match(line)
    131     if match:
    132       function = Function(match)
    133       continue
    134     if function is None: continue
    135 
    136     if line == FUNCTIONEND:
    137       if function is not None:
    138         functions.append(function)
    139         function = None
    140   return functions
    141 
    142 
    143 class Builtin(object):
    144   def __init__(self, match):
    145     self.name = match.group(1)
    146 
    147 
    148 def FindJSNatives():
    149   PATH = "src"
    150   fileslist = []
    151   for (root, dirs, files) in os.walk(PATH):
    152     for f in files:
    153       if f.endswith(".js"):
    154         fileslist.append(os.path.join(root, f))
    155   natives = []
    156   regexp = re.compile("^function (\w+)\s*\((.*?)\) {")
    157   matches = 0
    158   for filename in fileslist:
    159     with open(filename, "r") as f:
    160       file_contents = f.read()
    161     file_contents = js2c.ExpandInlineMacros(file_contents)
    162     lines = file_contents.split("\n")
    163     partial_line = ""
    164     for line in lines:
    165       if line.startswith("function") and not '{' in line:
    166         partial_line += line.rstrip()
    167         continue
    168       if partial_line:
    169         partial_line += " " + line.strip()
    170         if '{' in line:
    171           line = partial_line
    172           partial_line = ""
    173         else:
    174           continue
    175       match = regexp.match(line)
    176       if match:
    177         natives.append(Builtin(match))
    178   return natives
    179 
    180 
    181 def Main():
    182   functions = FindRuntimeFunctions()
    183   natives = FindJSNatives()
    184   errors = 0
    185   runtime_map = {}
    186   for f in functions:
    187     runtime_map[f.name] = 1
    188   for b in natives:
    189     if b.name in runtime_map:
    190       print("JS_Native/Runtime_Function name clash: %s" % b.name)
    191       errors += 1
    192 
    193   if errors > 0:
    194     return 1
    195   print("Runtime/Natives name clashes: checked %d/%d functions, all good." %
    196         (len(functions), len(natives)))
    197   return 0
    198 
    199 
    200 if __name__ == "__main__":
    201   sys.exit(Main())
    202