1 # vim: set ts=8 sts=4 et sw=4 tw=99:
2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 # ----------------------------------------------------------------------------
7 # This script checks that SpiderMonkey MacroAssembler methods are properly
10 # The MacroAssembler has one interface for all platforms, but it might have one
11 # definition per platform. The code of the MacroAssembler use a macro to
12 # annotate the method declarations, in order to delete the function if it is not
13 # present on the current platform, and also to locate the files in which the
14 # methods are defined.
16 # This script scans the MacroAssembler.h header, for method declarations.
17 # It also scans MacroAssembler-/arch/.cpp, MacroAssembler-/arch/-inl.h, and
18 # MacroAssembler-inl.h for method definitions. The result of both scans are
19 # uniformized, and compared, to determine if the MacroAssembler.h header as
20 # proper methods annotations.
21 # ----------------------------------------------------------------------------
23 from __future__
import print_function
30 architecture_independent
= set(['generic'])
31 all_unsupported_architectures_names
= set(['mips32', 'mips64', 'mips_shared'])
32 all_architecture_names
= set(['x86', 'x64', 'arm', 'arm64'])
33 all_shared_architecture_names
= set(['x86_shared', 'arm', 'arm64'])
35 reBeforeArg
= "(?<=[(,\s])"
36 reArgType
= "(?P<type>[\w\s:*&]+)"
37 reArgName
= "(?P<name>\s\w+)"
38 reArgDefault
= "(?P<default>(?:\s=[^,)]+)?)"
39 reAfterArg
= "(?=[,)])"
40 reMatchArg
= re
.compile(reBeforeArg
+ reArgType
+
41 reArgName
+ reArgDefault
+ reAfterArg
)
44 def get_normalized_signatures(signature
, fileAnnot
=None):
46 signature
= signature
.replace('static', '')
48 signature
= signature
.replace(';', ' ')
50 signature
= re
.sub(r
'\s+', ' ', signature
).strip()
51 # Remove new-line induced spaces after opening braces.
52 signature
= re
.sub(r
'\(\s+', '(', signature
).strip()
53 # Match arguments, and keep only the type.
54 signature
= reMatchArg
.sub('\g<type>', signature
)
56 signature
= signature
.replace('MacroAssembler::', '')
58 # Extract list of architectures
61 archs
= [fileAnnot
['arch']]
63 if 'DEFINED_ON(' in signature
:
65 r
'.*DEFINED_ON\((?P<archs>[^()]*)\).*', '\g<archs>', signature
).split(',')
66 archs
= [a
.strip() for a
in archs
]
67 signature
= re
.sub(r
'\s+DEFINED_ON\([^()]*\)', '', signature
)
69 elif 'PER_ARCH' in signature
:
70 archs
= all_architecture_names
71 signature
= re
.sub(r
'\s+PER_ARCH', '', signature
)
73 elif 'PER_SHARED_ARCH' in signature
:
74 archs
= all_shared_architecture_names
75 signature
= re
.sub(r
'\s+PER_SHARED_ARCH', '', signature
)
77 elif 'OOL_IN_HEADER' in signature
:
78 assert archs
== ['generic']
79 signature
= re
.sub(r
'\s+OOL_IN_HEADER', '', signature
)
82 # No signature annotation, the list of architectures remains unchanged.
85 # Extract inline annotation
88 inline
= fileAnnot
['inline']
90 if 'inline ' in signature
:
91 signature
= re
.sub(r
'inline\s+', '', signature
)
96 inlinePrefx
= 'inline '
98 {'arch': a
, 'sig': inlinePrefx
+ signature
}
105 file_suffixes
= set([
106 a
.replace('_', '-') for a
in
107 all_architecture_names
.union(all_shared_architecture_names
)
108 .union(all_unsupported_architectures_names
)
112 def get_file_annotation(filename
):
113 origFilename
= filename
114 filename
= filename
.split('/')[-1]
117 if filename
.endswith('.cpp'):
118 filename
= filename
[:-len('.cpp')]
119 elif filename
.endswith('-inl.h'):
121 filename
= filename
[:-len('-inl.h')]
122 elif filename
.endswith('.h'):
123 # This allows the definitions block in MacroAssembler.h to be
126 filename
= filename
[:-len('.h')]
128 raise Exception('unknown file name', origFilename
)
131 for suffix
in file_suffixes
:
132 if filename
== 'MacroAssembler-' + suffix
:
138 'arch': arch
.replace('-', '_')
142 def get_macroassembler_definitions(filename
):
144 fileAnnot
= get_file_annotation(filename
)
148 style_section
= False
151 with
open(filename
) as f
:
153 if '//{{{ check_macroassembler_style' in line
:
155 raise 'check_macroassembler_style section already opened.'
158 elif '//}}} check_macroassembler_style' in line
:
159 style_section
= False
160 if not style_section
:
163 # Remove comments from the processed line.
164 line
= re
.sub(r
'//.*', '', line
)
166 # Locate and count curly braces.
167 open_curly_brace
= line
.find('{')
168 was_braces_depth
= braces_depth
169 braces_depth
= braces_depth
+ line
.count('{') - line
.count('}')
171 # Raise an error if the check_macroassembler_style macro is used
172 # across namespaces / classes scopes.
174 raise 'check_macroassembler_style annotations are not well scoped.'
176 # If the current line contains an opening curly brace, check if
177 # this line combines with the previous one can be identified as a
178 # MacroAssembler function signature.
179 if open_curly_brace
!= -1 and was_braces_depth
== 0:
180 lines
= lines
+ line
[:open_curly_brace
]
181 if 'MacroAssembler::' in lines
:
183 get_normalized_signatures(lines
, fileAnnot
))
187 # We do not aggregate any lines if we are scanning lines which are
188 # in-between a set of curly braces.
191 if was_braces_depth
!= 0:
192 line
= line
[line
.rfind('}') + 1:]
194 # This logic is used to remove template instantiation, static
195 # variable definitions and function declaration from the next
196 # function definition.
197 last_semi_colon
= line
.rfind(';')
198 if last_semi_colon
!= -1:
200 line
= line
[last_semi_colon
+ 1:]
202 # Aggregate lines of non-braced text, which corresponds to the space
203 # where we are expecting to find function definitions.
209 def get_macroassembler_declaration(filename
):
210 style_section
= False
213 with
open(filename
) as f
:
215 if '//{{{ check_macroassembler_decl_style' in line
:
217 elif '//}}} check_macroassembler_decl_style' in line
:
218 style_section
= False
219 if not style_section
:
222 line
= re
.sub(r
'//.*', '', line
)
223 if len(line
.strip()) == 0 or 'public:' in line
or 'private:' in line
:
228 # Continue until we have a complete declaration
232 # Skip member declarations: which are lines ending with a
233 # semi-colon without any list of arguments.
238 signatures
.extend(get_normalized_signatures(lines
))
244 def append_signatures(d
, sigs
):
246 if s
['sig'] not in d
:
248 d
[s
['sig']].append(s
['arch'])
252 def generate_file_content(signatures
):
254 for s
in sorted(signatures
.keys()):
255 archs
= set(sorted(signatures
[s
]))
256 archs
-= all_unsupported_architectures_names
257 if len(archs
.symmetric_difference(architecture_independent
)) == 0:
258 output
.append(s
+ ';\n')
259 if s
.startswith('inline'):
260 # TODO, bug 1432600: This is mistaken for OOL_IN_HEADER
261 # functions. (Such annotation is already removed by the time
262 # this function sees the signature here.)
263 output
.append(' is defined in MacroAssembler-inl.h\n')
265 output
.append(' is defined in MacroAssembler.cpp\n')
267 if len(archs
.symmetric_difference(all_architecture_names
)) == 0:
268 output
.append(s
+ ' PER_ARCH;\n')
269 elif len(archs
.symmetric_difference(all_shared_architecture_names
)) == 0:
270 output
.append(s
+ ' PER_SHARED_ARCH;\n')
273 s
+ ' DEFINED_ON(' + ', '.join(sorted(archs
)) + ');\n')
274 for a
in sorted(archs
):
275 a
= a
.replace('_', '-')
276 masm
= '%s/MacroAssembler-%s' % (a
, a
)
277 if s
.startswith('inline'):
278 output
.append(' is defined in %s-inl.h\n' % masm
)
280 output
.append(' is defined in %s.cpp\n' % masm
)
285 # We read from the header file the signature of each function.
286 decls
= dict() # type: dict(signature => ['x86', 'x64'])
288 # We infer from each file the signature of each MacroAssembler function.
289 defs
= dict() # type: dict(signature => ['x86', 'x64'])
291 root_dir
= os
.path
.join('js', 'src', 'jit')
292 for dirpath
, dirnames
, filenames
in os
.walk(root_dir
):
293 for filename
in filenames
:
294 if 'MacroAssembler' not in filename
:
297 filepath
= os
.path
.join(dirpath
, filename
).replace('\\', '/')
299 if filepath
.endswith('MacroAssembler.h'):
300 decls
= append_signatures(
301 decls
, get_macroassembler_declaration(filepath
))
302 defs
= append_signatures(
303 defs
, get_macroassembler_definitions(filepath
))
305 if not decls
or not defs
:
306 raise Exception("Did not find any definitions or declarations")
308 # Compare declarations and definitions output.
309 difflines
= difflib
.unified_diff(generate_file_content(decls
),
310 generate_file_content(defs
),
311 fromfile
='check_macroassembler_style.py declared syntax',
312 tofile
='check_macroassembler_style.py found definitions')
314 for diffline
in difflines
:
316 print(diffline
, end
='')
325 print('TEST-PASS | check_macroassembler_style.py | ok')
327 print('TEST-UNEXPECTED-FAIL | check_macroassembler_style.py | actual output does not match expected output; diff is above') # noqa: E501
329 sys
.exit(0 if ok
else 1)
332 if __name__
== '__main__':