123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- # This Source Code Form is subject to the terms of the Mozilla Public
- # License, v. 2.0. If a copy of the MPL was not distributed with this
- # file, You can obtain one at http://mozilla.org/MPL/2.0/.
- #----------------------------------------------------------------------------
- # This script checks that SpiderMonkey MacroAssembler methods are properly
- # annotated.
- #
- # The MacroAssembler has one interface for all platforms, but it might have one
- # definition per platform. The code of the MacroAssembler use a macro to
- # annotate the method declarations, in order to delete the function if it is not
- # present on the current platform, and also to locate the files in which the
- # methods are defined.
- #
- # This script scans the MacroAssembler.h header, for method declarations.
- # It also scans MacroAssembler-/arch/.cpp, MacroAssembler-/arch/-inl.h, and
- # MacroAssembler-inl.h for method definitions. The result of both scans are
- # uniformized, and compared, to determine if the MacroAssembler.h header as
- # proper methods annotations.
- #----------------------------------------------------------------------------
- from __future__ import print_function
- import difflib
- import os
- import re
- import sys
- from mozversioncontrol import get_repository_from_env
- architecture_independent = set([ 'generic' ])
- all_architecture_names = set([ 'x86', 'x64', 'arm', 'arm64', 'mips32', 'mips64' ])
- all_shared_architecture_names = set([ 'x86_shared', 'mips_shared', 'arm', 'arm64' ])
- reBeforeArg = "(?<=[(,\s])"
- reArgType = "(?P<type>[\w\s:*&]+)"
- reArgName = "(?P<name>\s\w+)"
- reArgDefault = "(?P<default>(?:\s=[^,)]+)?)"
- reAfterArg = "(?=[,)])"
- reMatchArg = re.compile(reBeforeArg + reArgType + reArgName + reArgDefault + reAfterArg)
- def get_normalized_signatures(signature, fileAnnot = None):
- # Remove static
- signature = signature.replace('static', '')
- # Remove semicolon.
- signature = signature.replace(';', ' ')
- # Normalize spaces.
- signature = re.sub(r'\s+', ' ', signature).strip()
- # Match arguments, and keep only the type.
- signature = reMatchArg.sub('\g<type>', signature)
- # Remove class name
- signature = signature.replace('MacroAssembler::', '')
- # Extract list of architectures
- archs = ['generic']
- if fileAnnot:
- archs = [fileAnnot['arch']]
- if 'DEFINED_ON(' in signature:
- archs = re.sub(r'.*DEFINED_ON\((?P<archs>[^()]*)\).*', '\g<archs>', signature).split(',')
- archs = [a.strip() for a in archs]
- signature = re.sub(r'\s+DEFINED_ON\([^()]*\)', '', signature)
- elif 'PER_ARCH' in signature:
- archs = all_architecture_names
- signature = re.sub(r'\s+PER_ARCH', '', signature)
- elif 'PER_SHARED_ARCH' in signature:
- archs = all_shared_architecture_names
- signature = re.sub(r'\s+PER_SHARED_ARCH', '', signature)
- else:
- # No signature annotation, the list of architectures remains unchanged.
- pass
- # Extract inline annotation
- inline = False
- if fileAnnot:
- inline = fileAnnot['inline']
- if 'inline ' in signature:
- signature = re.sub(r'inline\s+', '', signature)
- inline = True
- inlinePrefx = ''
- if inline:
- inlinePrefx = 'inline '
- signatures = [
- { 'arch': a, 'sig': inlinePrefx + signature }
- for a in archs
- ]
- return signatures
- file_suffixes = set([
- a.replace('_', '-') for a in
- all_architecture_names.union(all_shared_architecture_names)
- ])
- def get_file_annotation(filename):
- origFilename = filename
- filename = filename.split('/')[-1]
- inline = False
- if filename.endswith('.cpp'):
- filename = filename[:-len('.cpp')]
- elif filename.endswith('-inl.h'):
- inline = True
- filename = filename[:-len('-inl.h')]
- else:
- raise Exception('unknown file name', origFilename)
- arch = 'generic'
- for suffix in file_suffixes:
- if filename == 'MacroAssembler-' + suffix:
- arch = suffix
- break
- return {
- 'inline': inline,
- 'arch': arch.replace('-', '_')
- }
- def get_macroassembler_definitions(filename):
- try:
- fileAnnot = get_file_annotation(filename)
- except:
- return []
- style_section = False
- code_section = False
- lines = ''
- signatures = []
- with open(filename) as f:
- for line in f:
- if '//{{{ check_macroassembler_style' in line:
- style_section = True
- elif '//}}} check_macroassembler_style' in line:
- style_section = False
- if not style_section:
- continue
- line = re.sub(r'//.*', '', line)
- if line.startswith('{'):
- if 'MacroAssembler::' in lines:
- signatures.extend(get_normalized_signatures(lines, fileAnnot))
- code_section = True
- continue
- if line.startswith('}'):
- code_section = False
- lines = ''
- continue
- if code_section:
- continue
- if len(line.strip()) == 0:
- lines = ''
- continue
- lines = lines + line
- # Continue until we have a complete declaration
- if '{' not in lines:
- continue
- # Skip variable declarations
- if ')' not in lines:
- lines = ''
- continue
- return signatures
- def get_macroassembler_declaration(filename):
- style_section = False
- lines = ''
- signatures = []
- with open(filename) as f:
- for line in f:
- if '//{{{ check_macroassembler_style' in line:
- style_section = True
- elif '//}}} check_macroassembler_style' in line:
- style_section = False
- if not style_section:
- continue
- line = re.sub(r'//.*', '', line)
- if len(line.strip()) == 0:
- lines = ''
- continue
- lines = lines + line
- # Continue until we have a complete declaration
- if ';' not in lines:
- continue
- # Skip variable declarations
- if ')' not in lines:
- lines = ''
- continue
- signatures.extend(get_normalized_signatures(lines))
- lines = ''
- return signatures
- def append_signatures(d, sigs):
- for s in sigs:
- if s['sig'] not in d:
- d[s['sig']] = []
- d[s['sig']].append(s['arch']);
- return d
- def generate_file_content(signatures):
- output = []
- for s in sorted(signatures.keys()):
- archs = set(sorted(signatures[s]))
- if len(archs.symmetric_difference(architecture_independent)) == 0:
- output.append(s + ';\n')
- if s.startswith('inline'):
- output.append(' is defined in MacroAssembler-inl.h\n')
- else:
- output.append(' is defined in MacroAssembler.cpp\n')
- else:
- if len(archs.symmetric_difference(all_architecture_names)) == 0:
- output.append(s + ' PER_ARCH;\n')
- elif len(archs.symmetric_difference(all_shared_architecture_names)) == 0:
- output.append(s + ' PER_SHARED_ARCH;\n')
- else:
- output.append(s + ' DEFINED_ON(' + ', '.join(archs) + ');\n')
- for a in archs:
- a = a.replace('_', '-')
- masm = '%s/MacroAssembler-%s' % (a, a)
- if s.startswith('inline'):
- output.append(' is defined in %s-inl.h\n' % masm)
- else:
- output.append(' is defined in %s.cpp\n' % masm)
- return output
- def check_style():
- # We read from the header file the signature of each function.
- decls = dict() # type: dict(signature => ['x86', 'x64'])
- # We infer from each file the signature of each MacroAssembler function.
- defs = dict() # type: dict(signature => ['x86', 'x64'])
- repo = get_repository_from_env()
- # Select the appropriate files.
- for filename in repo.get_files_in_working_directory():
- if not filename.startswith('js/src/jit/'):
- continue
- if 'MacroAssembler' not in filename:
- continue
- filename = os.path.join(repo.path, filename)
- if filename.endswith('MacroAssembler.h'):
- decls = append_signatures(decls, get_macroassembler_declaration(filename))
- else:
- defs = append_signatures(defs, get_macroassembler_definitions(filename))
- # Compare declarations and definitions output.
- difflines = difflib.unified_diff(generate_file_content(decls),
- generate_file_content(defs),
- fromfile='check_macroassembler_style.py declared syntax',
- tofile='check_macroassembler_style.py found definitions')
- ok = True
- for diffline in difflines:
- ok = False
- print(diffline, end='')
- return ok
- def main():
- ok = check_style()
- if ok:
- print('TEST-PASS | check_macroassembler_style.py | ok')
- else:
- print('TEST-UNEXPECTED-FAIL | check_macroassembler_style.py | actual output does not match expected output; diff is above')
- sys.exit(0 if ok else 1)
- if __name__ == '__main__':
- main()
|