|
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