| #!/usr/bin/env python |
| # Copyright 2014 the V8 project authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import js2c |
| import os |
| import re |
| import sys |
| |
| FILENAME = "src/runtime.cc" |
| FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)") |
| FUNCTIONEND = "}\n" |
| MACRO = re.compile(r"^#define ([^ ]+)\(([^)]*)\) *([^\\]*)\\?\n$") |
| FIRST_WORD = re.compile("^\s*(.*?)[\s({\[]") |
| |
| # Expand these macros, they define further runtime functions. |
| EXPAND_MACROS = [ |
| "BUFFER_VIEW_GETTER", |
| "DATA_VIEW_GETTER", |
| "DATA_VIEW_SETTER", |
| "ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION", |
| "FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION", |
| "RUNTIME_UNARY_MATH", |
| "TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION", |
| ] |
| |
| |
| class Function(object): |
| def __init__(self, match): |
| self.name = match.group(1) |
| |
| |
| class Macro(object): |
| def __init__(self, match): |
| self.name = match.group(1) |
| self.args = [s.strip() for s in match.group(2).split(",")] |
| self.lines = [] |
| self.indentation = 0 |
| self.AddLine(match.group(3)) |
| |
| def AddLine(self, line): |
| if not line: return |
| if not self.lines: |
| # This is the first line, detect indentation. |
| self.indentation = len(line) - len(line.lstrip()) |
| line = line.rstrip("\\\n ") |
| if not line: return |
| assert len(line[:self.indentation].strip()) == 0, \ |
| ("expected whitespace: '%s', full line: '%s'" % |
| (line[:self.indentation], line)) |
| line = line[self.indentation:] |
| if not line: return |
| self.lines.append(line + "\n") |
| |
| def Finalize(self): |
| for arg in self.args: |
| pattern = re.compile(r"(##|\b)%s(##|\b)" % arg) |
| for i in range(len(self.lines)): |
| self.lines[i] = re.sub(pattern, "%%(%s)s" % arg, self.lines[i]) |
| |
| def FillIn(self, arg_values): |
| filler = {} |
| assert len(arg_values) == len(self.args) |
| for i in range(len(self.args)): |
| filler[self.args[i]] = arg_values[i] |
| result = [] |
| for line in self.lines: |
| result.append(line % filler) |
| return result |
| |
| |
| def ReadFileAndExpandMacros(filename): |
| found_macros = {} |
| expanded_lines = [] |
| with open(filename, "r") as f: |
| found_macro = None |
| for line in f: |
| if found_macro is not None: |
| found_macro.AddLine(line) |
| if not line.endswith("\\\n"): |
| found_macro.Finalize() |
| found_macro = None |
| continue |
| |
| match = MACRO.match(line) |
| if match: |
| found_macro = Macro(match) |
| if found_macro.name in EXPAND_MACROS: |
| found_macros[found_macro.name] = found_macro |
| else: |
| found_macro = None |
| continue |
| |
| match = FIRST_WORD.match(line) |
| if match: |
| first_word = match.group(1) |
| if first_word in found_macros: |
| MACRO_CALL = re.compile("%s\(([^)]*)\)" % first_word) |
| match = MACRO_CALL.match(line) |
| assert match |
| args = [s.strip() for s in match.group(1).split(",")] |
| expanded_lines += found_macros[first_word].FillIn(args) |
| continue |
| |
| expanded_lines.append(line) |
| return expanded_lines |
| |
| |
| # Detects runtime functions by parsing FILENAME. |
| def FindRuntimeFunctions(): |
| functions = [] |
| expanded_lines = ReadFileAndExpandMacros(FILENAME) |
| function = None |
| partial_line = "" |
| for line in expanded_lines: |
| # Multi-line definition support, ignoring macros. |
| if line.startswith("RUNTIME_FUNCTION") and not line.endswith("{\n"): |
| if line.endswith("\\\n"): continue |
| partial_line = line.rstrip() |
| continue |
| if partial_line: |
| partial_line += " " + line.strip() |
| if partial_line.endswith("{"): |
| line = partial_line |
| partial_line = "" |
| else: |
| continue |
| |
| match = FUNCTION.match(line) |
| if match: |
| function = Function(match) |
| continue |
| if function is None: continue |
| |
| if line == FUNCTIONEND: |
| if function is not None: |
| functions.append(function) |
| function = None |
| return functions |
| |
| |
| class Builtin(object): |
| def __init__(self, match): |
| self.name = match.group(1) |
| |
| |
| def FindJSNatives(): |
| PATH = "src" |
| fileslist = [] |
| for (root, dirs, files) in os.walk(PATH): |
| for f in files: |
| if f.endswith(".js"): |
| fileslist.append(os.path.join(root, f)) |
| natives = [] |
| regexp = re.compile("^function (\w+)\s*\((.*?)\) {") |
| matches = 0 |
| for filename in fileslist: |
| with open(filename, "r") as f: |
| file_contents = f.read() |
| file_contents = js2c.ExpandInlineMacros(file_contents) |
| lines = file_contents.split("\n") |
| partial_line = "" |
| for line in lines: |
| if line.startswith("function") and not '{' in line: |
| partial_line += line.rstrip() |
| continue |
| if partial_line: |
| partial_line += " " + line.strip() |
| if '{' in line: |
| line = partial_line |
| partial_line = "" |
| else: |
| continue |
| match = regexp.match(line) |
| if match: |
| natives.append(Builtin(match)) |
| return natives |
| |
| |
| def Main(): |
| functions = FindRuntimeFunctions() |
| natives = FindJSNatives() |
| errors = 0 |
| runtime_map = {} |
| for f in functions: |
| runtime_map[f.name] = 1 |
| for b in natives: |
| if b.name in runtime_map: |
| print("JS_Native/Runtime_Function name clash: %s" % b.name) |
| errors += 1 |
| |
| if errors > 0: |
| return 1 |
| print("Runtime/Natives name clashes: checked %d/%d functions, all good." % |
| (len(functions), len(natives))) |
| return 0 |
| |
| |
| if __name__ == "__main__": |
| sys.exit(Main()) |