blob: e4489303270dbae94d66d075e9dc2dc9f5d759e9 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001#!/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
6import js2c
7import os
8import re
9import sys
10
11FILENAME = "src/runtime.cc"
12FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)")
13FUNCTIONEND = "}\n"
14MACRO = re.compile(r"^#define ([^ ]+)\(([^)]*)\) *([^\\]*)\\?\n$")
15FIRST_WORD = re.compile("^\s*(.*?)[\s({\[]")
16
17# Expand these macros, they define further runtime functions.
18EXPAND_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
29class Function(object):
30 def __init__(self, match):
31 self.name = match.group(1)
32
33
34class 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
73def 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.
111def 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
143class Builtin(object):
144 def __init__(self, match):
145 self.name = match.group(1)
146
147
148def 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
181def 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
200if __name__ == "__main__":
201 sys.exit(Main())