Chromium Code Reviews| Index: tools/generate-runtime-tests.py |
| diff --git a/tools/generate-runtime-tests.py b/tools/generate-runtime-tests.py |
| index 5ce4c9efddc2485dc8b60a2abd61e37834e7d45d..8770aa2916b51c76e49ec2e241fe782dac4714bf 100755 |
| --- a/tools/generate-runtime-tests.py |
| +++ b/tools/generate-runtime-tests.py |
| @@ -16,24 +16,38 @@ import subprocess |
| import sys |
| import time |
| -# TODO(jkummerow): Support DATA_VIEW_{G,S}ETTER in runtime.cc |
| - |
| FILENAME = "src/runtime.cc" |
| HEADERFILENAME = "src/runtime.h" |
| FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)") |
| ARGSLENGTH = re.compile(".*ASSERT\(.*args\.length\(\) == (\d+)\);") |
| FUNCTIONEND = "}\n" |
| +MACRO = re.compile(r"^#define ([^ ]+)\(([^)]*)\) *([^\\]*)\\?\n$") |
| +FIRST_WORD = re.compile("^\s*(.*?)[\s({\[]") |
| WORKSPACE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")) |
| BASEPATH = os.path.join(WORKSPACE, "test", "mjsunit", "runtime-gen") |
| THIS_SCRIPT = os.path.relpath(sys.argv[0]) |
| +# Expand these macros, they define further runtime functions. |
| +EXPAND_MACROS = [ |
| + "BUFFER_VIEW_GETTER", |
| + "DATA_VIEW_GETTER", |
| + "DATA_VIEW_SETTER", |
| + "RUNTIME_UNARY_MATH", |
| +] |
| +# TODO(jkummerow): We could also whitelist the following macros, but the |
| +# functions they define are so trivial that it's unclear how much benefit |
| +# that would provide: |
| +# ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION |
| +# FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION |
| +# TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION |
| + |
| # Counts of functions in each detection state. These are used to assert |
| # that the parser doesn't bit-rot. Change the values as needed when you add, |
| # remove or change runtime functions, but make sure we don't lose our ability |
| # to parse them! |
| -EXPECTED_FUNCTION_COUNT = 338 |
| -EXPECTED_FUZZABLE_COUNT = 305 |
| +EXPECTED_FUNCTION_COUNT = 362 |
| +EXPECTED_FUZZABLE_COUNT = 329 |
| EXPECTED_CCTEST_COUNT = 6 |
| EXPECTED_UNKNOWN_COUNT = 5 |
| @@ -580,6 +594,12 @@ class Generator(object): |
| "new %sArray(%s)" % (arraytype, ", ".join(args)), |
| fallback="new %sArray(8)" % arraytype)) |
| + def _JSArrayBufferView(self, name, recursion_budget): |
| + if random.random() < 0.4: |
| + return self._JSDataView(name, recursion_budget) |
| + else: |
| + return self._JSTypedArray(name, recursion_budget) |
| + |
| def _JSWeakCollection(self, name, recursion_budget): |
| ctor = random.choice([self._JSMap, self._JSSet]) |
| return ctor(name, recursion_budget, weak="Weak") |
| @@ -634,7 +654,8 @@ class Generator(object): |
| "Int32": ["32", _Int32], |
| "JSArray": ["new Array()", _JSArray], |
| "JSArrayBuffer": ["new ArrayBuffer(8)", _JSArrayBuffer], |
| - "JSDataView": ["new DataView(new ArrayBuffer(8))", _JSDataView], |
| + "JSArrayBufferView": ["new Int32Array(2)", _JSArrayBufferView], |
| + "JSDataView": ["new DataView(new ArrayBuffer(24))", _JSDataView], |
| "JSDate": ["new Date()", _JSDate], |
| "JSFunction": ["function() {}", _JSFunction], |
| "JSFunctionProxy": ["Proxy.createFunction({}, function() {})", |
| @@ -757,6 +778,45 @@ class Function(object): |
| return "".join(s) |
| +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 |
| + |
| + |
| # Parses HEADERFILENAME to find out which runtime functions are "inline". |
| def FindInlineRuntimeFunctions(): |
| inline_functions = [] |
| @@ -778,47 +838,84 @@ def FindInlineRuntimeFunctions(): |
| return inline_functions |
| -# Detects runtime functions by parsing FILENAME. |
| -def FindRuntimeFunctions(): |
| - inline_functions = FindInlineRuntimeFunctions() |
| - functions = [] |
| - with open(FILENAME, "r") as f: |
| - function = None |
| - partial_line = "" |
| +def ReadFileAndExpandMacros(filename): |
| + found_macros = {} |
| + expanded_lines = [] |
| + with open(filename, "r") as f: |
| + found_macro = None |
| for line in f: |
| - # 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() |
| + if found_macro is not None: |
| + found_macro.AddLine(line) |
| + if not line.endswith("\\\n"): |
| + found_macro.Finalize() |
| + found_macro = None |
| continue |
| - if partial_line: |
| - partial_line += " " + line.strip() |
| - if partial_line.endswith("{"): |
| - line = partial_line |
| - partial_line = "" |
| - else: |
| - continue |
| - match = FUNCTION.match(line) |
| + match = MACRO.match(line) |
| if match: |
| - function = Function(match) |
| - if function.name in inline_functions: |
| - function.inline = "_" |
| + found_macro = Macro(match) |
| + if found_macro.name in EXPAND_MACROS: |
| + found_macros[found_macro.name] = found_macro |
| + else: |
| + found_macro = None |
| continue |
| - if function is None: continue |
| - match = ARGSLENGTH.match(line) |
| + match = FIRST_WORD.match(line) |
| if match: |
| - function.SetArgsLength(match) |
| - continue |
| + 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 |
| + |
| - if function.TryParseArg(line): |
| +# Detects runtime functions by parsing FILENAME. |
| +def FindRuntimeFunctions(): |
| + inline_functions = FindInlineRuntimeFunctions() |
| + functions = [] |
| + expanded_lines = ReadFileAndExpandMacros(FILENAME) |
| + function = None |
| + partial_line = "" |
| + for line in expanded_lines: |
| + # Multi-line definition support, ignoring macros. |
|
Jakob Kummerow
2014/05/15 13:59:30
From here on downwards in this function, only inde
|
| + 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 |
| - if line == FUNCTIONEND: |
| - if function is not None: |
| - functions.append(function) |
| - function = None |
| + match = FUNCTION.match(line) |
| + if match: |
| + function = Function(match) |
| + if function.name in inline_functions: |
| + function.inline = "_" |
| + continue |
| + if function is None: continue |
| + |
| + match = ARGSLENGTH.match(line) |
| + if match: |
| + function.SetArgsLength(match) |
| + continue |
| + |
| + if function.TryParseArg(line): |
| + continue |
| + |
| + if line == FUNCTIONEND: |
| + if function is not None: |
| + functions.append(function) |
| + function = None |
| return functions |
| # Classifies runtime functions. |