Add builtin detector to generate-runtime-tests.py
authorjkummerow@chromium.org <jkummerow@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 16 May 2014 13:23:32 +0000 (13:23 +0000)
committerjkummerow@chromium.org <jkummerow@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 16 May 2014 13:23:32 +0000 (13:23 +0000)
R=dslomov@chromium.org

Review URL: https://codereview.chromium.org/283403002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21342 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/v8natives.js
tools/generate-runtime-tests.py

index 46dfab4..2e76162 100644 (file)
@@ -1886,6 +1886,9 @@ function RunMicrotasksJS() {
 }
 
 function EnqueueMicrotask(fn) {
+  if (!IS_FUNCTION(fn)) {
+    throw new $TypeError('Can only enqueue functions');
+  }
   var microtaskState = %GetMicrotaskState();
   if (IS_UNDEFINED(microtaskState.queue) || IS_NULL(microtaskState.queue)) {
     microtaskState.queue = new InternalArray;
index 8770aa2..429271a 100755 (executable)
@@ -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.")