forked from WebKit/WebKit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconvert-imported-v8-tests.py
executable file
·303 lines (235 loc) · 11.5 KB
/
convert-imported-v8-tests.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#!/usr/bin/env python3
# Copyright (C) 2022 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This tool aids with the importing of V8 test files into WebKit.
# It performs most of the common substitutions.
import argparse
import os
import re
import sys
import types
from pathlib import Path
FILE_MODE = "File"
LIST_MODE = "List"
RULE_REGEXP_IGNORE = 1
RULE_REGEXP_MANUAL_EDIT = 2
RULE_STRING_REPLACE = 3
RULE_REGEXP_REPLACE = 4
log_file = sys.stderr
files_converted = 0
header = ""
files_loaded_notes = None
files_needing_manual_changes = None
def print_log(*a):
print(*a, file=log_file)
def print_stderr(*a):
print(*a, file=sys.stderr)
def handle_loadfile_replacements(matchobj):
global files_loaded_notes
originalPath = matchobj.group(1)
fileToLoad = matchobj.group(2)
files_loaded_notes.add_note(fileToLoad, originalPath)
return "load(\"{0}\");".format(fileToLoad)
def handle_print_with_parens(matchobj):
possible_func_call = matchobj.group(3)
if possible_func_call:
print("found possible func call \"{0}\" in: {1}".format(possible_func_call, matchobj.group(0)))
else:
return "{white_space}// {print_stmnt}".format(white_space=matchobj.group(1), print_stmnt=matchobj.group(2))
def handle_percent_call(matchobj):
return "call to " + matchobj.group(1) + ")"
rules = [
(RULE_REGEXP_IGNORE, r'^\s*\/\/'),
(RULE_REGEXP_REPLACE, r'd8\.file\.execute\s*\("((?:[^\/]+\/)*([^"]+))"\)(?:;)?', handle_loadfile_replacements),
(RULE_REGEXP_REPLACE, r'd8\.file\.execute\s*\(\'((?:[^\/]+\/)*([^\']+))\'\)(?:;)?', handle_loadfile_replacements),
(RULE_REGEXP_REPLACE, r'^(\s*)(print\([^\(]+\);?)', r'\1// \2'),
(RULE_REGEXP_REPLACE, r'^(\s*)(print\(\'[^\']+\'\);?)', r'\1// \2'),
(RULE_REGEXP_REPLACE, r'^(\s*)(print\(\"[^\"]+\"\);?)', r'\1// \2'),
(RULE_REGEXP_REPLACE, r'^(\s*)(print\(.*([_a-zA-Z][_0-9a-zA-Z]\()?.*\);?)', handle_print_with_parens),
(RULE_REGEXP_MANUAL_EDIT, r'^\s*print\(', "call to print()"),
(RULE_REGEXP_MANUAL_EDIT, r'console.info', "call to console.info()"),
(RULE_REGEXP_MANUAL_EDIT, r'd8\.debugger\.enable\(\)', "call to db.debugger.enable()"),
(RULE_REGEXP_MANUAL_EDIT, r'd8\.profiler\.', "call to db.profiler.*()"),
(RULE_REGEXP_MANUAL_EDIT, r'(%[_a-zA-Z][_0-9a-zA-Z]*\()', handle_percent_call)]
def parse_args():
global header
parser = argparse.ArgumentParser(description='Process V8 test files for inclusion in WebKit.',
usage='%(prog)s [-f] [-v] [-h <header-string>] (-s <source-file> -d <dest-file> | -l <file-with-list-of-source-files> [-o <output-dir>] [-S <source-dir>] )')
parser.add_argument('-s', '--source', default=None, type=Path, dest='source_file_name', required=False,
help='source file to convert')
parser.add_argument('-d', '--dest', default=None, type=Path, dest='dest_file_name', required=False,
help='destination file from convert')
parser.add_argument('-H', '--header', default=None, dest='header_string', required=False,
help='String to add to the top of converted files')
parser.add_argument('-l', '--file-list', default=None, type=Path, dest='file_list_file_name', required=False,
help='file containing list of files to convert')
parser.add_argument('-S', '--source-dir', default=None, type=Path, dest='source_dir', required=False,
help='source directory for files in file list')
parser.add_argument('-o', '--output-dir', default='.', type=Path, dest='output_dir', required=False,
help='output directory (defaults to working directory)')
parser.add_argument('-f', '--force', action='store_true', required=False, help='overwrite existing destination files')
parser.add_argument('-v', '--verbose', action='store_true', required=False, help='verbose output')
args = parser.parse_args()
argument_errors = 0
if args.source_file_name is not None and args.dest_file_name is None:
argument_errors += 1
print_stderr("Use of -s option requires the -d option")
if args.source_file_name is None and args.dest_file_name is not None:
argument_errors += 1
print_stderr("Use of -d option requires the -s option")
if args.source_file_name is not None and args.file_list_file_name is not None:
argument_errors += 1
print_stderr("Either use -s and -d options or -l and -o option pairs")
if argument_errors:
parser.print_help()
exit(1)
if args.header_string:
header = args.header_string + "\n"
if args.source_file_name:
args.mode = FILE_MODE
elif args.file_list_file_name:
args.mode = LIST_MODE
return args
class ItemNotes(object):
def __init__(self, title, item_heading, note_heading):
self.notes_by_item = {}
self.note_title = title
self.item_heading = item_heading
self.note_heading = note_heading
self.max_item_length = 0
self.max_note_length = 0
def add_note(self, item, note):
if item not in self.notes_by_item:
self.notes_by_item[item] = []
if len(item) > self.max_item_length:
self.max_item_length = len(item)
if note not in self.notes_by_item[item]:
self.notes_by_item[item].append(note)
if len(note) > self.max_note_length:
self.max_note_length = len(note)
def print_notes(self):
if not len(self.notes_by_item):
return
item_width = round((self.max_item_length + 2) / 5) * 5
item_divider = ""
item_spacer = ""
for i in range(item_width):
item_divider += '-'
item_spacer += ' '
note_divider = ""
for i in range(self.max_note_length):
note_divider += '-'
print(self.note_title)
print("{item_heading:<{item_width}} {note_heading}".format(item_width=item_width, item_heading=self.item_heading, note_heading=self.note_heading))
print("{item_divider} {note_divider}".format(item_divider=item_divider, note_divider=note_divider))
for k, v in sorted(self.notes_by_item.items()):
print("{item:<{width}} ".format(width=item_width, item=k), end="")
note_count = 0
for note in v:
if note_count:
print("{spacer} ".format(spacer=item_spacer), end="")
print("{note}".format(note=note))
note_count += 1
print()
def convert_file(source_file_name, dest_file_name, force_overwrite=False):
global files_converted
global files_needing_manual_changes
files_converted += 1
line_number = 0
dest_open_mode = 'w' if force_overwrite else 'x'
try:
buffer = ""
manual_issues = set()
with open(source_file_name, 'r') as src_file:
for line in src_file:
line_number += 1
original_line = line
for rule in rules:
type = rule[0]
if type == RULE_REGEXP_IGNORE:
if re.search(rule[1], line):
break
elif type == RULE_REGEXP_MANUAL_EDIT:
matchobj = re.search(rule[1], line)
if matchobj:
reason = None
if isinstance(rule[2], str):
reason = rule[2]
if isinstance(rule[2], types.FunctionType):
reason = rule[2](matchobj)
files_needing_manual_changes.add_note(str(source_file_name.name),
"Line {line_number:>4}, Found {reason}".format(line_number=line_number, reason=reason))
manual_issues.add("// {reason}\n".format(reason=reason))
break
elif type == RULE_STRING_REPLACE:
line = line.replace(rule[1], rule[2])
elif type == RULE_REGEXP_REPLACE:
line = re.sub(rule[1], rule[2], line)
if line != original_line:
break
buffer += line
with open(dest_file_name, dest_open_mode) as dest_file:
if len(header):
dest_file.write(header)
if len(manual_issues):
dest_file.write("//@ skip\n")
dest_file.write("// Skipping this test due to the following issues:\n")
for issue in sorted(manual_issues):
dest_file.write(issue)
dest_file.write("\n")
dest_file.write(buffer)
except IOError as e:
print_stderr("Error: {0}".format(e))
return False
return True
def main():
global files_loaded_notes
global files_needing_manual_changes
args = parse_args()
force_overwrite = args.force
files_loaded_notes = ItemNotes("Processed List of Loaded Files:", "File Name", "Original Path(s) Found")
files_needing_manual_changes = ItemNotes("Converted Files Needing Manual Changes", "File Name", "Reason")
if args.mode == FILE_MODE:
convert_file(args.source_file_name, args.dest_file_name, force_overwrite)
elif args.mode == LIST_MODE:
src_root_dir = args.source_dir
dest_root_dir = args.output_dir
try:
with open(args.file_list_file_name, 'r') as file_list:
for file in file_list:
file = Path(file.strip())
name = file.name
src_file = src_root_dir / file if src_root_dir else file
if not os.path.exists(src_file):
print_stderr("Cannot open \"{0}\", skipping".format(src_file))
dest_file = dest_root_dir / name if dest_root_dir else name
convert_file(src_file, dest_file, force_overwrite)
except IOError as e:
print_stderr("Error: {0}".format(e))
return False
if args.verbose:
print("Processed {count} files".format(count=files_converted))
files_loaded_notes.print_notes()
files_needing_manual_changes.print_notes()
if __name__ == "__main__":
main()