-
-
Notifications
You must be signed in to change notification settings - Fork 7
feat(r8): Propagate class-level synthesized flag to members #73
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
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
216c5eb
feat(r8-tests): Add R8 ambiguous tests
romtsn 7ae46d0
Update tests
romtsn 1deef0a
Change expected of test_ambiguous_missing_line_stacktrace
romtsn 558ed43
fix(r8-tests): Correctly handle no-line mappings
romtsn 59a8dd7
Remove import
romtsn cf3f732
Preserve input lineno when no mapping is available
romtsn 147a43e
feat(r8-tests): Add R8 synthetic tests
romtsn 20d7b62
Add MD file explaining failures
romtsn e44baf3
Merge branch 'master' into rz/feat/r8-tests-synthetic
romtsn a9125eb
Remove r8-ambiguous.NOTES
romtsn bcb6e4a
Merge remote-tracking branch 'origin/master' into rz/feat/r8-tests-sy…
romtsn 5c79e40
feat(r8): Propagate class-level synthesized flag to members
romtsn a311ab6
Address review feedback: remove stale NOTES, fix JSON, add cache flag…
romtsn cdc7cd7
formatting
romtsn 2988ea1
fix: Use is_some_and instead of map_or for clippy
romtsn 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
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,249 @@ | ||
| //! Tests for R8 synthetic / lambda method retracing fixtures. | ||
| //! | ||
| //! These tests are based on the R8 retrace test suite from: | ||
| //! src/test/java/com/android/tools/r8/retrace/stacktraces/ | ||
| //! | ||
| //! Note: this crate does NOT filter out synthesized frames. Instead it | ||
| //! propagates the `method_synthesized` flag so that callers (e.g. Sentry | ||
| //! symbolicator) can decide whether to strip them. | ||
|
|
||
| use proguard::{ProguardCache, ProguardMapper, ProguardMapping, StackFrame}; | ||
|
|
||
| // ============================================================================= | ||
| // SyntheticLambdaMethodStackTrace | ||
| // ============================================================================= | ||
|
|
||
| const SYNTHETIC_LAMBDA_METHOD_MAPPING: &str = "\ | ||
| # {\"id\":\"com.android.tools.r8.mapping\",\"version\":\"1.0\"} | ||
| example.Main -> example.Main: | ||
| 1:1:void main(java.lang.String[]):123 -> main | ||
| example.Foo -> a.a: | ||
| 5:5:void lambda$main$0():225 -> a | ||
| 3:3:void runIt():218 -> b | ||
| 2:2:void main():223 -> c | ||
| example.Foo$$ExternalSyntheticLambda0 -> a.b: | ||
| void run(example.Foo) -> a | ||
| # {\"id\":\"com.android.tools.r8.synthesized\"} | ||
| "; | ||
|
|
||
| #[test] | ||
| fn test_synthetic_lambda_method_stacktrace() { | ||
| let input = "\ | ||
| Exception in thread \"main\" java.lang.NullPointerException | ||
| at a.a.a(a.java:5) | ||
| at a.b.a(Unknown Source) | ||
| at a.a.b(a.java:3) | ||
| at a.a.c(a.java:2) | ||
| at example.Main.main(Main.java:1) | ||
| "; | ||
|
|
||
| // Synthetic frames are kept in the output; callers filter via method_synthesized(). | ||
| let expected = "\ | ||
| Exception in thread \"main\" java.lang.NullPointerException | ||
| at example.Foo.lambda$main$0(Foo.java:225) | ||
| at example.Foo$$ExternalSyntheticLambda0.run(Foo.java:0) | ||
| at example.Foo.runIt(Foo.java:218) | ||
| at example.Foo.main(Foo.java:223) | ||
| at example.Main.main(Main.java:123) | ||
| "; | ||
|
|
||
| let mapper = ProguardMapper::from(SYNTHETIC_LAMBDA_METHOD_MAPPING); | ||
| let actual = mapper.remap_stacktrace(input).unwrap(); | ||
| assert_eq!(actual.trim(), expected.trim()); | ||
|
|
||
| let mapping = ProguardMapping::new(SYNTHETIC_LAMBDA_METHOD_MAPPING.as_bytes()); | ||
| let mut buf = Vec::new(); | ||
| ProguardCache::write(&mapping, &mut buf).unwrap(); | ||
| let cache = ProguardCache::parse(&buf).unwrap(); | ||
| cache.test(); | ||
|
|
||
| let actual = cache.remap_stacktrace(input).unwrap(); | ||
| assert_eq!(actual.trim(), expected.trim()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_synthetic_lambda_method_synthesized_flag() { | ||
| let mapper = ProguardMapper::from(SYNTHETIC_LAMBDA_METHOD_MAPPING); | ||
|
|
||
| // The synthetic lambda class member should have method_synthesized = true. | ||
| let frame = StackFrame::try_parse(" at a.b.a(Unknown Source)".as_bytes()).unwrap(); | ||
| let remapped: Vec<_> = mapper.remap_frame(&frame).collect(); | ||
| assert!( | ||
| remapped.iter().all(|f| f.method_synthesized()), | ||
| "expected all frames from synthetic class to have method_synthesized = true, got: {remapped:?}" | ||
| ); | ||
|
|
||
| // A regular method should have method_synthesized = false. | ||
| let frame = StackFrame::try_parse(" at a.a.a(a.java:5)".as_bytes()).unwrap(); | ||
| let remapped: Vec<_> = mapper.remap_frame(&frame).collect(); | ||
| assert!( | ||
| remapped.iter().all(|f| !f.method_synthesized()), | ||
| "expected regular frame to have method_synthesized = false, got: {remapped:?}" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_synthetic_lambda_method_synthesized_flag_cache() { | ||
| let mapping = ProguardMapping::new(SYNTHETIC_LAMBDA_METHOD_MAPPING.as_bytes()); | ||
| let mut buf = Vec::new(); | ||
| ProguardCache::write(&mapping, &mut buf).unwrap(); | ||
| let cache = ProguardCache::parse(&buf).unwrap(); | ||
|
|
||
| // The synthetic lambda class member should have method_synthesized = true. | ||
| let frame = StackFrame::try_parse(" at a.b.a(Unknown Source)".as_bytes()).unwrap(); | ||
| let remapped: Vec<_> = cache.remap_frame(&frame).collect(); | ||
| assert!( | ||
| remapped.iter().all(|f| f.method_synthesized()), | ||
| "cache: expected synthetic frame to have method_synthesized = true, got: {remapped:?}" | ||
| ); | ||
|
|
||
| // A regular method should have method_synthesized = false. | ||
| let frame = StackFrame::try_parse(" at a.a.a(a.java:5)".as_bytes()).unwrap(); | ||
| let remapped: Vec<_> = cache.remap_frame(&frame).collect(); | ||
| assert!( | ||
| remapped.iter().all(|f| !f.method_synthesized()), | ||
| "cache: expected regular frame to have method_synthesized = false, got: {remapped:?}" | ||
| ); | ||
| } | ||
|
|
||
| // ============================================================================= | ||
| // SyntheticLambdaMethodWithInliningStackTrace | ||
| // ============================================================================= | ||
|
|
||
| const SYNTHETIC_LAMBDA_METHOD_WITH_INLINING_MAPPING: &str = "\ | ||
| # {\"id\":\"com.android.tools.r8.mapping\",\"version\":\"1.0\"} | ||
| example.Main -> example.Main: | ||
| 1:1:void main(java.lang.String[]):123 -> main | ||
| example.Foo -> a.a: | ||
| 3:3:void runIt():218 -> b | ||
| 2:2:void main():223 -> c | ||
| example.Foo$$ExternalSyntheticLambda0 -> a.b: | ||
| 4:4:void example.Foo.lambda$main$0():225 -> a | ||
| 4:4:void run(example.Foo):0 -> a | ||
| # {\"id\":\"com.android.tools.r8.synthesized\"} | ||
| "; | ||
|
|
||
| #[test] | ||
| fn test_synthetic_lambda_method_with_inlining_stacktrace() { | ||
| let input = "\ | ||
| Exception in thread \"main\" java.lang.NullPointerException | ||
| at a.b.a(Unknown Source:4) | ||
| at a.a.b(a.java:3) | ||
| at a.a.c(a.java:2) | ||
| at example.Main.main(Main.java:1) | ||
| "; | ||
|
|
||
| // Synthetic frames are kept; the inlined lambda$main$0 is not synthetic, | ||
| // but the outer run() method is. | ||
| let expected = "\ | ||
| Exception in thread \"main\" java.lang.NullPointerException | ||
| at example.Foo.lambda$main$0(Foo.java:225) | ||
| at example.Foo$$ExternalSyntheticLambda0.run(Foo.java:0) | ||
| at example.Foo.runIt(Foo.java:218) | ||
| at example.Foo.main(Foo.java:223) | ||
| at example.Main.main(Main.java:123) | ||
| "; | ||
|
|
||
| let mapper = ProguardMapper::from(SYNTHETIC_LAMBDA_METHOD_WITH_INLINING_MAPPING); | ||
| let actual = mapper.remap_stacktrace(input).unwrap(); | ||
| assert_eq!(actual.trim(), expected.trim()); | ||
|
|
||
| let mapping = ProguardMapping::new(SYNTHETIC_LAMBDA_METHOD_WITH_INLINING_MAPPING.as_bytes()); | ||
| let mut buf = Vec::new(); | ||
| ProguardCache::write(&mapping, &mut buf).unwrap(); | ||
| let cache = ProguardCache::parse(&buf).unwrap(); | ||
| cache.test(); | ||
|
|
||
| let actual = cache.remap_stacktrace(input).unwrap(); | ||
| assert_eq!(actual.trim(), expected.trim()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_synthetic_lambda_method_with_inlining_synthesized_flag() { | ||
| let mapper = ProguardMapper::from(SYNTHETIC_LAMBDA_METHOD_WITH_INLINING_MAPPING); | ||
|
|
||
| // Inline expansion from a synthetic class: the run() member is synthesized | ||
| // but the inlined lambda$main$0 is from example.Foo (not synthesized). | ||
| let frame = StackFrame::try_parse(" at a.b.a(Unknown Source:4)".as_bytes()).unwrap(); | ||
| let remapped: Vec<_> = mapper.remap_frame(&frame).collect(); | ||
| assert_eq!(remapped.len(), 2); | ||
| // lambda$main$0 is from example.Foo — not synthesized | ||
| assert!( | ||
| !remapped[0].method_synthesized(), | ||
| "inlined frame should not be synthesized" | ||
| ); | ||
| // run() is from the synthetic class — synthesized | ||
| assert!( | ||
| remapped[1].method_synthesized(), | ||
| "outer synthetic frame should be synthesized" | ||
| ); | ||
| } | ||
|
|
||
| // ============================================================================= | ||
| // MovedSynthetizedInfoStackTraceTest | ||
| // ============================================================================= | ||
|
|
||
| const MOVED_SYNTHETIZED_INFO_MAPPING: &str = "\ | ||
| # {\"id\":\"com.android.tools.r8.mapping\",\"version\":\"2.2\"} | ||
| com.android.tools.r8.BaseCommand$Builder -> foo.bar: | ||
| 1:1:void inlinee(java.util.Collection):0:0 -> inlinee$synthetic | ||
| 1:1:void inlinee$synthetic(java.util.Collection):0:0 -> inlinee$synthetic | ||
| 2:2:void inlinee(java.util.Collection):206:206 -> inlinee$synthetic | ||
| 2:2:void inlinee$synthetic(java.util.Collection):0:0 -> inlinee$synthetic | ||
| # {\"id\":\"com.android.tools.r8.synthesized\"} | ||
| 4:4:void inlinee(java.util.Collection):208:208 -> inlinee$synthetic | ||
| 4:4:void inlinee$synthetic(java.util.Collection):0 -> inlinee$synthetic | ||
| 7:7:void error(origin.Origin,java.lang.Throwable):363:363 -> inlinee$synthetic | ||
| 7:7:void inlinee(java.util.Collection):210 -> inlinee$synthetic | ||
| 7:7:void inlinee$synthetic(java.util.Collection):0:0 -> inlinee$synthetic | ||
| "; | ||
|
|
||
| #[test] | ||
| fn test_moved_synthetized_info_stacktrace() { | ||
| let input = "\ | ||
| java.lang.RuntimeException: foobar | ||
| \tat foo.bar.inlinee$synthetic(BaseCommand.java:2) | ||
| "; | ||
|
|
||
| // The inlined pair at line 2: inlinee (original:206) + inlinee$synthetic (original:0). | ||
| // The inlinee$synthetic method is marked synthesized; inlinee is not. | ||
| let expected = "\ | ||
| java.lang.RuntimeException: foobar | ||
| at com.android.tools.r8.BaseCommand$Builder.inlinee(BaseCommand.java:206) | ||
| at com.android.tools.r8.BaseCommand$Builder.inlinee$synthetic(BaseCommand.java:0) | ||
| "; | ||
|
|
||
| let mapper = ProguardMapper::from(MOVED_SYNTHETIZED_INFO_MAPPING); | ||
| let actual = mapper.remap_stacktrace(input).unwrap(); | ||
| assert_eq!(actual.trim(), expected.trim()); | ||
|
|
||
| let mapping = ProguardMapping::new(MOVED_SYNTHETIZED_INFO_MAPPING.as_bytes()); | ||
| let mut buf = Vec::new(); | ||
| ProguardCache::write(&mapping, &mut buf).unwrap(); | ||
| let cache = ProguardCache::parse(&buf).unwrap(); | ||
| cache.test(); | ||
|
|
||
| let actual = cache.remap_stacktrace(input).unwrap(); | ||
| assert_eq!(actual.trim(), expected.trim()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_moved_synthetized_info_synthesized_flag() { | ||
| let mapper = ProguardMapper::from(MOVED_SYNTHETIZED_INFO_MAPPING); | ||
|
|
||
| let frame = | ||
| StackFrame::try_parse("\tat foo.bar.inlinee$synthetic(BaseCommand.java:2)".as_bytes()) | ||
| .unwrap(); | ||
| let remapped: Vec<_> = mapper.remap_frame(&frame).collect(); | ||
| assert_eq!(remapped.len(), 2); | ||
| // inlinee — not synthesized | ||
| assert!( | ||
| !remapped[0].method_synthesized(), | ||
| "inlinee should not be synthesized" | ||
| ); | ||
| // inlinee$synthetic — synthesized | ||
| assert!( | ||
| remapped[1].method_synthesized(), | ||
| "inlinee$synthetic should be synthesized" | ||
| ); | ||
| } | ||
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.
Uh oh!
There was an error while loading. Please reload this page.