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