|  | #!/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:])) |