-
-
Notifications
You must be signed in to change notification settings - Fork 4
Group iOS treemap by known libraries #632
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
trevor-e
wants to merge
1
commit into
main
Choose a base branch
from
trevorelkins/eme-139-group-ios-by-known-libraries-62f2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| """Catalog of well-known iOS libraries for treemap grouping. | ||
|
|
||
| Statically-linked third-party libraries show up in the main binary as Swift module | ||
| nodes (e.g. ``Alamofire``) or Objective-C class nodes (e.g. ``FIRApp``). This catalog | ||
| lets the treemap builder recognize those nodes and group them under a single | ||
| ``Libraries`` parent so reviewers can see how much size known SDKs contribute. | ||
|
|
||
| Keep entries conservative: exact Swift module names, and distinctive Objective-C class | ||
| prefixes (3+ chars) that are unlikely to collide with first-party code. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import dataclass, field | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class KnownLibrary: | ||
| """A known iOS library and the identifiers used to recognize it.""" | ||
|
|
||
| name: str | ||
| # Exact Swift module names that map to this library. | ||
| swift_modules: frozenset[str] = field(default_factory=frozenset) | ||
| # Distinctive Objective-C class-name prefixes that map to this library. | ||
| objc_prefixes: tuple[str, ...] = () | ||
|
|
||
|
|
||
| # Curated, non-exhaustive catalog of popular iOS libraries. Extend as needed. | ||
| KNOWN_LIBRARIES: tuple[KnownLibrary, ...] = ( | ||
| KnownLibrary("Sentry", frozenset({"Sentry", "SentrySwift", "SentrySwiftUI"}), ("Sentry",)), | ||
| KnownLibrary( | ||
| "Firebase", | ||
| frozenset( | ||
| { | ||
| "FirebaseCore", | ||
| "FirebaseCoreInternal", | ||
| "FirebaseAnalytics", | ||
| "FirebaseCrashlytics", | ||
| "FirebaseMessaging", | ||
| "FirebaseFirestore", | ||
| "FirebaseFirestoreSwift", | ||
| "FirebaseAuth", | ||
| "FirebaseDatabase", | ||
| "FirebaseStorage", | ||
| "FirebaseRemoteConfig", | ||
| "FirebaseInstallations", | ||
| "FirebasePerformance", | ||
| "FirebaseDynamicLinks", | ||
| "FirebaseInAppMessaging", | ||
| "FirebaseAppCheck", | ||
| } | ||
| ), | ||
| ("FIR", "FBLPromise", "GUL"), | ||
| ), | ||
| KnownLibrary("Alamofire", frozenset({"Alamofire"})), | ||
| KnownLibrary("Lottie", frozenset({"Lottie"})), | ||
| KnownLibrary("Kingfisher", frozenset({"Kingfisher"})), | ||
| KnownLibrary("SnapKit", frozenset({"SnapKit"})), | ||
| KnownLibrary("Nuke", frozenset({"Nuke", "NukeUI"})), | ||
| KnownLibrary("SDWebImage", frozenset({"SDWebImage", "SDWebImageSwiftUI"}), ("SDWeb", "SDImage", "SDAnimated")), | ||
| KnownLibrary("RxSwift", frozenset({"RxSwift", "RxCocoa", "RxRelay", "RxBlocking"})), | ||
| KnownLibrary("Realm", frozenset({"Realm", "RealmSwift"}), ("RLM",)), | ||
| KnownLibrary("GoogleMaps", frozenset({"GoogleMaps", "GoogleMapsBase"}), ("GMS",)), | ||
| KnownLibrary("GoogleSignIn", frozenset({"GoogleSignIn"}), ("GID",)), | ||
| KnownLibrary("Facebook", frozenset({"FacebookCore", "FacebookLogin", "FacebookShare"}), ("FBSDK",)), | ||
| KnownLibrary("AFNetworking", frozenset(), ("AFHTTP", "AFURL", "AFNetwork", "AFSecurity")), | ||
| KnownLibrary("Stripe", frozenset({"Stripe", "StripeCore", "StripePayments", "StripeUICore"}), ("STP",)), | ||
| KnownLibrary("Branch", frozenset({"BranchSDK"}), ("BNC", "Branch")), | ||
| KnownLibrary("Mixpanel", frozenset({"Mixpanel"}), ("Mixpanel",)), | ||
| KnownLibrary("Amplitude", frozenset({"Amplitude", "AmplitudeSwift"}), ("AMP",)), | ||
| KnownLibrary("Segment", frozenset({"Segment"}), ("SEG",)), | ||
| KnownLibrary("Adjust", frozenset({"Adjust", "AdjustSdk"}), ("ADJ",)), | ||
| KnownLibrary("AppsFlyer", frozenset({"AppsFlyerLib"}), ("AppsFlyer",)), | ||
| KnownLibrary("Bugsnag", frozenset({"Bugsnag"}), ("BSG", "Bugsnag")), | ||
| KnownLibrary( | ||
| "Datadog", frozenset({"Datadog", "DatadogCore", "DatadogLogs", "DatadogRUM", "DatadogTrace"}), ("DD",) | ||
| ), | ||
| KnownLibrary("Charts", frozenset({"Charts", "DGCharts"})), | ||
| KnownLibrary("SwiftyJSON", frozenset({"SwiftyJSON"})), | ||
| KnownLibrary("Moya", frozenset({"Moya"})), | ||
| KnownLibrary("PromiseKit", frozenset({"PromiseKit"})), | ||
| KnownLibrary("EmergeTools", frozenset({"EmergeSnapshots", "SnapshotPreferences", "SnapshotPreviewsCore"})), | ||
| ) | ||
|
|
||
|
|
||
| def _build_swift_module_index() -> dict[str, str]: | ||
| index: dict[str, str] = {} | ||
| for library in KNOWN_LIBRARIES: | ||
| for module in library.swift_modules: | ||
| index[module] = library.name | ||
| return index | ||
|
|
||
|
|
||
| def _build_objc_prefix_index() -> tuple[tuple[str, str], ...]: | ||
| # Sort by descending prefix length so the most specific prefix wins. | ||
| pairs = [(prefix, library.name) for library in KNOWN_LIBRARIES for prefix in library.objc_prefixes] | ||
| pairs.sort(key=lambda pair: len(pair[0]), reverse=True) | ||
| return tuple(pairs) | ||
|
|
||
|
|
||
| _SWIFT_MODULE_INDEX = _build_swift_module_index() | ||
| _OBJC_PREFIX_INDEX = _build_objc_prefix_index() | ||
|
|
||
|
|
||
| def resolve_known_library(node_name: str) -> str | None: | ||
| """Resolve a treemap node name to a known library's canonical name. | ||
|
|
||
| Matches an exact Swift module name first, then a distinctive Objective-C class | ||
| prefix. Returns ``None`` when the name doesn't correspond to a known library. | ||
| """ | ||
| if not node_name: | ||
| return None | ||
|
|
||
| library = _SWIFT_MODULE_INDEX.get(node_name) | ||
| if library is not None: | ||
| return library | ||
|
|
||
| for prefix, library_name in _OBJC_PREFIX_INDEX: | ||
| if node_name.startswith(prefix): | ||
| return library_name | ||
|
|
||
| return None | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| """Unit tests for the known-libraries catalog.""" | ||
|
|
||
| from launchpad.size.treemap.known_libraries import resolve_known_library | ||
|
|
||
|
|
||
| def test_resolves_exact_swift_module() -> None: | ||
| assert resolve_known_library("Alamofire") == "Alamofire" | ||
| assert resolve_known_library("Lottie") == "Lottie" | ||
| assert resolve_known_library("RxCocoa") == "RxSwift" | ||
|
|
||
|
|
||
| def test_groups_multiple_modules_to_one_library() -> None: | ||
| assert resolve_known_library("FirebaseCore") == "Firebase" | ||
| assert resolve_known_library("FirebaseAnalytics") == "Firebase" | ||
|
|
||
|
|
||
| def test_resolves_objc_class_prefix() -> None: | ||
| assert resolve_known_library("FIRApp") == "Firebase" | ||
| assert resolve_known_library("RLMRealm") == "Realm" | ||
| assert resolve_known_library("GMSMapView") == "GoogleMaps" | ||
|
|
||
|
|
||
| def test_swift_module_takes_priority_over_prefix() -> None: | ||
| # "Sentry" is both an exact module and an ObjC prefix; either way it resolves | ||
| # to the same library, but the exact-module path should win. | ||
| assert resolve_known_library("Sentry") == "Sentry" | ||
|
|
||
|
|
||
| def test_unknown_and_empty_names_return_none() -> None: | ||
| assert resolve_known_library("MyApp") is None | ||
| assert resolve_known_library("AppViewModel") is None | ||
| assert resolve_known_library("") is None |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefix matching mislabels modules
Medium Severity
resolve_known_libraryruns Objective-C prefix checks on every symbol child name, including Swift module nodes, so first-party modules that only share a prefix (for exampleBranchorSentryUI) can be grouped under third-partyLibraries. The DatadogDDprefix is two characters and matches unrelated ObjC types such as CocoaLumberjack’sDDLog, despite the catalog comment requiring 3+ character prefixes.Additional Locations (1)
src/launchpad/size/treemap/macho_element_builder.py#L190-L196Reviewed by Cursor Bugbot for commit 7282aeb. Configure here.