|
7 | 7 | #include "Interop.h" |
8 | 8 | #include "Helpers.h" |
9 | 9 | #include "Runtime.h" |
| 10 | +#include "RuntimeConfig.h" |
10 | 11 |
|
11 | 12 | using namespace v8; |
12 | 13 | using namespace std; |
|
27 | 28 | bool callSuper = false; |
28 | 29 | if (instanceMethod) { |
29 | 30 | BaseDataWrapper* wrapper = tns::GetValue(isolate, receiver); |
30 | | - tns::Assert(wrapper != nullptr, isolate); |
| 31 | + |
| 32 | + if (wrapper == nullptr) { |
| 33 | + // During fast view churn like HMR in development, JS objects can outlive their |
| 34 | + // native wrappers briefly. In Debug, throw a catchable JS error instead of crashing. |
| 35 | + // In Release, assert so crash reporting can capture unexpected cases. |
| 36 | + if (RuntimeConfig.IsDebug) { |
| 37 | + const char* selectorStr = meta ? meta->selectorAsString() : "<unknown>"; |
| 38 | + const char* jsNameStr = meta ? meta->jsName() : "<unknown>"; |
| 39 | + const char* classNameStr = klass ? class_getName(klass) : "<unknown>"; |
| 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))); |
| 44 | + return v8::Undefined(isolate); |
| 45 | + } else { |
| 46 | + tns::Assert(false, isolate); |
| 47 | + } |
| 48 | + } |
31 | 49 |
|
32 | 50 | if (wrapper->Type() == WrapperType::ObjCAllocObject) { |
33 | 51 | ObjCAllocDataWrapper* allocWrapper = static_cast<ObjCAllocDataWrapper*>(wrapper); |
|
43 | 61 | // For extended classes we will call the base method |
44 | 62 | callSuper = isMethodCallback && it != cache->ClassPrototypes.end(); |
45 | 63 | } else { |
46 | | - tns::Assert(false, isolate); |
| 64 | + if (RuntimeConfig.IsDebug) { |
| 65 | + const char* selectorStr = meta ? meta->selectorAsString() : "<unknown>"; |
| 66 | + const char* jsNameStr = meta ? meta->jsName() : "<unknown>"; |
| 67 | + const char* classNameStr = klass ? class_getName(klass) : "<unknown>"; |
| 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))); |
| 73 | + return v8::Undefined(isolate); |
| 74 | + } else { |
| 75 | + tns::Assert(false, isolate); |
| 76 | + } |
47 | 77 | } |
48 | 78 | } |
49 | 79 |
|
|
878 | 908 | Local<Object> thiz = args.This(); |
879 | 909 | Isolate* isolate = args.GetIsolate(); |
880 | 910 | BaseDataWrapper* wrapper = tns::GetValue(isolate, thiz); |
881 | | - if (wrapper == nullptr && wrapper->Type() != WrapperType::ObjCObject) { |
| 911 | + if (wrapper == nullptr || wrapper->Type() != WrapperType::ObjCObject) { |
882 | 912 | return; |
883 | 913 | } |
884 | 914 |
|
|
0 commit comments