Skip to content
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

fix(sourcemap): Improve scope name resolution #786

Merged
merged 6 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

**Fixes**:

- Optimze SourceBundle/DebugSession ([#787](https://github.com/getsentry/symbolic/pull/787))
- sourcemapcache: Improved scope name resolution. ([#786](https://github.com/getsentry/symbolic/pull/786))
- Optimize SourceBundle/DebugSession ([#787](https://github.com/getsentry/symbolic/pull/787))

## 12.1.3

Expand Down
44 changes: 42 additions & 2 deletions symbolic-sourcemapcache/src/writer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::io::Write;
use std::ops::Range;

use itertools::Itertools;
use js_source_scopes::{
Expand Down Expand Up @@ -71,14 +72,27 @@ impl SourceMapCacheWriter {
// resolve scopes to original names
let ctx = SourceContext::new(source).map_err(SourceMapCacheErrorInner::SourceContext)?;
let resolver = NameResolver::new(&ctx, &sm);

let scopes: Vec<_> = tracing::trace_span!("resolve original names").in_scope(|| {
scopes
.into_iter()
.map(|(range, name)| {
let name = name
let orig_name = name.as_ref().map(|name| name.to_string());
let resolved_name = name
.map(|n| resolver.resolve_name(&n))
.filter(|s| !s.is_empty());
(range, name)

// A hack specifically for Flutter. If the resolved scope name is the same as the original name,
// that indicates that we probably couldn't resolve the scope. In that case, we find the name
// at the very end of the scope, if it exists, and use it instead of the "conventionally"
// resolved scope.
let name_at_end_of_scope = if orig_name == resolved_name {
Self::try_resolve_closing_name(&ctx, &sm, range.clone())
} else {
None
};

(range, name_at_end_of_scope.or(resolved_name))
})
.collect()
});
Expand Down Expand Up @@ -211,6 +225,32 @@ impl SourceMapCacheWriter {
})
}

/// Returns the name attached to the token at the given range's end, if any.
fn try_resolve_closing_name(
ctx: &SourceContext<&str>,
sourcemap: &DecodedMap,
range: Range<u32>,
) -> Option<String> {
let sp = ctx.offset_to_position(range.end - 1)?;
let token = sourcemap.lookup_token(sp.line, sp.column)?;

// Validate that the token really is exactly at the scope's end
if token.get_dst() != (sp.line, sp.column) {
return None;
}

let sp_past_end = ctx.offset_to_position(range.end);
let token_past_end = sp_past_end.and_then(|sp| sourcemap.lookup_token(sp.line, sp.column));

// Validate that the token one past the scope's end (if it exists) is different
if token_past_end == Some(token) {
return None;
}

let token_name = token.get_name()?;
Some(token_name.to_owned())
}

/// Serialize the converted data.
///
/// This writes the SourceMapCache binary format into the given [`Write`].
Expand Down