Skip to content

Commit 92a5f3d

Browse files
committed
fix: throw JS exception instead of silent warning for disposed native object calls in debug mode
1 parent 17ed63a commit 92a5f3d

1 file changed

Lines changed: 10 additions & 16 deletions

File tree

NativeScript/runtime/ArgConverter.mm

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include <Foundation/Foundation.h>
22
#include <sstream>
3-
#include <unordered_set>
43
#include "ArgConverter.h"
54
#include "NativeScriptException.h"
65
#include "DictionaryAdapter.h"
@@ -32,19 +31,16 @@
3231

3332
if (wrapper == nullptr) {
3433
// During fast view churn like HMR in development, JS objects can outlive their
35-
// native wrappers briefly. In Debug, avoid a crash and just skip the native call.
34+
// native wrappers briefly. In Debug, throw a catchable JS error instead of crashing.
3635
// In Release, assert so crash reporting can capture unexpected cases.
3736
if (RuntimeConfig.IsDebug) {
3837
const char* selectorStr = meta ? meta->selectorAsString() : "<unknown>";
3938
const char* jsNameStr = meta ? meta->jsName() : "<unknown>";
4039
const char* classNameStr = klass ? class_getName(klass) : "<unknown>";
41-
// Suppress duplicate logs: only log once per class+selector for this process.
42-
static std::unordered_set<std::string> s_logged;
43-
std::string key = std::string(classNameStr) + ":" + selectorStr;
44-
if (s_logged.insert(key).second) {
45-
Log(@"Note: ignore method on non-native receiver (class: %s, selector: %s, jsName: %s, args: %d). Common during HMR.",
46-
classNameStr, selectorStr, jsNameStr, (int)args.Length());
47-
}
40+
std::string errMsg = std::string("Cannot call method '") + jsNameStr +
41+
"' on a disposed native object (class: " + classNameStr +
42+
", selector: " + selectorStr + "). This can happen during HMR or fast view churn.";
43+
isolate->ThrowException(Exception::Error(tns::ToV8String(isolate, errMsg)));
4844
return v8::Undefined(isolate);
4945
} else {
5046
tns::Assert(false, isolate);
@@ -69,13 +65,11 @@
6965
const char* selectorStr = meta ? meta->selectorAsString() : "<unknown>";
7066
const char* jsNameStr = meta ? meta->jsName() : "<unknown>";
7167
const char* classNameStr = klass ? class_getName(klass) : "<unknown>";
72-
// Suppress duplicate logs: only log once per class+selector for this process.
73-
static std::unordered_set<std::string> s_logged;
74-
std::string key = std::string(classNameStr) + ":" + selectorStr;
75-
if (s_logged.insert(key).second) {
76-
Log(@"Note: ignore receiver wrapper type %d (class: %s, selector: %s, jsName: %s). Common during HMR.",
77-
(int)wrapper->Type(), classNameStr, selectorStr, jsNameStr);
78-
}
68+
std::string errMsg = std::string("Unexpected receiver wrapper type ") +
69+
std::to_string((int)wrapper->Type()) + " for method '" + jsNameStr +
70+
"' (class: " + classNameStr + ", selector: " + selectorStr +
71+
"). This can happen during HMR or fast view churn.";
72+
isolate->ThrowException(Exception::Error(tns::ToV8String(isolate, errMsg)));
7973
return v8::Undefined(isolate);
8074
} else {
8175
tns::Assert(false, isolate);

0 commit comments

Comments
 (0)