dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.19k stars 1.57k forks source link

Compiled function token has no original name mapping in produced source map #52483

Open kamilogorek opened 1 year ago

kamilogorek commented 1 year ago

It appears that produced source map mappings are generated in a quite odd name. The produced function token doesn't have a valid reference to its original name, however, brackets that are part of that scope do, eg:

Foo.prototype={
abc(){...}
// ^     ^
// |     | closing brace of fn body
// | opening paren of argument list
}

Or a more concrete example:

A.Tt.prototype={
$0(){throw A.c(A.c2("Test exception"))},
$S:0}

The A.Tt.prototype.$0 scope can’t be resolved, but columns 3 (open paren), 6 (throw), 16 (the second A), and 39 (closing brace) in the second line refer to the correct name.

We worked around this in our processing pipeline here https://github.com/getsentry/symbolic/pull/786 but would be great to understand how is this exactly produced, how should we interpret this, and potentially fix (if one applies) this in future releases.

I was asked to do so, thus cc: @sigmundch

kamilogorek commented 1 year ago

cc @Swatinem @loewenheim @marandaneto for visibility.

marandaneto commented 1 year ago

@kamilogorek we can look at their own symbolicator source code:

source_map_stack_trace source_maps stack_trace native_stack_traces

But let's wait for the Dart team to follow up since we already have a workaround.

sigmundch commented 1 year ago

Thanks for reaching out!

I'm curious to learn more about what does your symbolication service provides and whether it matches how we produce source-maps.

Our encoding of source-map was tailored for two use cases: local debugging and deobfuscation of production stack traces. Years ago we had a lot of symbol information that was never in use and we started noticing the tax (e.g. memory utilization on deobfuscation servers). As a result, today we use a frugal approach and only include symbol information if it supports known scenarios.

We hope that one day source-maps will consider incorporating some of these extensions in the future into the standard, but that will take time. This is being tracked in https://github.com/source-map/source-map-rfc/issues/40

In terms of code - note that the links shared by @marandaneto are packages that make use of regular source-maps without our custom extensions. I'd recommend also looking at the dart2js deobfuscator. You'll see the use of the minified data where we translate error messages, and the use of the inline data in the logic mapping stack traces.

To address the specific question about the ( containing the mapping: there is a step in the deobfuscator that looks for the enclosing function to figure out the name. Long ago, dart2js used to produce ES5 code which contained the function keyword. We used this to identify the enclosing function and put the mapping to the original name there. Once we started emitting ES6 code, we decided to use the open parenthesis as the unique position where we located the start of the function. Our deobfuscator is tailored to find this position as well (see sourcemap_helper#L38).

loewenheim commented 1 year ago

At Sentry we try to resolve the scope a token is in and use that as the function name because the name is often something like apply with no further context. It appears in this situation that is exactly the wrong thing to do :/