OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 the V8 project authors. All rights reserved. | 2 # Copyright 2014 the V8 project authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import itertools | 6 import itertools |
7 import multiprocessing | 7 import multiprocessing |
8 import optparse | 8 import optparse |
9 import os | 9 import os |
10 import random | 10 import random |
11 import re | 11 import re |
12 import shutil | 12 import shutil |
13 import signal | 13 import signal |
14 import string | 14 import string |
15 import subprocess | 15 import subprocess |
16 import sys | 16 import sys |
17 import time | 17 import time |
18 | 18 |
19 # TODO(jkummerow): Support DATA_VIEW_{G,S}ETTER in runtime.cc | |
20 | |
21 FILENAME = "src/runtime.cc" | 19 FILENAME = "src/runtime.cc" |
22 HEADERFILENAME = "src/runtime.h" | 20 HEADERFILENAME = "src/runtime.h" |
23 FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)") | 21 FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)") |
24 ARGSLENGTH = re.compile(".*ASSERT\(.*args\.length\(\) == (\d+)\);") | 22 ARGSLENGTH = re.compile(".*ASSERT\(.*args\.length\(\) == (\d+)\);") |
25 FUNCTIONEND = "}\n" | 23 FUNCTIONEND = "}\n" |
| 24 MACRO = re.compile(r"^#define ([^ ]+)\(([^)]*)\) *([^\\]*)\\?\n$") |
| 25 FIRST_WORD = re.compile("^\s*(.*?)[\s({\[]") |
26 | 26 |
27 WORKSPACE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")) | 27 WORKSPACE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")) |
28 BASEPATH = os.path.join(WORKSPACE, "test", "mjsunit", "runtime-gen") | 28 BASEPATH = os.path.join(WORKSPACE, "test", "mjsunit", "runtime-gen") |
29 THIS_SCRIPT = os.path.relpath(sys.argv[0]) | 29 THIS_SCRIPT = os.path.relpath(sys.argv[0]) |
30 | 30 |
| 31 # Expand these macros, they define further runtime functions. |
| 32 EXPAND_MACROS = [ |
| 33 "BUFFER_VIEW_GETTER", |
| 34 "DATA_VIEW_GETTER", |
| 35 "DATA_VIEW_SETTER", |
| 36 "RUNTIME_UNARY_MATH", |
| 37 ] |
| 38 # TODO(jkummerow): We could also whitelist the following macros, but the |
| 39 # functions they define are so trivial that it's unclear how much benefit |
| 40 # that would provide: |
| 41 # ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION |
| 42 # FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION |
| 43 # TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION |
| 44 |
31 # Counts of functions in each detection state. These are used to assert | 45 # Counts of functions in each detection state. These are used to assert |
32 # that the parser doesn't bit-rot. Change the values as needed when you add, | 46 # that the parser doesn't bit-rot. Change the values as needed when you add, |
33 # remove or change runtime functions, but make sure we don't lose our ability | 47 # remove or change runtime functions, but make sure we don't lose our ability |
34 # to parse them! | 48 # to parse them! |
35 EXPECTED_FUNCTION_COUNT = 338 | 49 EXPECTED_FUNCTION_COUNT = 362 |
36 EXPECTED_FUZZABLE_COUNT = 305 | 50 EXPECTED_FUZZABLE_COUNT = 329 |
37 EXPECTED_CCTEST_COUNT = 6 | 51 EXPECTED_CCTEST_COUNT = 6 |
38 EXPECTED_UNKNOWN_COUNT = 5 | 52 EXPECTED_UNKNOWN_COUNT = 5 |
39 | 53 |
40 | 54 |
41 # Don't call these at all. | 55 # Don't call these at all. |
42 BLACKLISTED = [ | 56 BLACKLISTED = [ |
43 "Abort", # Kills the process. | 57 "Abort", # Kills the process. |
44 "AbortJS", # Kills the process. | 58 "AbortJS", # Kills the process. |
45 "CompileForOnStackReplacement", # Riddled with ASSERTs. | 59 "CompileForOnStackReplacement", # Riddled with ASSERTs. |
46 "IS_VAR", # Not implemented in the runtime. | 60 "IS_VAR", # Not implemented in the runtime. |
(...skipping 526 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
573 result += self._Int32(offset_name) | 587 result += self._Int32(offset_name) |
574 if die < 0.33: | 588 if die < 0.33: |
575 length_name = name + "_length" | 589 length_name = name + "_length" |
576 args.append(length_name) | 590 args.append(length_name) |
577 result += self._Int32(length_name) | 591 result += self._Int32(length_name) |
578 return (result + | 592 return (result + |
579 self._Variable(name, | 593 self._Variable(name, |
580 "new %sArray(%s)" % (arraytype, ", ".join(args)), | 594 "new %sArray(%s)" % (arraytype, ", ".join(args)), |
581 fallback="new %sArray(8)" % arraytype)) | 595 fallback="new %sArray(8)" % arraytype)) |
582 | 596 |
| 597 def _JSArrayBufferView(self, name, recursion_budget): |
| 598 if random.random() < 0.4: |
| 599 return self._JSDataView(name, recursion_budget) |
| 600 else: |
| 601 return self._JSTypedArray(name, recursion_budget) |
| 602 |
583 def _JSWeakCollection(self, name, recursion_budget): | 603 def _JSWeakCollection(self, name, recursion_budget): |
584 ctor = random.choice([self._JSMap, self._JSSet]) | 604 ctor = random.choice([self._JSMap, self._JSSet]) |
585 return ctor(name, recursion_budget, weak="Weak") | 605 return ctor(name, recursion_budget, weak="Weak") |
586 | 606 |
587 def _PropertyDetails(self, name, recursion_budget): | 607 def _PropertyDetails(self, name, recursion_budget): |
588 # TODO(jkummerow): Be more clever here? | 608 # TODO(jkummerow): Be more clever here? |
589 return self._Int32(name) | 609 return self._Int32(name) |
590 | 610 |
591 def _JSObject(self, name, recursion_budget): | 611 def _JSObject(self, name, recursion_budget): |
592 die = random.random() | 612 die = random.random() |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
627 if random.random() < 0.2: | 647 if random.random() < 0.2: |
628 return self._Smi(name, recursion_budget) | 648 return self._Smi(name, recursion_budget) |
629 return self._HeapObject(name, recursion_budget) | 649 return self._HeapObject(name, recursion_budget) |
630 | 650 |
631 GENERATORS = { | 651 GENERATORS = { |
632 "Boolean": ["true", _Boolean], | 652 "Boolean": ["true", _Boolean], |
633 "HeapObject": ["new Object()", _HeapObject], | 653 "HeapObject": ["new Object()", _HeapObject], |
634 "Int32": ["32", _Int32], | 654 "Int32": ["32", _Int32], |
635 "JSArray": ["new Array()", _JSArray], | 655 "JSArray": ["new Array()", _JSArray], |
636 "JSArrayBuffer": ["new ArrayBuffer(8)", _JSArrayBuffer], | 656 "JSArrayBuffer": ["new ArrayBuffer(8)", _JSArrayBuffer], |
637 "JSDataView": ["new DataView(new ArrayBuffer(8))", _JSDataView], | 657 "JSArrayBufferView": ["new Int32Array(2)", _JSArrayBufferView], |
| 658 "JSDataView": ["new DataView(new ArrayBuffer(24))", _JSDataView], |
638 "JSDate": ["new Date()", _JSDate], | 659 "JSDate": ["new Date()", _JSDate], |
639 "JSFunction": ["function() {}", _JSFunction], | 660 "JSFunction": ["function() {}", _JSFunction], |
640 "JSFunctionProxy": ["Proxy.createFunction({}, function() {})", | 661 "JSFunctionProxy": ["Proxy.createFunction({}, function() {})", |
641 _JSFunctionProxy], | 662 _JSFunctionProxy], |
642 "JSGeneratorObject": ["(function*(){ yield 1; })()", _JSGeneratorObject], | 663 "JSGeneratorObject": ["(function*(){ yield 1; })()", _JSGeneratorObject], |
643 "JSMap": ["new Map()", _JSMap], | 664 "JSMap": ["new Map()", _JSMap], |
644 "JSMapIterator": ["%MapCreateIterator(new Map(), 3)", _JSMapIterator], | 665 "JSMapIterator": ["%MapCreateIterator(new Map(), 3)", _JSMapIterator], |
645 "JSObject": ["new Object()", _JSObject], | 666 "JSObject": ["new Object()", _JSObject], |
646 "JSProxy": ["Proxy.create({})", _JSProxy], | 667 "JSProxy": ["Proxy.create({})", _JSProxy], |
647 "JSReceiver": ["new Object()", _JSReceiver], | 668 "JSReceiver": ["new Object()", _JSReceiver], |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
750 argcount = max([self.args[i].index + 1 for i in self.args]) | 771 argcount = max([self.args[i].index + 1 for i in self.args]) |
751 else: | 772 else: |
752 argcount = 0 | 773 argcount = 0 |
753 for i in range(argcount): | 774 for i in range(argcount): |
754 if i > 0: s.append(", ") | 775 if i > 0: s.append(", ") |
755 s.append(self.args[i].type if i in self.args else "<unknown>") | 776 s.append(self.args[i].type if i in self.args else "<unknown>") |
756 s.append(")") | 777 s.append(")") |
757 return "".join(s) | 778 return "".join(s) |
758 | 779 |
759 | 780 |
| 781 class Macro(object): |
| 782 def __init__(self, match): |
| 783 self.name = match.group(1) |
| 784 self.args = [s.strip() for s in match.group(2).split(",")] |
| 785 self.lines = [] |
| 786 self.indentation = 0 |
| 787 self.AddLine(match.group(3)) |
| 788 |
| 789 def AddLine(self, line): |
| 790 if not line: return |
| 791 if not self.lines: |
| 792 # This is the first line, detect indentation. |
| 793 self.indentation = len(line) - len(line.lstrip()) |
| 794 line = line.rstrip("\\\n ") |
| 795 if not line: return |
| 796 assert len(line[:self.indentation].strip()) == 0, \ |
| 797 ("expected whitespace: '%s', full line: '%s'" % |
| 798 (line[:self.indentation], line)) |
| 799 line = line[self.indentation:] |
| 800 if not line: return |
| 801 self.lines.append(line + "\n") |
| 802 |
| 803 def Finalize(self): |
| 804 for arg in self.args: |
| 805 pattern = re.compile(r"(##|\b)%s(##|\b)" % arg) |
| 806 for i in range(len(self.lines)): |
| 807 self.lines[i] = re.sub(pattern, "%%(%s)s" % arg, self.lines[i]) |
| 808 |
| 809 def FillIn(self, arg_values): |
| 810 filler = {} |
| 811 assert len(arg_values) == len(self.args) |
| 812 for i in range(len(self.args)): |
| 813 filler[self.args[i]] = arg_values[i] |
| 814 result = [] |
| 815 for line in self.lines: |
| 816 result.append(line % filler) |
| 817 return result |
| 818 |
| 819 |
760 # Parses HEADERFILENAME to find out which runtime functions are "inline". | 820 # Parses HEADERFILENAME to find out which runtime functions are "inline". |
761 def FindInlineRuntimeFunctions(): | 821 def FindInlineRuntimeFunctions(): |
762 inline_functions = [] | 822 inline_functions = [] |
763 with open(HEADERFILENAME, "r") as f: | 823 with open(HEADERFILENAME, "r") as f: |
764 inline_list = "#define INLINE_FUNCTION_LIST(F) \\\n" | 824 inline_list = "#define INLINE_FUNCTION_LIST(F) \\\n" |
765 inline_opt_list = "#define INLINE_OPTIMIZED_FUNCTION_LIST(F) \\\n" | 825 inline_opt_list = "#define INLINE_OPTIMIZED_FUNCTION_LIST(F) \\\n" |
766 inline_function = re.compile(r"^\s*F\((\w+), \d+, \d+\)\s*\\?") | 826 inline_function = re.compile(r"^\s*F\((\w+), \d+, \d+\)\s*\\?") |
767 mode = "SEARCHING" | 827 mode = "SEARCHING" |
768 for line in f: | 828 for line in f: |
769 if mode == "ACTIVE": | 829 if mode == "ACTIVE": |
770 match = inline_function.match(line) | 830 match = inline_function.match(line) |
771 if match: | 831 if match: |
772 inline_functions.append(match.group(1)) | 832 inline_functions.append(match.group(1)) |
773 if not line.endswith("\\\n"): | 833 if not line.endswith("\\\n"): |
774 mode = "SEARCHING" | 834 mode = "SEARCHING" |
775 elif mode == "SEARCHING": | 835 elif mode == "SEARCHING": |
776 if line == inline_list or line == inline_opt_list: | 836 if line == inline_list or line == inline_opt_list: |
777 mode = "ACTIVE" | 837 mode = "ACTIVE" |
778 return inline_functions | 838 return inline_functions |
779 | 839 |
780 | 840 |
| 841 def ReadFileAndExpandMacros(filename): |
| 842 found_macros = {} |
| 843 expanded_lines = [] |
| 844 with open(filename, "r") as f: |
| 845 found_macro = None |
| 846 for line in f: |
| 847 if found_macro is not None: |
| 848 found_macro.AddLine(line) |
| 849 if not line.endswith("\\\n"): |
| 850 found_macro.Finalize() |
| 851 found_macro = None |
| 852 continue |
| 853 |
| 854 match = MACRO.match(line) |
| 855 if match: |
| 856 found_macro = Macro(match) |
| 857 if found_macro.name in EXPAND_MACROS: |
| 858 found_macros[found_macro.name] = found_macro |
| 859 else: |
| 860 found_macro = None |
| 861 continue |
| 862 |
| 863 match = FIRST_WORD.match(line) |
| 864 if match: |
| 865 first_word = match.group(1) |
| 866 if first_word in found_macros: |
| 867 MACRO_CALL = re.compile("%s\(([^)]*)\)" % first_word) |
| 868 match = MACRO_CALL.match(line) |
| 869 assert match |
| 870 args = [s.strip() for s in match.group(1).split(",")] |
| 871 expanded_lines += found_macros[first_word].FillIn(args) |
| 872 continue |
| 873 |
| 874 expanded_lines.append(line) |
| 875 return expanded_lines |
| 876 |
| 877 |
781 # Detects runtime functions by parsing FILENAME. | 878 # Detects runtime functions by parsing FILENAME. |
782 def FindRuntimeFunctions(): | 879 def FindRuntimeFunctions(): |
783 inline_functions = FindInlineRuntimeFunctions() | 880 inline_functions = FindInlineRuntimeFunctions() |
784 functions = [] | 881 functions = [] |
785 with open(FILENAME, "r") as f: | 882 expanded_lines = ReadFileAndExpandMacros(FILENAME) |
786 function = None | 883 function = None |
787 partial_line = "" | 884 partial_line = "" |
788 for line in f: | 885 for line in expanded_lines: |
789 # Multi-line definition support, ignoring macros. | 886 # Multi-line definition support, ignoring macros. |
790 if line.startswith("RUNTIME_FUNCTION") and not line.endswith("{\n"): | 887 if line.startswith("RUNTIME_FUNCTION") and not line.endswith("{\n"): |
791 if line.endswith("\\\n"): continue | 888 if line.endswith("\\\n"): continue |
792 partial_line = line.rstrip() | 889 partial_line = line.rstrip() |
793 continue | 890 continue |
794 if partial_line: | 891 if partial_line: |
795 partial_line += " " + line.strip() | 892 partial_line += " " + line.strip() |
796 if partial_line.endswith("{"): | 893 if partial_line.endswith("{"): |
797 line = partial_line | 894 line = partial_line |
798 partial_line = "" | 895 partial_line = "" |
799 else: | 896 else: |
800 continue | |
801 | |
802 match = FUNCTION.match(line) | |
803 if match: | |
804 function = Function(match) | |
805 if function.name in inline_functions: | |
806 function.inline = "_" | |
807 continue | |
808 if function is None: continue | |
809 | |
810 match = ARGSLENGTH.match(line) | |
811 if match: | |
812 function.SetArgsLength(match) | |
813 continue | 897 continue |
814 | 898 |
815 if function.TryParseArg(line): | 899 match = FUNCTION.match(line) |
816 continue | 900 if match: |
| 901 function = Function(match) |
| 902 if function.name in inline_functions: |
| 903 function.inline = "_" |
| 904 continue |
| 905 if function is None: continue |
817 | 906 |
818 if line == FUNCTIONEND: | 907 match = ARGSLENGTH.match(line) |
819 if function is not None: | 908 if match: |
820 functions.append(function) | 909 function.SetArgsLength(match) |
821 function = None | 910 continue |
| 911 |
| 912 if function.TryParseArg(line): |
| 913 continue |
| 914 |
| 915 if line == FUNCTIONEND: |
| 916 if function is not None: |
| 917 functions.append(function) |
| 918 function = None |
822 return functions | 919 return functions |
823 | 920 |
824 # Classifies runtime functions. | 921 # Classifies runtime functions. |
825 def ClassifyFunctions(functions): | 922 def ClassifyFunctions(functions): |
826 # Can be fuzzed with a JavaScript testcase. | 923 # Can be fuzzed with a JavaScript testcase. |
827 js_fuzzable_functions = [] | 924 js_fuzzable_functions = [] |
828 # We have enough information to fuzz these, but they need inputs that | 925 # We have enough information to fuzz these, but they need inputs that |
829 # cannot be created or passed around in JavaScript. | 926 # cannot be created or passed around in JavaScript. |
830 cctest_fuzzable_functions = [] | 927 cctest_fuzzable_functions = [] |
831 # This script does not have enough information about these. | 928 # This script does not have enough information about these. |
(...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1122 for i in range(len(processes)): | 1219 for i in range(len(processes)): |
1123 processes[i].join() | 1220 processes[i].join() |
1124 except KeyboardInterrupt: | 1221 except KeyboardInterrupt: |
1125 stop_running.set() | 1222 stop_running.set() |
1126 for i in range(len(processes)): | 1223 for i in range(len(processes)): |
1127 processes[i].join() | 1224 processes[i].join() |
1128 return 0 | 1225 return 0 |
1129 | 1226 |
1130 if __name__ == "__main__": | 1227 if __name__ == "__main__": |
1131 sys.exit(Main()) | 1228 sys.exit(Main()) |
OLD | NEW |