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