objc-js 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -32,7 +32,12 @@ class NobjcLibrary {
|
|
|
32
32
|
LoadLibrary(library);
|
|
33
33
|
this.wasLoaded = true;
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
const classObject = GetClassObject(className);
|
|
36
|
+
if (classObject === undefined) {
|
|
37
|
+
// Class not found. Make sure the class exists before trying to access it.
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
cls = new NobjcObject(classObject);
|
|
36
41
|
classCache.set(className, cls);
|
|
37
42
|
return cls;
|
|
38
43
|
}
|
|
@@ -55,6 +60,9 @@ class NobjcObject {
|
|
|
55
60
|
// Return true for the special Symbol to enable unwrapping
|
|
56
61
|
if (p === NATIVE_OBJC_OBJECT)
|
|
57
62
|
return true;
|
|
63
|
+
// Return true for inspect symbols so console.log uses custom inspect
|
|
64
|
+
if (p === customInspectSymbol)
|
|
65
|
+
return true;
|
|
58
66
|
// guard against other symbols
|
|
59
67
|
if (typeof p === "symbol")
|
|
60
68
|
return Reflect.has(target, p);
|
|
@@ -117,6 +125,10 @@ class NobjcObject {
|
|
|
117
125
|
// object (avoids triggering proxy 'has' trap which would be a second FFI call)
|
|
118
126
|
const selector = NobjcMethodNameToObjcSelector(methodName);
|
|
119
127
|
if (!target.$respondsToSelector(selector)) {
|
|
128
|
+
// special case since JS checks for `.then` on Promise objects
|
|
129
|
+
if (methodName === "then")
|
|
130
|
+
return undefined;
|
|
131
|
+
// Otherwise, throw an error
|
|
120
132
|
throw new Error(`Method ${methodName} not found on object`);
|
|
121
133
|
}
|
|
122
134
|
method = NobjcMethod(object, methodName);
|
|
@@ -127,6 +139,9 @@ class NobjcObject {
|
|
|
127
139
|
};
|
|
128
140
|
// Create the proxy
|
|
129
141
|
const proxy = new Proxy(object, handler);
|
|
142
|
+
// Set custom inspect on the native object so console.log works through the Proxy.
|
|
143
|
+
// Runtimes (Node, Bun) bypass Proxy traps during inspect and read the target directly.
|
|
144
|
+
object[customInspectSymbol] = () => proxy.toString();
|
|
130
145
|
// Store proxy → native mapping in WeakMap for O(1) unwrap (bypasses Proxy traps)
|
|
131
146
|
nativeObjectMap.set(proxy, object);
|
|
132
147
|
// Return the proxy
|
package/package.json
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"prebuild:x64": "prebuildify --napi --strip --arch x64 -r node",
|
|
47
47
|
"prebuild:all": "bun run prebuild:arm64 && bun run prebuild:x64"
|
|
48
48
|
},
|
|
49
|
-
"version": "1.3.
|
|
49
|
+
"version": "1.3.1",
|
|
50
50
|
"description": "Objective-C bridge for Node.js",
|
|
51
51
|
"main": "dist/index.js",
|
|
52
52
|
"dependencies": {
|
|
Binary file
|
|
Binary file
|
package/src/native/ObjcObject.h
CHANGED
|
@@ -4,11 +4,18 @@
|
|
|
4
4
|
#include <napi.h>
|
|
5
5
|
#include <objc/objc.h>
|
|
6
6
|
#include <objc/runtime.h>
|
|
7
|
+
#include <objc/message.h>
|
|
7
8
|
#include <optional>
|
|
8
9
|
#include <variant>
|
|
9
10
|
#include <unordered_map>
|
|
10
11
|
#include <vector>
|
|
11
12
|
|
|
13
|
+
// objc_retain/objc_release are part of the stable ObjC ABI (macOS 10.12+)
|
|
14
|
+
// but not declared in public headers. We use them for manual reference counting
|
|
15
|
+
// since ARC is not enabled for .mm files in this project.
|
|
16
|
+
extern "C" id objc_retain(id value);
|
|
17
|
+
extern "C" void objc_release(id value);
|
|
18
|
+
|
|
12
19
|
#ifdef __OBJC__
|
|
13
20
|
@class NSMethodSignature;
|
|
14
21
|
#else
|
|
@@ -51,6 +58,14 @@ public:
|
|
|
51
58
|
// This better be an Napi::External<id>! We lost the type info at runtime.
|
|
52
59
|
Napi::External<id> external = info[0].As<Napi::External<id>>();
|
|
53
60
|
objcObject = *(external.Data());
|
|
61
|
+
// Retain the ObjC object so it stays alive as long as this JS wrapper
|
|
62
|
+
// exists. Without this, ARC/autorelease can deallocate the object while
|
|
63
|
+
// JS still holds a reference, causing Use-After-Free crashes (SIGTRAP)
|
|
64
|
+
// in completion handler callbacks and other async contexts.
|
|
65
|
+
// Note: ARC is not enabled for .mm files in this project (the -fobjc-arc
|
|
66
|
+
// flag is in OTHER_CFLAGS, not OTHER_CPLUSPLUSFLAGS), so __strong has
|
|
67
|
+
// no effect — we must manage retain/release manually.
|
|
68
|
+
if (objcObject) objc_retain(objcObject);
|
|
54
69
|
return;
|
|
55
70
|
}
|
|
56
71
|
// If someone tries `new ObjcObject()` from JS, forbid it:
|
|
@@ -58,7 +73,12 @@ public:
|
|
|
58
73
|
.ThrowAsJavaScriptException();
|
|
59
74
|
}
|
|
60
75
|
static Napi::Object NewInstance(Napi::Env env, id obj);
|
|
61
|
-
~ObjcObject()
|
|
76
|
+
~ObjcObject() {
|
|
77
|
+
if (objcObject) {
|
|
78
|
+
objc_release(objcObject);
|
|
79
|
+
objcObject = nil;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
62
82
|
|
|
63
83
|
private:
|
|
64
84
|
Napi::Value $MsgSend(const Napi::CallbackInfo &info);
|