| #!/usr/bin/env python3 | 
 | # Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file | 
 | # for details. All rights reserved. Use of this source code is governed by a | 
 | # BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | import argparse | 
 | import sys | 
 |  | 
 | COMPANION_CLASS_SUFFIX = '$-CC' | 
 | EXTERNAL_SYNTHETIC_SUFFIX = '$$ExternalSynthetic' | 
 | SYNTHETIC_PREFIX = 'S' | 
 |  | 
 |  | 
 | # Parses a list of class and method descriptors, prefixed with one or more flags | 
 | # 'H' (hot), 'S' (startup), 'P' (post startup). | 
 | # | 
 | # Example: | 
 | # | 
 | # HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V | 
 | # HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I | 
 | # HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V | 
 | # PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V | 
 | # HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I | 
 | # Landroidx/compose/runtime/ComposerImpl; | 
 | # | 
 | # See also https://developer.android.com/studio/profile/baselineprofiles. | 
 | def parse_art_profile(lines): | 
 |     art_profile = {} | 
 |     flags_to_name = {'H': 'hot', 'S': 'startup', 'P': 'post_startup'} | 
 |     for line in lines: | 
 |         line = line.strip() | 
 |         if not line: | 
 |             continue | 
 |         flags = {'hot': False, 'startup': False, 'post_startup': False} | 
 |         while line[0] in flags_to_name: | 
 |             flag_abbreviation = line[0] | 
 |             flag_name = flags_to_name.get(flag_abbreviation) | 
 |             flags[flag_name] = True | 
 |             line = line[1:] | 
 |         while line.startswith('['): | 
 |             line = line[1:] | 
 |         assert line.startswith('L'), line | 
 |         descriptor = line | 
 |         art_profile[descriptor] = flags | 
 |     return art_profile | 
 |  | 
 |  | 
 | def transform_art_profile_to_r8_startup_list(art_profile, | 
 |                                              generalize_synthetics=False): | 
 |     r8_startup_list = {} | 
 |     for startup_descriptor, flags in art_profile.items(): | 
 |         transformed_startup_descriptor = transform_synthetic_descriptor( | 
 |             startup_descriptor) if generalize_synthetics else startup_descriptor | 
 |         r8_startup_list[transformed_startup_descriptor] = { | 
 |             'conditional_startup': False, | 
 |             'hot': flags['hot'], | 
 |             'startup': flags['startup'], | 
 |             'post_startup': flags['post_startup'] | 
 |         } | 
 |     return r8_startup_list | 
 |  | 
 |  | 
 | def transform_synthetic_descriptor(descriptor): | 
 |     companion_class_index = descriptor.find(COMPANION_CLASS_SUFFIX) | 
 |     if companion_class_index >= 0: | 
 |         return SYNTHETIC_PREFIX + descriptor[0:companion_class_index] + ';' | 
 |     external_synthetic_index = descriptor.find(EXTERNAL_SYNTHETIC_SUFFIX) | 
 |     if external_synthetic_index >= 0: | 
 |         return SYNTHETIC_PREFIX + descriptor[0:external_synthetic_index] + ';' | 
 |     return descriptor | 
 |  | 
 |  | 
 | def filter_r8_startup_list(r8_startup_list, options): | 
 |     filtered_r8_startup_list = {} | 
 |     for startup_descriptor, flags in r8_startup_list.items(): | 
 |         if not options.include_post_startup \ | 
 |             and flags.get('post_startup') \ | 
 |             and not flags.get('startup'): | 
 |             continue | 
 |         filtered_r8_startup_list[startup_descriptor] = flags | 
 |     return filtered_r8_startup_list | 
 |  | 
 |  | 
 | def parse_options(argv): | 
 |     result = argparse.ArgumentParser( | 
 |         description='Utilities for converting an ART profile into an R8 startup ' | 
 |         'list.') | 
 |     result.add_argument('--art-profile', help='Path to the ART profile') | 
 |     result.add_argument( | 
 |         '--include-post-startup', | 
 |         help='Include post startup classes and methods in the R8 ' | 
 |         'startup list', | 
 |         action='store_true', | 
 |         default=False) | 
 |     result.add_argument('--out', help='Where to store the R8 startup list') | 
 |     options, args = result.parse_known_args(argv) | 
 |     return options, args | 
 |  | 
 |  | 
 | def main(argv): | 
 |     (options, args) = parse_options(argv) | 
 |     with open(options.art_profile, 'r') as f: | 
 |         art_profile = parse_art_profile(f.read().splitlines()) | 
 |     r8_startup_list = transform_art_profile_to_r8_startup_list(art_profile) | 
 |     filtered_r8_startup_list = filter_r8_startup_list(r8_startup_list, options) | 
 |     if options.out is not None: | 
 |         with open(options.out, 'w') as f: | 
 |             for startup_descriptor, flags in filtered_r8_startup_list.items(): | 
 |                 f.write(startup_descriptor) | 
 |                 f.write('\n') | 
 |     else: | 
 |         for startup_descriptor, flags in filtered_r8_startup_list.items(): | 
 |             print(startup_descriptor) | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     sys.exit(main(sys.argv[1:])) |