| 
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