objc-js 1.4.0 → 1.5.0
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.d.ts +32 -1
- package/dist/index.js +48 -1
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/src/native/ObjcObject.h +7 -1
- package/src/native/ObjcObject.mm +21 -4
- package/src/native/bridge.h +1 -1
- package/src/native/nobjc.mm +6 -1
- package/src/native/nobjc_block.h +84 -8
- package/src/native/subclass-impl.mm +2 -2
- package/src/native/type-conversion.h +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,37 @@ declare class NobjcObject {
|
|
|
7
7
|
[key: string]: NobjcMethod;
|
|
8
8
|
constructor(object: NobjcNative.ObjcObject);
|
|
9
9
|
}
|
|
10
|
+
export interface TypedBlockOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Block return type encoding. Example: "v" (void), "@" (id), "B" (BOOL).
|
|
13
|
+
*/
|
|
14
|
+
returns: string;
|
|
15
|
+
/**
|
|
16
|
+
* Block argument type encodings, excluding the implicit block-self (@?) parameter.
|
|
17
|
+
* Example: ["@", "Q", "^B"] for enumerateObjectsUsingBlock:.
|
|
18
|
+
*/
|
|
19
|
+
args?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Full block type encoding. Example: "@?<v@?@Q^B>".
|
|
22
|
+
* When provided, this takes precedence over returns/args.
|
|
23
|
+
*/
|
|
24
|
+
types?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Attach an explicit block signature to a JavaScript function.
|
|
28
|
+
*
|
|
29
|
+
* Use this when Objective-C only exposes `@?` for a block parameter and
|
|
30
|
+
* objc-js would otherwise have to guess the callback argument types.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const handler = typedBlock({ returns: "v", args: ["@", "Q", "^B"] }, (obj, idx, stop) => {
|
|
35
|
+
* console.log(obj.toString(), idx, stop);
|
|
36
|
+
* });
|
|
37
|
+
* array.enumerateObjectsUsingBlock$(handler);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function typedBlock<T extends (...args: any[]) => any>(signature: string | TypedBlockOptions, fn: T): T;
|
|
10
41
|
interface NobjcMethod {
|
|
11
42
|
(...args: any[]): any;
|
|
12
43
|
}
|
|
@@ -297,4 +328,4 @@ declare const RunLoop: {
|
|
|
297
328
|
*/
|
|
298
329
|
stop(): void;
|
|
299
330
|
};
|
|
300
|
-
export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol, NobjcClass, RunLoop, getPointer, fromPointer, callFunction, callVariadicFunction };
|
|
331
|
+
export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol, NobjcClass, typedBlock, RunLoop, getPointer, fromPointer, callFunction, callVariadicFunction };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation, DefineClass, CallSuper, CallFunction } from "./native.js";
|
|
2
2
|
const customInspectSymbol = Symbol.for("nodejs.util.inspect.custom");
|
|
3
3
|
const NATIVE_OBJC_OBJECT = Symbol("nativeObjcObject");
|
|
4
|
+
const TYPED_BLOCK_ENCODING = "__nobjcBlockTypeEncoding";
|
|
4
5
|
// WeakMap side-channel for O(1) proxy → native object lookup (bypasses Proxy traps)
|
|
5
6
|
const nativeObjectMap = new WeakMap();
|
|
6
7
|
// Module-scope Set for O(1) lookup instead of per-access array with O(n) .includes()
|
|
@@ -148,6 +149,44 @@ class NobjcObject {
|
|
|
148
149
|
return proxy;
|
|
149
150
|
}
|
|
150
151
|
}
|
|
152
|
+
function normalizeTypedBlockEncoding(signature) {
|
|
153
|
+
if (typeof signature === "string") {
|
|
154
|
+
if (!signature.startsWith("@?")) {
|
|
155
|
+
throw new TypeError("typedBlock(string, fn) expects a full block type encoding starting with '@?'");
|
|
156
|
+
}
|
|
157
|
+
return signature;
|
|
158
|
+
}
|
|
159
|
+
if (signature.types !== undefined) {
|
|
160
|
+
if (!signature.types.startsWith("@?")) {
|
|
161
|
+
throw new TypeError("typedBlock({ types }, fn) expects a full block type encoding starting with '@?'");
|
|
162
|
+
}
|
|
163
|
+
return signature.types;
|
|
164
|
+
}
|
|
165
|
+
const args = signature.args ?? [];
|
|
166
|
+
return `@?<${signature.returns}@?${args.join("")}>`;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Attach an explicit block signature to a JavaScript function.
|
|
170
|
+
*
|
|
171
|
+
* Use this when Objective-C only exposes `@?` for a block parameter and
|
|
172
|
+
* objc-js would otherwise have to guess the callback argument types.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```typescript
|
|
176
|
+
* const handler = typedBlock({ returns: "v", args: ["@", "Q", "^B"] }, (obj, idx, stop) => {
|
|
177
|
+
* console.log(obj.toString(), idx, stop);
|
|
178
|
+
* });
|
|
179
|
+
* array.enumerateObjectsUsingBlock$(handler);
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
function typedBlock(signature, fn) {
|
|
183
|
+
Object.defineProperty(fn, TYPED_BLOCK_ENCODING, {
|
|
184
|
+
value: normalizeTypedBlockEncoding(signature),
|
|
185
|
+
enumerable: false,
|
|
186
|
+
configurable: true
|
|
187
|
+
});
|
|
188
|
+
return fn;
|
|
189
|
+
}
|
|
151
190
|
function unwrapArg(arg) {
|
|
152
191
|
if (arg && typeof arg === "object") {
|
|
153
192
|
return nativeObjectMap.get(arg) ?? arg;
|
|
@@ -161,6 +200,14 @@ function unwrapArg(arg) {
|
|
|
161
200
|
}
|
|
162
201
|
return unwrapArg(arg(...nativeArgs));
|
|
163
202
|
};
|
|
203
|
+
const explicitBlockEncoding = arg[TYPED_BLOCK_ENCODING];
|
|
204
|
+
if (typeof explicitBlockEncoding === "string") {
|
|
205
|
+
Object.defineProperty(wrapped, TYPED_BLOCK_ENCODING, {
|
|
206
|
+
value: explicitBlockEncoding,
|
|
207
|
+
enumerable: false,
|
|
208
|
+
configurable: true
|
|
209
|
+
});
|
|
210
|
+
}
|
|
164
211
|
// Preserve the original function's .length so the native layer can read it
|
|
165
212
|
// (used to infer block parameter count when extended encoding is unavailable)
|
|
166
213
|
Object.defineProperty(wrapped, "length", { value: arg.length });
|
|
@@ -697,4 +744,4 @@ const RunLoop = {
|
|
|
697
744
|
}
|
|
698
745
|
}
|
|
699
746
|
};
|
|
700
|
-
export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol, NobjcClass, RunLoop, getPointer, fromPointer, callFunction, callVariadicFunction };
|
|
747
|
+
export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol, NobjcClass, typedBlock, RunLoop, getPointer, fromPointer, callFunction, callVariadicFunction };
|
package/package.json
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"prebuild:x64": "prebuildify --napi --strip --arch x64 -r node",
|
|
48
48
|
"prebuild:all": "bun run prebuild:arm64 && bun run prebuild:x64"
|
|
49
49
|
},
|
|
50
|
-
"version": "1.
|
|
50
|
+
"version": "1.5.0",
|
|
51
51
|
"description": "Objective-C bridge for Node.js",
|
|
52
52
|
"main": "dist/index.js",
|
|
53
53
|
"dependencies": {
|
|
Binary file
|
|
Binary file
|
package/src/native/ObjcObject.h
CHANGED
|
@@ -47,11 +47,17 @@ struct PreparedSend {
|
|
|
47
47
|
std::vector<ArgInfo> argInfos;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
+
struct NobjcEnvData {
|
|
51
|
+
Napi::FunctionReference objcObjectConstructor;
|
|
52
|
+
};
|
|
53
|
+
|
|
50
54
|
class ObjcObject : public Napi::ObjectWrap<ObjcObject> {
|
|
51
55
|
public:
|
|
52
56
|
__strong id objcObject;
|
|
53
|
-
static Napi::FunctionReference constructor;
|
|
54
57
|
static void Init(Napi::Env env, Napi::Object exports);
|
|
58
|
+
static NobjcEnvData *GetEnvData(Napi::Env env);
|
|
59
|
+
static Napi::FunctionReference &GetConstructorRef(Napi::Env env);
|
|
60
|
+
static bool IsInstance(Napi::Env env, const Napi::Value &value);
|
|
55
61
|
ObjcObject(const Napi::CallbackInfo &info)
|
|
56
62
|
: Napi::ObjectWrap<ObjcObject>(info), objcObject(nil) {
|
|
57
63
|
if (info.Length() == 1 && info[0].IsExternal()) {
|
package/src/native/ObjcObject.mm
CHANGED
|
@@ -311,7 +311,24 @@ struct ClassSELHash {
|
|
|
311
311
|
static std::unordered_map<std::pair<Class, SEL>, NSMethodSignature *, ClassSELHash>
|
|
312
312
|
methodSignatureCache;
|
|
313
313
|
|
|
314
|
-
Napi::
|
|
314
|
+
NobjcEnvData *ObjcObject::GetEnvData(Napi::Env env) {
|
|
315
|
+
NobjcEnvData *data = env.GetInstanceData<NobjcEnvData>();
|
|
316
|
+
if (data == nullptr) {
|
|
317
|
+
throw Napi::Error::New(env, "objc-js addon state is not initialized");
|
|
318
|
+
}
|
|
319
|
+
return data;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
Napi::FunctionReference &ObjcObject::GetConstructorRef(Napi::Env env) {
|
|
323
|
+
return GetEnvData(env)->objcObjectConstructor;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
bool ObjcObject::IsInstance(Napi::Env env, const Napi::Value &value) {
|
|
327
|
+
if (!value.IsObject()) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
return value.As<Napi::Object>().InstanceOf(GetConstructorRef(env).Value());
|
|
331
|
+
}
|
|
315
332
|
|
|
316
333
|
void ObjcObject::Init(Napi::Env env, Napi::Object exports) {
|
|
317
334
|
Napi::Function func =
|
|
@@ -323,8 +340,7 @@ void ObjcObject::Init(Napi::Env env, Napi::Object exports) {
|
|
|
323
340
|
InstanceMethod("$msgSendPrepared", &ObjcObject::$MsgSendPrepared),
|
|
324
341
|
InstanceMethod("$getPointer", &ObjcObject::GetPointer),
|
|
325
342
|
});
|
|
326
|
-
|
|
327
|
-
constructor.SuppressDestruct();
|
|
343
|
+
GetConstructorRef(env) = Napi::Persistent(func);
|
|
328
344
|
exports.Set("ObjcObject", func);
|
|
329
345
|
}
|
|
330
346
|
|
|
@@ -332,7 +348,8 @@ Napi::Object ObjcObject::NewInstance(Napi::Env env, id obj) {
|
|
|
332
348
|
Napi::EscapableHandleScope scope(env);
|
|
333
349
|
// `obj` is already a pointer, technically, but the Napi::External
|
|
334
350
|
// API expects a pointer, so we have to pointer to the pointer.
|
|
335
|
-
Napi::Object jsObj =
|
|
351
|
+
Napi::Object jsObj =
|
|
352
|
+
GetConstructorRef(env).New({Napi::External<id>::New(env, &obj)});
|
|
336
353
|
return scope.Escape(jsObj).ToObject();
|
|
337
354
|
}
|
|
338
355
|
|
package/src/native/bridge.h
CHANGED
|
@@ -181,7 +181,7 @@ T ConvertToNativeValue(const Napi::Value &value,
|
|
|
181
181
|
// is value an ObjcObject instance?
|
|
182
182
|
if (value.IsObject()) {
|
|
183
183
|
Napi::Object obj = value.As<Napi::Object>();
|
|
184
|
-
if (
|
|
184
|
+
if (ObjcObject::IsInstance(value.Env(), obj)) {
|
|
185
185
|
ObjcObject *objcObj = Napi::ObjectWrap<ObjcObject>::Unwrap(obj);
|
|
186
186
|
return objcObj->objcObject;
|
|
187
187
|
}
|
package/src/native/nobjc.mm
CHANGED
|
@@ -43,7 +43,7 @@ Napi::Value GetPointer(const Napi::CallbackInfo &info) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
Napi::Object obj = info[0].As<Napi::Object>();
|
|
46
|
-
if (!
|
|
46
|
+
if (!ObjcObject::IsInstance(env, obj)) {
|
|
47
47
|
throw Napi::TypeError::New(env, "Argument must be an ObjcObject instance");
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -103,7 +103,12 @@ Napi::Value PumpRunLoop(const Napi::CallbackInfo &info) {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
static void CleanupEnvData(napi_env, void *data, void *) {
|
|
107
|
+
delete static_cast<NobjcEnvData *>(data);
|
|
108
|
+
}
|
|
109
|
+
|
|
106
110
|
Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
|
|
111
|
+
napi_set_instance_data(env, new NobjcEnvData(), CleanupEnvData, nullptr);
|
|
107
112
|
ObjcObject::Init(env, exports);
|
|
108
113
|
exports.Set("LoadLibrary", Napi::Function::New(env, LoadLibrary));
|
|
109
114
|
exports.Set("GetClassObject", Napi::Function::New(env, GetClassObject));
|
package/src/native/nobjc_block.h
CHANGED
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
#include <Block.h>
|
|
41
41
|
#include <Foundation/Foundation.h>
|
|
42
42
|
#include <atomic>
|
|
43
|
+
#include <dlfcn.h>
|
|
43
44
|
#include <ffi.h>
|
|
44
45
|
#include <napi.h>
|
|
45
46
|
#include <objc/runtime.h>
|
|
@@ -316,6 +317,8 @@ struct BlockInfo {
|
|
|
316
317
|
}
|
|
317
318
|
};
|
|
318
319
|
|
|
320
|
+
constexpr const char *kTypedBlockEncodingProperty = "__nobjcBlockTypeEncoding";
|
|
321
|
+
|
|
319
322
|
// MARK: - Block Call Data (transient, for cross-thread invocation)
|
|
320
323
|
|
|
321
324
|
/**
|
|
@@ -400,7 +403,64 @@ inline void NobjcBlockDisposeHelper(const void *src) {
|
|
|
400
403
|
* 1. Tagged pointers (arm64: high bit set) are always valid objects
|
|
401
404
|
* 2. Use malloc_zone_from_ptr() to check if it's a heap allocation
|
|
402
405
|
* 3. If it is, verify it has a valid class pointer
|
|
406
|
+
* 4. Fall back to image-backed singleton detection for constant objects like
|
|
407
|
+
* __NSArray0 that live in the dyld shared cache instead of malloc heap
|
|
403
408
|
*/
|
|
409
|
+
inline bool PointerResolvesToLoadedImage(const void *ptr) {
|
|
410
|
+
if (!ptr) return false;
|
|
411
|
+
Dl_info info;
|
|
412
|
+
return dladdr(ptr, &info) != 0;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
inline uintptr_t GetObjCIsaClassMask() {
|
|
416
|
+
static uintptr_t mask = []() -> uintptr_t {
|
|
417
|
+
void *symbol = dlsym(RTLD_DEFAULT, "objc_debug_isa_class_mask");
|
|
418
|
+
if (symbol) {
|
|
419
|
+
return *static_cast<const uintptr_t *>(symbol);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
#if defined(__aarch64__) || defined(__arm64__)
|
|
423
|
+
return 0x0000000ffffffff8ULL;
|
|
424
|
+
#elif defined(__x86_64__)
|
|
425
|
+
return 0x00007ffffffffff8ULL;
|
|
426
|
+
#else
|
|
427
|
+
return 0;
|
|
428
|
+
#endif
|
|
429
|
+
}();
|
|
430
|
+
return mask;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
inline bool LooksLikeImageBackedObjCObject(uintptr_t val) {
|
|
434
|
+
void *ptr = reinterpret_cast<void *>(val);
|
|
435
|
+
if (!PointerResolvesToLoadedImage(ptr)) return false;
|
|
436
|
+
|
|
437
|
+
// Image-backed singleton objects (for example __NSArray0) are not malloc
|
|
438
|
+
// allocations, so inspect the isa word and see if it resolves to something
|
|
439
|
+
// that also looks like a class pointer in a loaded image.
|
|
440
|
+
uintptr_t isaBits = *reinterpret_cast<const uintptr_t *>(ptr);
|
|
441
|
+
if (isaBits < 4096) return false;
|
|
442
|
+
|
|
443
|
+
const uintptr_t isaMask = GetObjCIsaClassMask();
|
|
444
|
+
const uintptr_t isaCandidates[] = {
|
|
445
|
+
isaBits,
|
|
446
|
+
isaMask == 0 ? 0 : (isaBits & isaMask),
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
for (uintptr_t candidate : isaCandidates) {
|
|
450
|
+
if (candidate < 4096 || (candidate & (alignof(void *) - 1)) != 0) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
void *candidatePtr = reinterpret_cast<void *>(candidate);
|
|
455
|
+
if (malloc_zone_from_ptr(candidatePtr) ||
|
|
456
|
+
PointerResolvesToLoadedImage(candidatePtr)) {
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
|
|
404
464
|
inline bool LooksLikeObjCObject(uintptr_t val) {
|
|
405
465
|
if (val == 0) return false; // nil
|
|
406
466
|
|
|
@@ -415,12 +475,14 @@ inline bool LooksLikeObjCObject(uintptr_t val) {
|
|
|
415
475
|
// malloc_zone_from_ptr returns non-NULL only for valid heap allocations.
|
|
416
476
|
void *ptr = (void *)val;
|
|
417
477
|
malloc_zone_t *zone = malloc_zone_from_ptr(ptr);
|
|
418
|
-
if (
|
|
478
|
+
if (zone) {
|
|
479
|
+
// It's a heap allocation — very likely an ObjC object.
|
|
480
|
+
// Do a final check: object_getClass should return a valid class.
|
|
481
|
+
Class cls = object_getClass((__bridge id)ptr);
|
|
482
|
+
if (cls != nil) return true;
|
|
483
|
+
}
|
|
419
484
|
|
|
420
|
-
|
|
421
|
-
// Do a final check: object_getClass should return a valid class.
|
|
422
|
-
Class cls = object_getClass((__bridge id)ptr);
|
|
423
|
-
return cls != nil;
|
|
485
|
+
return LooksLikeImageBackedObjCObject(val);
|
|
424
486
|
}
|
|
425
487
|
|
|
426
488
|
/**
|
|
@@ -530,7 +592,7 @@ inline void SetBlockReturnFromJS(Napi::Value result, void *returnPtr,
|
|
|
530
592
|
id objcVal = nil;
|
|
531
593
|
if (result.IsObject()) {
|
|
532
594
|
Napi::Object obj = result.As<Napi::Object>();
|
|
533
|
-
if (
|
|
595
|
+
if (ObjcObject::IsInstance(result.Env(), obj)) {
|
|
534
596
|
ObjcObject *wrapper = Napi::ObjectWrap<ObjcObject>::Unwrap(obj);
|
|
535
597
|
objcVal = wrapper->objcObject;
|
|
536
598
|
}
|
|
@@ -718,8 +780,22 @@ inline id CreateBlockFromJSFunction(Napi::Env env,
|
|
|
718
780
|
const char *typeEncoding) {
|
|
719
781
|
NOBJC_LOG("CreateBlockFromJSFunction: encoding='%s'", typeEncoding);
|
|
720
782
|
|
|
783
|
+
std::string explicitTypeEncoding;
|
|
784
|
+
if (jsFunction.IsFunction()) {
|
|
785
|
+
Napi::Value encodingValue =
|
|
786
|
+
jsFunction.As<Napi::Function>().Get(kTypedBlockEncodingProperty);
|
|
787
|
+
if (encodingValue.IsString()) {
|
|
788
|
+
explicitTypeEncoding = encodingValue.As<Napi::String>().Utf8Value();
|
|
789
|
+
NOBJC_LOG("CreateBlockFromJSFunction: using explicit encoding='%s'",
|
|
790
|
+
explicitTypeEncoding.c_str());
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const char *effectiveTypeEncoding =
|
|
795
|
+
explicitTypeEncoding.empty() ? typeEncoding : explicitTypeEncoding.c_str();
|
|
796
|
+
|
|
721
797
|
// Parse the block signature
|
|
722
|
-
BlockSignature sig = ParseBlockSignature(
|
|
798
|
+
BlockSignature sig = ParseBlockSignature(effectiveTypeEncoding);
|
|
723
799
|
if (!sig.valid) {
|
|
724
800
|
// No extended encoding — infer from JS function's .length
|
|
725
801
|
// All params are treated as pointer-sized (heuristic detection in callback)
|
|
@@ -728,7 +804,7 @@ inline id CreateBlockFromJSFunction(Napi::Env env,
|
|
|
728
804
|
|
|
729
805
|
NOBJC_LOG("CreateBlockFromJSFunction: No extended block encoding, "
|
|
730
806
|
"inferring %u params from JS function.length. Encoding: '%s'",
|
|
731
|
-
jsParamCount,
|
|
807
|
+
jsParamCount, effectiveTypeEncoding);
|
|
732
808
|
|
|
733
809
|
sig.returnType = "v"; // Assume void return
|
|
734
810
|
sig.paramTypes.clear();
|
|
@@ -279,7 +279,7 @@ Napi::Value DefineClass(const Napi::CallbackInfo &info) {
|
|
|
279
279
|
}
|
|
280
280
|
} else if (superValue.IsObject()) {
|
|
281
281
|
Napi::Object superObj = superValue.As<Napi::Object>();
|
|
282
|
-
if (
|
|
282
|
+
if (ObjcObject::IsInstance(env, superObj)) {
|
|
283
283
|
ObjcObject *objcObj = Napi::ObjectWrap<ObjcObject>::Unwrap(superObj);
|
|
284
284
|
superClass = (Class)objcObj->objcObject;
|
|
285
285
|
}
|
|
@@ -627,7 +627,7 @@ Napi::Value CallSuper(const Napi::CallbackInfo &info) {
|
|
|
627
627
|
throw Napi::TypeError::New(env, "First argument must be an ObjcObject (self)");
|
|
628
628
|
}
|
|
629
629
|
Napi::Object selfObj = info[0].As<Napi::Object>();
|
|
630
|
-
if (!
|
|
630
|
+
if (!ObjcObject::IsInstance(env, selfObj)) {
|
|
631
631
|
throw Napi::TypeError::New(env, "First argument must be an ObjcObject (self)");
|
|
632
632
|
}
|
|
633
633
|
ObjcObject *selfWrapper = Napi::ObjectWrap<ObjcObject>::Unwrap(selfObj);
|
|
@@ -583,7 +583,7 @@ inline void SetInvocationReturnFromJS(NSInvocation *invocation,
|
|
|
583
583
|
case '@': {
|
|
584
584
|
if (result.IsObject()) {
|
|
585
585
|
Napi::Object resultObj = result.As<Napi::Object>();
|
|
586
|
-
if (
|
|
586
|
+
if (ObjcObject::IsInstance(result.Env(), resultObj)) {
|
|
587
587
|
ObjcObject *objcObj = Napi::ObjectWrap<ObjcObject>::Unwrap(resultObj);
|
|
588
588
|
id objcValue = objcObj->objcObject;
|
|
589
589
|
[invocation setReturnValue:&objcValue];
|