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. |
+ 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. |