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 absolute_import
24 from __future__
import print_function
31 architecture_independent
= set(['generic'])
32 all_unsupported_architectures_names
= set(['mips32', 'mips64', 'mips_shared'])
33 all_architecture_names
= set(['x86', 'x64', 'arm', 'arm64'])
34 all_shared_architecture_names
= set(['x86_shared', 'arm', 'arm64'])
36 reBeforeArg
= "(?<=[(,\s])"
37 reArgType
= "(?P<type>[\w\s:*&]+)"
38 reArgName
= "(?P<name>\s\w+)"
39 reArgDefault
= "(?P<default>(?:\s=[^,)]+)?)"
40 reAfterArg
= "(?=[,)])"
41 reMatchArg
= re
.compile(reBeforeArg
+ reArgType
+
42 reArgName
+ reArgDefault
+ reAfterArg
)
45 def get_normalized_signatures(signature
, fileAnnot
=None):
47 signature
= signature
.replace('static', '')
49 signature
= signature
.replace(';', ' ')
51 signature
= re
.sub(r
'\s+', ' ', signature
).strip()
52 # Remove new-line induced spaces after opening braces.
53 signature
= re
.sub(r
'\(\s+', '(', signature
).strip()
54 # Match arguments, and keep only the type.
55 signature
= reMatchArg
.sub('\g<type>', signature
)
57 signature
= signature
.replace('MacroAssembler::', '')
59 # Extract list of architectures
62 archs
= [fileAnnot
['arch']]
64 if 'DEFINED_ON(' in signature
:
66 r
'.*DEFINED_ON\((?P<archs>[^()]*)\).*', '\g<archs>', signature
).split(',')
67 archs
= [a
.strip() for a
in archs
]
68 signature
= re
.sub(r
'\s+DEFINED_ON\([^()]*\)', '', signature
)
70 elif 'PER_ARCH' in signature
:
71 archs
= all_architecture_names
72 signature
= re
.sub(r
'\s+PER_ARCH', '', signature
)
74 elif 'PER_SHARED_ARCH' in signature
:
75 archs
= all_shared_architecture_names
76 signature
= re
.sub(r
'\s+PER_SHARED_ARCH', '', signature
)
78 elif 'OOL_IN_HEADER' in signature
:
79 assert archs
== ['generic']
80 signature
= re
.sub(r
'\s+OOL_IN_HEADER', '', signature
)
83 # No signature annotation, the list of architectures remains unchanged.
86 # Extract inline annotation
89 inline
= fileAnnot
['inline']
91 if 'inline ' in signature
:
92 signature
= re
.sub(r
'inline\s+', '', signature
)
97 inlinePrefx
= 'inline '
99 {'arch': a
, 'sig': inlinePrefx
+ signature
}
106 file_suffixes
= set([
107 a
.replace('_', '-') for a
in
108 all_architecture_names
.union(all_shared_architecture_names
)
109 .union(all_unsupported_architectures_names
)
113 def get_file_annotation(filename
):
114 origFilename
= filename
115 filename
= filename
.split('/')[-1]
118 if filename
.endswith('.cpp'):
119 filename
= filename
[:-len('.cpp')]
120 elif filename
.endswith('-inl.h'):
122 filename
= filename
[:-len('-inl.h')]
123 elif filename
.endswith('.h'):
124 # This allows the definitions block in MacroAssembler.h to be
127 filename
= filename
[:-len('.h')]
129 raise Exception('unknown file name', origFilename
)
132 for suffix
in file_suffixes
:
133 if filename
== 'MacroAssembler-' + suffix
:
139 'arch': arch
.replace('-', '_')
143 def get_macroassembler_definitions(filename
):
145 fileAnnot
= get_file_annotation(filename
)
149 style_section
= False
152 with
open(filename
) as f
:
154 if '//{{{ check_macroassembler_style' in line
:
156 raise 'check_macroassembler_style section already opened.'
159 elif '//}}} check_macroassembler_style' in line
:
160 style_section
= False
161 if not style_section
:
164 # Ignore preprocessor directives.
165 if line
.startswith('#'):
168 # Remove comments from the processed line.
169 line
= re
.sub(r
'//.*', '', line
)
171 # Locate and count curly braces.
172 open_curly_brace
= line
.find('{')
173 was_braces_depth
= braces_depth
174 braces_depth
= braces_depth
+ line
.count('{') - line
.count('}')
176 # Raise an error if the check_macroassembler_style macro is used
177 # across namespaces / classes scopes.
179 raise 'check_macroassembler_style annotations are not well scoped.'
181 # If the current line contains an opening curly brace, check if
182 # this line combines with the previous one can be identified as a
183 # MacroAssembler function signature.
184 if open_curly_brace
!= -1 and was_braces_depth
== 0:
185 lines
= lines
+ line
[:open_curly_brace
]
186 if 'MacroAssembler::' in lines
:
188 get_normalized_signatures(lines
, fileAnnot
))
192 # We do not aggregate any lines if we are scanning lines which are
193 # in-between a set of curly braces.
196 if was_braces_depth
!= 0:
197 line
= line
[line
.rfind('}') + 1:]
199 # This logic is used to remove template instantiation, static
200 # variable definitions and function declaration from the next
201 # function definition.
202 last_semi_colon
= line
.rfind(';')
203 if last_semi_colon
!= -1:
205 line
= line
[last_semi_colon
+ 1:]
207 # Aggregate lines of non-braced text, which corresponds to the space
208 # where we are expecting to find function definitions.
214 def get_macroassembler_declaration(filename
):
215 style_section
= False
218 with
open(filename
) as f
:
220 if '//{{{ check_macroassembler_decl_style' in line
:
222 elif '//}}} check_macroassembler_decl_style' in line
:
223 style_section
= False
224 if not style_section
:
227 # Ignore preprocessor directives.
228 if line
.startswith('#'):
231 line
= re
.sub(r
'//.*', '', line
)
232 if len(line
.strip()) == 0 or 'public:' in line
or 'private:' in line
:
238 # Continue until we have a complete declaration
242 # Skip member declarations: which are lines ending with a
243 # semi-colon without any list of arguments.
248 signatures
.extend(get_normalized_signatures(lines
))
254 def append_signatures(d
, sigs
):
256 if s
['sig'] not in d
:
258 d
[s
['sig']].append(s
['arch'])
262 def generate_file_content(signatures
):
264 for s
in sorted(signatures
.keys()):
265 archs
= set(sorted(signatures
[s
]))
266 archs
-= all_unsupported_architectures_names
267 if len(archs
.symmetric_difference(architecture_independent
)) == 0:
268 output
.append(s
+ ';\n')
269 if s
.startswith('inline'):
270 # TODO, bug 1432600: This is mistaken for OOL_IN_HEADER
271 # functions. (Such annotation is already removed by the time
272 # this function sees the signature here.)
273 output
.append(' is defined in MacroAssembler-inl.h\n')
275 output
.append(' is defined in MacroAssembler.cpp\n')
277 if len(archs
.symmetric_difference(all_architecture_names
)) == 0:
278 output
.append(s
+ ' PER_ARCH;\n')
279 elif len(archs
.symmetric_difference(all_shared_architecture_names
)) == 0:
280 output
.append(s
+ ' PER_SHARED_ARCH;\n')
283 s
+ ' DEFINED_ON(' + ', '.join(sorted(archs
)) + ');\n')
284 for a
in sorted(archs
):
285 a
= a
.replace('_', '-')
286 masm
= '%s/MacroAssembler-%s' % (a
, a
)
287 if s
.startswith('inline'):
288 output
.append(' is defined in %s-inl.h\n' % masm
)
290 output
.append(' is defined in %s.cpp\n' % masm
)
295 # We read from the header file the signature of each function.
296 decls
= dict() # type: dict(signature => ['x86', 'x64'])
298 # We infer from each file the signature of each MacroAssembler function.
299 defs
= dict() # type: dict(signature => ['x86', 'x64'])
301 root_dir
= os
.path
.join('js', 'src', 'jit')
302 for dirpath
, dirnames
, filenames
in os
.walk(root_dir
):
303 for filename
in filenames
:
304 if 'MacroAssembler' not in filename
:
307 filepath
= os
.path
.join(dirpath
, filename
).replace('\\', '/')
309 if filepath
.endswith('MacroAssembler.h'):
310 decls
= append_signatures(
311 decls
, get_macroassembler_declaration(filepath
))
312 defs
= append_signatures(
313 defs
, get_macroassembler_definitions(filepath
))
315 if not decls
or not defs
:
316 raise Exception("Did not find any definitions or declarations")
318 # Compare declarations and definitions output.
319 difflines
= difflib
.unified_diff(generate_file_content(decls
),
320 generate_file_content(defs
),
321 fromfile
='check_macroassembler_style.py declared syntax',
322 tofile
='check_macroassembler_style.py found definitions')
324 for diffline
in difflines
:
326 print(diffline
, end
='')
335 print('TEST-PASS | check_macroassembler_style.py | ok')
337 print('TEST-UNEXPECTED-FAIL | check_macroassembler_style.py | actual output does not match expected output; diff is above') # noqa: E501
339 sys
.exit(0 if ok
else 1)
342 if __name__
== '__main__':