Skip to content

Commit 262cd87

Browse files
committed
chore: cleanup stack trace handling for error display
1 parent 9e0acc6 commit 262cd87

1 file changed

Lines changed: 13 additions & 129 deletions

File tree

NativeScript/runtime/NativeScriptException.mm

Lines changed: 13 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
std::string rawStack;
3333
std::string canonicalStack;
3434
std::string consolePayload;
35-
int canonicalQuality = -1;
3635
};
3736

3837
static std::mutex gErrorDisplayMutex;
@@ -56,7 +55,6 @@ static void ShowErrorModalSynchronously(const std::string& title,
5655
static void ScheduleFallbackPresentation(uint64_t ticket);
5756
static void PresentFallbackIfNeeded(uint64_t ticket);
5857
static std::string ResolveDisplayStack(const PendingErrorDisplay& state);
59-
static int EvaluateStackQuality(const std::string& stackText);
6058
static void ConsiderStackCandidate(PendingErrorDisplay& state, v8::Isolate* isolate,
6159
const std::string& candidateStack);
6260

@@ -509,7 +507,6 @@ static void ConsiderStackCandidate(PendingErrorDisplay& state, v8::Isolate* isol
509507
gPendingErrorDisplay.rawStack = stackTrace;
510508
gPendingErrorDisplay.consolePayload.clear();
511509
gPendingErrorDisplay.canonicalStack.clear();
512-
gPendingErrorDisplay.canonicalQuality = -1;
513510
ConsiderStackCandidate(gPendingErrorDisplay, isolate, stackTrace);
514511
ticketToSchedule = gPendingErrorDisplay.ticket;
515512
}
@@ -540,7 +537,6 @@ static void ConsiderStackCandidate(PendingErrorDisplay& state, v8::Isolate* isol
540537
gPendingErrorDisplay.isolate = payloadIsolate;
541538
}
542539
gPendingErrorDisplay.canonicalStack = text;
543-
gPendingErrorDisplay.canonicalQuality = std::numeric_limits<int>::max();
544540
};
545541

