Index: tools/generate-runtime-tests.py |
diff --git a/tools/generate-runtime-tests.py b/tools/generate-runtime-tests.py |
index 8770aa2916b51c76e49ec2e241fe782dac4714bf..429271a1d193ed0453692f88309a93546c51adf4 100755 |
--- a/tools/generate-runtime-tests.py |
+++ b/tools/generate-runtime-tests.py |
@@ -4,6 +4,7 @@ |
# found in the LICENSE file. |
import itertools |
+import js2c |
import multiprocessing |
import optparse |
import os |
@@ -50,6 +51,7 @@ EXPECTED_FUNCTION_COUNT = 362 |
EXPECTED_FUZZABLE_COUNT = 329 |
EXPECTED_CCTEST_COUNT = 6 |
EXPECTED_UNKNOWN_COUNT = 5 |
+EXPECTED_BUILTINS_COUNT = 827 |
# Don't call these at all. |
@@ -298,10 +300,6 @@ class Generator(object): |
return self._Variable(name, |
"\"%s\" + (function() { return \"%s\";})()" % (s1, s2)) |
- def _ExternalString(self, name): |
- # Needs --expose-externalize-string. |
- return None |
- |
def _InternalizedString(self, name): |
return self._Variable(name, "\"%s\"" % self._RawRandomString(0, 20)) |
@@ -918,6 +916,60 @@ def FindRuntimeFunctions(): |
function = None |
return functions |
+ |
+# Hack: This must have the same fields as class Function above, because the |
+# two are used polymorphically in RunFuzzer(). We could use inheritance... |
+class Builtin(object): |
+ def __init__(self, match): |
+ self.name = match.group(1) |
+ args = match.group(2) |
+ self.argslength = 0 if args == "" else args.count(",") + 1 |
+ self.inline = "" |
+ self.args = {} |
+ if self.argslength > 0: |
+ args = args.split(",") |
+ for i in range(len(args)): |
+ # a = args[i].strip() # TODO: filter out /* comments */ first. |
+ a = "" |
+ self.args[i] = Arg("Object", a, i) |
+ |
+ def __str__(self): |
+ return "%s(%d)" % (self.name, self.argslength) |
+ |
+ |
+def FindJSBuiltins(): |
+ 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)) |
+ builtins = [] |
+ 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: |
+ builtins.append(Builtin(match)) |
+ return builtins |
+ |
+ |
# Classifies runtime functions. |
def ClassifyFunctions(functions): |
# Can be fuzzed with a JavaScript testcase. |
@@ -1018,7 +1070,23 @@ def _SaveFileName(save_path, process_id, save_file_index): |
return "%s/fuzz_%d_%d.js" % (save_path, process_id, save_file_index) |
+def _GetFuzzableRuntimeFunctions(): |
+ functions = FindRuntimeFunctions() |
+ (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \ |
+ ClassifyFunctions(functions) |
+ return js_fuzzable_functions |
+ |
+ |
+FUZZ_TARGET_LISTS = { |
+ "runtime": _GetFuzzableRuntimeFunctions, |
+ "builtins": FindJSBuiltins, |
+} |
+ |
+ |
def RunFuzzer(process_id, options, stop_running): |
+ MAX_SLEEP_TIME = 0.1 |
+ INITIAL_SLEEP_TIME = 0.001 |
+ SLEEP_TIME_FACTOR = 1.25 |
base_file_name = "/dev/shm/runtime_fuzz_%d" % process_id |
test_file_name = "%s.js" % base_file_name |
stderr_file_name = "%s.out" % base_file_name |
@@ -1026,19 +1094,14 @@ def RunFuzzer(process_id, options, stop_running): |
while os.path.exists(_SaveFileName(options.save_path, process_id, |
save_file_index)): |
save_file_index += 1 |
- MAX_SLEEP_TIME = 0.1 |
- INITIAL_SLEEP_TIME = 0.001 |
- SLEEP_TIME_FACTOR = 1.5 |
- |
- functions = FindRuntimeFunctions() |
- (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \ |
- ClassifyFunctions(functions) |
+ targets = FUZZ_TARGET_LISTS[options.fuzz_target]() |
try: |
for i in range(options.num_tests): |
if stop_running.is_set(): break |
- function = random.choice(js_fuzzable_functions) # TODO: others too |
- if function.argslength == 0: continue |
+ function = None |
+ while function is None or function.argslength == 0: |
+ function = random.choice(targets) |
args = [] |
definitions = [] |
gen = Generator() |
@@ -1086,11 +1149,11 @@ def RunFuzzer(process_id, options, stop_running): |
save_file_index += 1 |
except KeyboardInterrupt: |
stop_running.set() |
- except Exception, e: |
- print e |
finally: |
- os.remove(test_file_name) |
- os.remove(stderr_file_name) |
+ if os.path.exists(test_file_name): |
+ os.remove(test_file_name) |
+ if os.path.exists(stderr_file_name): |
+ os.remove(stderr_file_name) |
def BuildOptionParser(): |
@@ -1110,6 +1173,9 @@ fuzz Generate fuzz tests, run them, save those that crashed (see options). |
o = optparse.OptionParser(usage=usage) |
o.add_option("--binary", default="out/x64.debug/d8", |
help="d8 binary used for running fuzz tests (default: %default)") |
+ o.add_option("--fuzz-target", default="runtime", |
+ help="Set of functions targeted by fuzzing. Allowed values: " |
+ "%s (default: %%default)" % ", ".join(FUZZ_TARGET_LISTS)) |
o.add_option("-n", "--num-tests", default=1000, type="int", |
help="Number of fuzz tests to generate per worker process" |
" (default: %default)") |
@@ -1122,12 +1188,21 @@ fuzz Generate fuzz tests, run them, save those that crashed (see options). |
return o |
+def ProcessOptions(options, args): |
+ options.save_path = os.path.expanduser(options.save_path) |
+ if options.fuzz_target not in FUZZ_TARGET_LISTS: |
+ print("Invalid fuzz target: %s" % options.fuzz_target) |
+ return False |
+ if len(args) != 1 or args[0] == "help": |
+ return False |
+ return True |
+ |
+ |
def Main(): |
parser = BuildOptionParser() |
(options, args) = parser.parse_args() |
- options.save_path = os.path.expanduser(options.save_path) |
- if len(args) != 1 or args[0] == "help": |
+ if not ProcessOptions(options, args): |
parser.print_help() |
return 1 |
action = args[0] |
@@ -1135,14 +1210,10 @@ def Main(): |
functions = FindRuntimeFunctions() |
(js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \ |
ClassifyFunctions(functions) |
+ builtins = FindJSBuiltins() |
if action == "test": |
- gen = Generator() |
- vartype = "JSTypedArray" |
- print("simple: %s" % gen.RandomVariable("x", vartype, True)) |
- for i in range(10): |
- print("----") |
- print("%s" % "\n".join(gen.RandomVariable("x", vartype, False))) |
+ print("put your temporary debugging code here") |
return 0 |
if action == "info": |
@@ -1150,6 +1221,7 @@ def Main(): |
"cctest_fuzzable_functions: %d, unknown_functions: %d" |
% (len(functions), len(js_fuzzable_functions), |
len(cctest_fuzzable_functions), len(unknown_functions))) |
+ print("%d JavaScript builtins" % len(builtins)) |
print("unknown functions:") |
for f in unknown_functions: |
print(f) |
@@ -1175,6 +1247,8 @@ def Main(): |
"cctest-fuzzable functions") |
errors += CheckCount(unknown_functions, EXPECTED_UNKNOWN_COUNT, |
"functions with incomplete type information") |
+ errors += CheckCount(builtins, EXPECTED_BUILTINS_COUNT, |
+ "JavaScript builtins") |
def CheckTestcasesExisting(functions): |
errors = 0 |
@@ -1196,6 +1270,19 @@ def Main(): |
errors += CheckTestcasesExisting(js_fuzzable_functions) |
+ def CheckNameClashes(runtime_functions, builtins): |
+ errors = 0 |
+ runtime_map = {} |
+ for f in runtime_functions: |
+ runtime_map[f.name] = 1 |
+ for b in builtins: |
+ if b.name in runtime_map: |
+ print("Builtin/Runtime_Function name clash: %s" % b.name) |
+ errors += 1 |
+ return errors |
+ |
+ errors += CheckNameClashes(functions, builtins) |
+ |
if errors > 0: |
return 1 |
print("Generated runtime tests: all good.") |