|
1 | 1 | #include <Foundation/Foundation.h> |
2 | 2 | #include <sstream> |
| 3 | +#include <unordered_set> |
3 | 4 | #include "ArgConverter.h" |
4 | 5 | #include "NativeScriptException.h" |
5 | 6 | #include "DictionaryAdapter.h" |
6 | 7 | #include "ObjectManager.h" |
7 | 8 | #include "Interop.h" |
8 | 9 | #include "Helpers.h" |
9 | 10 | #include "Runtime.h" |
| 11 | +#include "RuntimeConfig.h" |
10 | 12 |
|
11 | 13 | using namespace v8; |
12 | 14 | using namespace std; |
|
27 | 29 | bool callSuper = false; |
28 | 30 | if (instanceMethod) { |
29 | 31 | BaseDataWrapper* wrapper = tns::GetValue(isolate, receiver); |
30 | | - tns::Assert(wrapper != nullptr, isolate); |
| 32 | + |
| 33 | + if (wrapper == nullptr) { |
| 34 | + // 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. |
| 36 | + // In Release, assert so crash reporting can capture unexpected cases. |
| 37 | + if (RuntimeConfig.IsDebug) { |
| 38 | + const char* selectorStr = meta ? meta->selectorAsString() : "<unknown>"; |
| 39 | + const char* jsNameStr = meta ? meta->jsName() : "<unknown>"; |
| 40 | + 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 | + } |
| 48 | + return v8::Undefined(isolate); |
| 49 | + } else { |
| 50 | + tns::Assert(false, isolate); |
| 51 | + } |
| 52 | + } |
31 | 53 |
|
32 | 54 | if (wrapper->Type() == WrapperType::ObjCAllocObject) { |
33 | 55 | ObjCAllocDataWrapper* allocWrapper = static_cast<ObjCAllocDataWrapper*>(wrapper); |
|
43 | 65 | // For extended classes we will call the base method |
44 | 66 | callSuper = isMethodCallback && it != cache->ClassPrototypes.end(); |
45 | 67 | } else { |
46 | | - tns::Assert(false, isolate); |
| 68 | + if (RuntimeConfig.IsDebug) { |
| 69 | + const char* selectorStr = meta ? meta->selectorAsString() : "<unknown>"; |
| 70 | + const char* jsNameStr = meta ? meta->jsName() : "<unknown>"; |
| 71 | + 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 | + } |
| 79 | + return v8::Undefined(isolate); |
| 80 | + } else { |
| 81 | + tns::Assert(false, isolate); |
| 82 | + } |
47 | 83 | } |
48 | 84 | } |
49 | 85 |
|
|
878 | 914 | Local<Object> thiz = args.This(); |
879 | 915 | Isolate* isolate = args.GetIsolate(); |
880 | 916 | BaseDataWrapper* wrapper = tns::GetValue(isolate, thiz); |
881 | | - if (wrapper == nullptr && wrapper->Type() != WrapperType::ObjCObject) { |
| 917 | + if (wrapper == nullptr || wrapper->Type() != WrapperType::ObjCObject) { |
882 | 918 | return; |
883 | 919 | } |
884 | 920 |
|
|
0 commit comments