546542
{
@@ -561,7 +557,6 @@ static void ConsiderStackCandidate(PendingErrorDisplay& state, v8::Isolate* isol
561557
if (gPendingErrorDisplay.ticket == 0) {
562558
gPendingErrorDisplay.ticket = gNextErrorTicket++;
563559
gPendingErrorDisplay.canonicalStack.clear();
564-
gPendingErrorDisplay.canonicalQuality = -1;
565560
}
566561

567562
if (!gPendingErrorDisplay.contextCaptured && !gPendingErrorDisplay.modalPresented) {
@@ -638,125 +633,30 @@ static void PresentFallbackIfNeeded(uint64_t ticket) {
638633
}
639634

640635
static std::string ResolveDisplayStack(const PendingErrorDisplay& state) {
636+
// Deterministic preference: canonicalStack > consolePayload > rawStack > message
637+
// Remap when possible so the UI matches terminal output.
641638
if (!state.canonicalStack.empty()) {
642-
size_t previewLen = std::min<size_t>(state.canonicalStack.size(), 120);
643-
std::string preview = state.canonicalStack.substr(0, previewLen);
644-
// Log(@"[ErrorDisplay] resolved stack (canonical) len=%zu preview=%@",
645-
// state.canonicalStack.size(), [NSString stringWithUTF8String:preview.c_str()]);
646639
return state.canonicalStack;
647640
}
648641

649-
std::string bestStack;
650-
int bestQuality = std::numeric_limits<int>::min();
651-
652-
auto consider = [&](const std::string& candidate, v8::Isolate* isolate) {
653-
if (candidate.empty()) {
654-
return;
655-
}
656-
std::string normalized = candidate;
657-
if (isolate != nullptr) {
658-
std::string remapped = tns::RemapStackTraceIfAvailable(isolate, candidate);
642+
auto remapIfPossible = [&](const std::string& text) -> std::string {
643+
if (text.empty()) return std::string();
644+
if (state.isolate != nullptr) {
645+
std::string remapped = tns::RemapStackTraceIfAvailable(state.isolate, text);
659646
if (!remapped.empty()) {
660-
normalized = remapped;
647+
return remapped;
661648
}
662649
}
663-
int candidateQuality = EvaluateStackQuality(normalized);
664-
size_t previewLen = std::min<size_t>(normalized.size(), 120);
665-
std::string preview = normalized.substr(0, previewLen);
666-
// Log(@"[ErrorDisplay] consider: quality=%d len=%zu preview=%@", candidateQuality,
667-
// normalized.size(), [NSString stringWithUTF8String:preview.c_str()]);
668-
if (candidateQuality > bestQuality ||
669-
(candidateQuality == bestQuality && normalized.size() > bestStack.size())) {
670-
bestStack = normalized;
671-
bestQuality = candidateQuality;
672-
}
650+
return text;
673651
};
674652

675-
if (!state.canonicalStack.empty()) {
676-
consider(state.canonicalStack, nullptr);
677-
}
678653
if (!state.consolePayload.empty()) {
679-
consider(state.consolePayload, state.isolate);
654+
return remapIfPossible(state.consolePayload);
680655
}
681656
if (!state.rawStack.empty()) {
682-
consider(state.rawStack, state.isolate);
683-
}
684-
685-
if (bestStack.empty()) {
686-
return state.message;
687-
}
688-
689-
size_t finalPreviewLen = std::min<size_t>(bestStack.size(), 120);
690-
std::string finalPreview = bestStack.substr(0, finalPreviewLen);
691-
// Log(@"[ErrorDisplay] resolved stack quality=%d len=%zu preview=%@", bestQuality,
692-
// bestStack.size(), [NSString stringWithUTF8String:finalPreview.c_str()]);
693-
694-
return bestStack;
695-
}
696-
697-
static size_t CountOccurrences(const std::string& haystack, const std::string& needle) {
698-
if (haystack.empty() || needle.empty()) {
699-
return 0;
700-
}
701-
size_t count = 0;
702-
size_t pos = haystack.find(needle, 0);
703-
while (pos != std::string::npos) {
704-
++count;
705-
pos = haystack.find(needle, pos + needle.length());
657+
return remapIfPossible(state.rawStack);
706658
}
707-
return count;
708-
}
709-
710-
static int EvaluateStackQuality(const std::string& stackText) {
711-
if (stackText.empty()) {
712-
return std::numeric_limits<int>::min();
713-
}
714-
715-
auto hasAny = [&](const std::initializer_list<const char*>& tokens) {
716-
for (const auto* token : tokens) {
717-
if (stackText.find(token) != std::string::npos) {
718-
return true;
719-
}
720-
}
721-
return false;
722-
};
723-
724-
int score = 0;
725-
726-
size_t tsFrames = CountOccurrences(stackText, ".ts:") +
727-
CountOccurrences(stackText, ".tsx:") +
728-
CountOccurrences(stackText, ".vue:");
729-
if (tsFrames > 0) {
730-
score += 60;
731-
score += static_cast<int>(tsFrames) * 5;
732-
}
733-
734-
if (hasAny({"webpack:/", "file: src/", "sourceURL"})) {
735-
score += 20;
736-
}
737-
738-
size_t newlineCount = CountOccurrences(stackText, "\n");
739-
if (newlineCount > 0) {
740-
score += static_cast<int>(std::min<size_t>(20, newlineCount));
741-
}
742-
743-
if (stackText.find(" at ") != std::string::npos) {
744-
score += 10;
745-
}
746-
747-
int penalty = 0;
748-
penalty += static_cast<int>(CountOccurrences(stackText, "file:///app/")) * 3;
749-
penalty += static_cast<int>(CountOccurrences(stackText, ".bundle.js")) * 2;
750-
penalty = std::min(penalty, score / 2); // don't let bundle frames dominate completely
751-
score -= penalty;
752-
753-
score += static_cast<int>(std::min<size_t>(10, stackText.size() / 400));
754-
755-
if (score <= 0) {
756-
score = 1; // ensure non-empty strings beat truly empty candidates
757-
}
758-
759-
return score;
659+
return state.message;
760660
}
761661

762662
static void ConsiderStackCandidate(PendingErrorDisplay& state, v8::Isolate* isolate,
@@ -773,26 +673,10 @@ static void ConsiderStackCandidate(PendingErrorDisplay& state, v8::Isolate* isol
773673
normalized = remapped;
774674
}
775675
}
776-
777-
int quality = EvaluateStackQuality(normalized);
778-
if (quality < 0 && state.canonicalQuality >= 0) {
779-
return;
780-
}
781-
782-
bool shouldReplace = false;
783-
if (quality > state.canonicalQuality) {
784-
shouldReplace = true;
785-
} else if (quality == state.canonicalQuality && normalized.size() > state.canonicalStack.size()) {
786-
shouldReplace = true;
787-
}
788-
676+
// Deterministic behavior: if no canonical stack yet, set it to the first available candidate.
677+
// Console payloads will explicitly override canonicalStack elsewhere.
789678
if (state.canonicalStack.empty()) {
790-
shouldReplace = true;
791-
}
792-
793-
if (shouldReplace) {
794679
state.canonicalStack = normalized;
795-
state.canonicalQuality = quality;
796680
}
797681
}
798682

0 commit comments

Comments
 (0)