Skip to content
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
35 changes: 33 additions & 2 deletions src/sentry/lang/javascript/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import zlib
from datetime import datetime
from io import BytesIO
from itertools import groupby
from os.path import splitext
from typing import IO, Optional, Tuple
from urllib.parse import urlsplit
Expand Down Expand Up @@ -843,6 +844,36 @@ def get_function_for_token(frame, token, previous_frame=None):
return frame_function_name


def fold_function_name(function_name):
"""
Fold multiple consecutive occurences of the same property name into a single group, excluding the last component.

foo | foo
foo.foo | foo.foo
foo.foo.foo | {foo#2}.foo
bar.foo.foo | bar.foo.foo
bar.foo.foo.foo | bar.{foo#2}.foo
bar.foo.foo.onError | bar.{foo#2}.onError
bar.bar.bar.foo.foo.onError | {bar#3}.{foo#2}.onError
bar.foo.foo.bar.bar.onError | bar.{foo#2}.{bar#2}.onError
"""

parts = function_name.split(".")

if len(parts) == 1:
return function_name

tail = parts.pop()
grouped = [list(g) for _, g in groupby(parts)]

def format_groups(p):
if len(p) == 1:
return p[0]
return f"\u007b{p[0]}#{len(p)}\u007d"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay with that but for the record you can do f"{{{p[0]}#{len(p)}}}".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need more cowbell braces.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replit was not happy about this syntax for some reason, so I went with codes directly

image


return f'{".".join(map(format_groups, grouped))}.{tail}'


class JavaScriptStacktraceProcessor(StacktraceProcessor):
"""
Modern SourceMap processor using symbolic-sourcemapcache.
Expand Down Expand Up @@ -1030,8 +1061,8 @@ def process_frame(self, processable_frame, processing_task):
# The tokens are 1-indexed.
new_frame["lineno"] = token.line
new_frame["colno"] = token.col
new_frame["function"] = get_function_for_token(
new_frame, token, processable_frame.previous_frame
new_frame["function"] = fold_function_name(
get_function_for_token(new_frame, token, processable_frame.previous_frame)
)

filename = token.src
Expand Down
13 changes: 13 additions & 0 deletions tests/sentry/lang/javascript/test_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
fetch_release_archive_for_url,
fetch_release_file,
fetch_sourcemap,
fold_function_name,
generate_module,
get_function_for_token,
get_max_age,
Expand Down Expand Up @@ -1143,6 +1144,18 @@ def test_fallback_to_original_name(self):
assert get_function_for_token(frame, token, previous_frame) == "original"


class FoldFunctionNameTest(unittest.TestCase):
def test_dedupe_properties(self):
assert fold_function_name("foo") == "foo"
assert fold_function_name("foo.foo") == "foo.foo"
assert fold_function_name("foo.foo.foo") == "{foo#2}.foo"
assert fold_function_name("bar.foo.foo") == "bar.foo.foo"
assert fold_function_name("bar.foo.foo.foo") == "bar.{foo#2}.foo"
assert fold_function_name("bar.foo.foo.onError") == "bar.{foo#2}.onError"
assert fold_function_name("bar.bar.bar.foo.foo.onError") == "{bar#3}.{foo#2}.onError"
assert fold_function_name("bar.foo.foo.bar.bar.onError") == "bar.{foo#2}.{bar#2}.onError"


class FetchSourcemapTest(TestCase):
def test_simple_base64(self):
smap_view = fetch_sourcemap(base64_sourcemap)
Expand Down