objc-js 0.0.8 → 0.0.9

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.
@@ -0,0 +1,113 @@
1
+ #include "ObjcObject.h"
2
+ #include "protocol-impl.h"
3
+ #include <Foundation/Foundation.h>
4
+ #include <dlfcn.h>
5
+ #include <napi.h>
6
+
7
+ Napi::Value LoadLibrary(const Napi::CallbackInfo &info) {
8
+ Napi::Env env = info.Env();
9
+ if (info.Length() != 1 || !info[0].IsString()) {
10
+ throw Napi::TypeError::New(env, "Expected a single string argument");
11
+ }
12
+ std::string libPath = info[0].As<Napi::String>().Utf8Value();
13
+ void *handle = dlopen(libPath.c_str(), RTLD_LAZY | RTLD_GLOBAL);
14
+ if (!handle) {
15
+ throw Napi::Error::New(env, dlerror());
16
+ }
17
+ return env.Undefined();
18
+ }
19
+
20
+ Napi::Value GetClassObject(const Napi::CallbackInfo &info) {
21
+ Napi::Env env = info.Env();
22
+ if (info.Length() != 1 || !info[0].IsString()) {
23
+ throw Napi::TypeError::New(env, "Expected a single string argument");
24
+ }
25
+ std::string className = info[0].As<Napi::String>().Utf8Value();
26
+ Class cls =
27
+ NSClassFromString([NSString stringWithUTF8String:className.c_str()]);
28
+ if (cls == nil) {
29
+ return env.Undefined();
30
+ }
31
+ return ObjcObject::NewInstance(env, cls);
32
+ }
33
+
34
+ Napi::Value GetPointer(const Napi::CallbackInfo &info) {
35
+ Napi::Env env = info.Env();
36
+ if (info.Length() != 1 || !info[0].IsObject()) {
37
+ throw Napi::TypeError::New(env, "Expected a single ObjcObject argument");
38
+ }
39
+
40
+ Napi::Object obj = info[0].As<Napi::Object>();
41
+ if (!obj.InstanceOf(ObjcObject::constructor.Value())) {
42
+ throw Napi::TypeError::New(env, "Argument must be an ObjcObject instance");
43
+ }
44
+
45
+ ObjcObject *objcObj = Napi::ObjectWrap<ObjcObject>::Unwrap(obj);
46
+ uintptr_t ptrValue = reinterpret_cast<uintptr_t>(objcObj->objcObject);
47
+
48
+ // Create a Buffer to hold the pointer (8 bytes on 64-bit macOS)
49
+ Napi::Buffer<uint8_t> buffer = Napi::Buffer<uint8_t>::New(env, sizeof(void*));
50
+
51
+ // Write the pointer value to the buffer in little-endian format
52
+ uint8_t* data = buffer.Data();
53
+ for (size_t i = 0; i < sizeof(void*); ++i) {
54
+ data[i] = static_cast<uint8_t>((ptrValue >> (i * 8)) & 0xFF);
55
+ }
56
+
57
+ return buffer;
58
+ }
59
+
60
+ Napi::Value FromPointer(const Napi::CallbackInfo &info) {
61
+ Napi::Env env = info.Env();
62
+
63
+ if (info.Length() != 1) {
64
+ throw Napi::TypeError::New(env, "Expected a single Buffer or BigInt argument");
65
+ }
66
+
67
+ uintptr_t ptrValue = 0;
68
+
69
+ if (info[0].IsBuffer()) {
70
+ // Read pointer from Buffer
71
+ Napi::Buffer<uint8_t> buffer = info[0].As<Napi::Buffer<uint8_t>>();
72
+ if (buffer.Length() != sizeof(void*)) {
73
+ throw Napi::TypeError::New(env, "Buffer must be exactly 8 bytes for a 64-bit pointer");
74
+ }
75
+
76
+ uint8_t* data = buffer.Data();
77
+ for (size_t i = 0; i < sizeof(void*); ++i) {
78
+ ptrValue |= (static_cast<uintptr_t>(data[i]) << (i * 8));
79
+ }
80
+ } else if (info[0].IsBigInt()) {
81
+ // Read pointer from BigInt
82
+ bool lossless = false;
83
+ uint64_t value = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
84
+ if (!lossless) {
85
+ throw Napi::RangeError::New(env, "BigInt value out of range for pointer");
86
+ }
87
+ ptrValue = static_cast<uintptr_t>(value);
88
+ } else {
89
+ throw Napi::TypeError::New(env, "Expected a Buffer or BigInt argument");
90
+ }
91
+
92
+ if (ptrValue == 0) {
93
+ return env.Null();
94
+ }
95
+
96
+ // Convert the pointer value back to an Objective-C object pointer
97
+ id obj = reinterpret_cast<id>(ptrValue);
98
+
99
+ return ObjcObject::NewInstance(env, obj);
100
+ }
101
+
102
+ Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
103
+ ObjcObject::Init(env, exports);
104
+ exports.Set("LoadLibrary", Napi::Function::New(env, LoadLibrary));
105
+ exports.Set("GetClassObject", Napi::Function::New(env, GetClassObject));
106
+ exports.Set("GetPointer", Napi::Function::New(env, GetPointer));
107
+ exports.Set("FromPointer", Napi::Function::New(env, FromPointer));
108
+ exports.Set("CreateProtocolImplementation",
109
+ Napi::Function::New(env, CreateProtocolImplementation));
110
+ return exports;
111
+ }
112
+
113
+ NODE_API_MODULE(nobjc_native, InitAll)
@@ -0,0 +1,69 @@
1
+ #ifndef PROTOCOL_IMPL_H
2
+ #define PROTOCOL_IMPL_H
3
+
4
+ #include <napi.h>
5
+ #include <objc/runtime.h>
6
+ #include <string>
7
+ #include <unordered_map>
8
+ #include <vector>
9
+
10
+ // Forward declarations for Objective-C types
11
+ #ifdef __OBJC__
12
+ @class NSMethodSignature;
13
+ @class NSInvocation;
14
+ #else
15
+ typedef struct NSMethodSignature NSMethodSignature;
16
+ typedef struct NSInvocation NSInvocation;
17
+ #endif
18
+
19
+ // MARK: - Data Structures
20
+
21
+ // Data passed from native thread to JS thread for invocation handling
22
+ struct InvocationData {
23
+ NSInvocation *invocation;
24
+ std::string selectorName;
25
+ std::string typeEncoding;
26
+ // The invocation itself stores the return value, so we don't need separate storage
27
+ // BlockingCall ensures the callback completes before returning, so no sync primitives needed
28
+ };
29
+
30
+ // Stores information about a protocol implementation instance
31
+ struct ProtocolImplementation {
32
+ std::unordered_map<std::string, Napi::ThreadSafeFunction> callbacks;
33
+ std::unordered_map<std::string, Napi::FunctionReference> jsCallbacks; // Original JS functions for direct calls
34
+ std::unordered_map<std::string, std::string> typeEncodings;
35
+ std::string className;
36
+ napi_env env; // Store the environment for direct calls
37
+ pthread_t js_thread; // Store the JS thread ID
38
+ };
39
+
40
+ // Global map: instance pointer -> implementation details
41
+ // This keeps JavaScript callbacks alive for the lifetime of the Objective-C object
42
+ extern std::unordered_map<void *, ProtocolImplementation> g_implementations;
43
+
44
+ // MARK: - Function Declarations
45
+
46
+ // Main entry point: creates a new Objective-C class that implements a protocol
47
+ Napi::Value CreateProtocolImplementation(const Napi::CallbackInfo &info);
48
+
49
+ // Override respondsToSelector to return YES for implemented methods
50
+ BOOL RespondsToSelector(id self, SEL _cmd, SEL selector);
51
+
52
+ // Method signature provider for message forwarding
53
+ NSMethodSignature* MethodSignatureForSelector(id self, SEL _cmd, SEL selector);
54
+
55
+ // Forward invocation handler for dynamic method dispatch
56
+ void ForwardInvocation(id self, SEL _cmd, NSInvocation *invocation);
57
+
58
+ // Helper: Parses an Objective-C method signature to extract argument types
59
+ std::vector<std::string> ParseMethodSignature(const char *typeEncoding);
60
+
61
+ // Helper: Converts an Objective-C value to a JavaScript value
62
+ Napi::Value ConvertObjCValueToJS(Napi::Env env, void *value,
63
+ const char *typeEncoding);
64
+
65
+ // Deallocation implementation to clean up when instance is destroyed
66
+ void DeallocImplementation(id self, SEL _cmd);
67
+
68
+ #endif // PROTOCOL_IMPL_H
69
+