objc-js 0.0.12 → 0.0.13

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.
Binary file
package/package.json CHANGED
@@ -39,7 +39,7 @@
39
39
  "format": "prettier --write \"**/*.{ts,js,json,md}\"",
40
40
  "preinstall-disabled": "npm run build-scripts && npm run make-clangd-config"
41
41
  },
42
- "version": "0.0.12",
42
+ "version": "0.0.13",
43
43
  "description": "Objective-C bridge for Node.js",
44
44
  "main": "dist/index.js",
45
45
  "dependencies": {
@@ -57,19 +57,27 @@ void CallJSCallback(Napi::Env env, Napi::Function jsCallback,
57
57
  jsArgs.push_back(ExtractInvocationArgumentToJS(env, invocation, i, argType[0]));
58
58
  }
59
59
 
60
+ NSLog(@"[DEBUG] About to call JS callback for %s with %zu arguments",
61
+ data->selectorName.c_str(), jsArgs.size());
62
+
60
63
  // Call the JavaScript callback
61
64
  try {
62
65
  Napi::Value result = jsCallback.Call(jsArgs);
63
66
 
67
+ NSLog(@"[DEBUG] JS callback for %s returned, result type: %d",
68
+ data->selectorName.c_str(), result.Type());
69
+
64
70
  // Handle return value if the method expects one
65
71
  const char *returnType = [sig methodReturnType];
66
72
  SimplifiedTypeEncoding retType(returnType);
67
73
 
68
74
  if (retType[0] != 'v') { // Not void
69
- NSLog(@"Setting return value for %s, return type: %c",
70
- data->selectorName.c_str(), retType[0]);
75
+ NSLog(@"[DEBUG] Setting return value for %s, return type: %c, JS result is %s",
76
+ data->selectorName.c_str(), retType[0],
77
+ result.IsNull() ? "null" : result.IsUndefined() ? "undefined" : "value");
71
78
  SetInvocationReturnFromJS(invocation, result, retType[0],
72
79
  data->selectorName.c_str());
80
+ NSLog(@"[DEBUG] Return value set for %s", data->selectorName.c_str());
73
81
  }
74
82
  } catch (const Napi::Error &e) {
75
83
  NSLog(@"Error calling JavaScript callback for %s: %s",
@@ -165,6 +173,7 @@ void ForwardInvocation(id self, SEL _cmd, NSInvocation *invocation) {
165
173
  Napi::ThreadSafeFunction tsfn;
166
174
  std::string typeEncoding;
167
175
  pthread_t js_thread;
176
+ bool isElectron;
168
177
  {
169
178
  std::lock_guard<std::mutex> lock(g_implementations_mutex);
170
179
  auto it = g_implementations.find(ptr);
@@ -201,13 +210,19 @@ void ForwardInvocation(id self, SEL _cmd, NSInvocation *invocation) {
201
210
 
202
211
  // Get the JS thread ID to check if we're on the same thread
203
212
  js_thread = it->second.js_thread;
213
+ isElectron = it->second.isElectron;
204
214
  }
205
215
 
206
216
  // Check if we're on the JS thread
207
217
  bool is_js_thread = pthread_equal(pthread_self(), js_thread);
208
218
 
219
+ NSLog(@"[DEBUG] ForwardInvocation for %s: is_js_thread=%d, isElectron=%d, current_thread=%p, js_thread=%p",
220
+ selectorName.c_str(), is_js_thread, isElectron, pthread_self(), js_thread);
221
+
209
222
  // IMPORTANT: We call directly on the JS thread so return values are set
210
223
  // synchronously; otherwise we use a ThreadSafeFunction to marshal work.
224
+ // EXCEPTION: In Electron, we ALWAYS use TSFN even on the JS thread because
225
+ // Electron's V8 context isn't properly set up for direct handle creation.
211
226
 
212
227
  // Create invocation data
213
228
  auto data = new InvocationData();
@@ -217,9 +232,10 @@ void ForwardInvocation(id self, SEL _cmd, NSInvocation *invocation) {
217
232
 
218
233
  napi_status status;
219
234
 
220
- if (is_js_thread) {
221
- // We're on the JS thread (e.g., called from performSelector in Bun/Node)
235
+ if (is_js_thread && !isElectron) {
236
+ // We're on the JS thread in Node/Bun (NOT Electron)
222
237
  // Call directly to ensure return values are set synchronously.
238
+ NSLog(@"[DEBUG] Taking JS thread direct call path for %s", selectorName.c_str());
223
239
  data->completionMutex = nullptr;
224
240
  data->completionCv = nullptr;
225
241
  data->isComplete = nullptr;
@@ -258,13 +274,17 @@ void ForwardInvocation(id self, SEL _cmd, NSInvocation *invocation) {
258
274
  // Wrap in try-catch to handle invalid env (e.g., in Electron when context
259
275
  // is destroyed)
260
276
  try {
277
+ NSLog(@"[DEBUG] Creating Napi::Env from stored_env for %s", selectorName.c_str());
261
278
  Napi::Env callEnv(stored_env);
262
279
 
263
280
  // Create a HandleScope to properly manage V8 handles
264
281
  // This is critical for Electron which may have multiple V8 contexts
282
+ NSLog(@"[DEBUG] Creating HandleScope for %s", selectorName.c_str());
265
283
  Napi::HandleScope scope(callEnv);
266
284
 
285
+ NSLog(@"[DEBUG] Calling CallJSCallback directly for %s", selectorName.c_str());
267
286
  CallJSCallback(callEnv, jsFn, data);
287
+ NSLog(@"[DEBUG] CallJSCallback completed for %s", selectorName.c_str());
268
288
  // CallJSCallback releases invocation and deletes data.
269
289
  } catch (const std::exception &e) {
270
290
  NSLog(@"Error calling JS callback directly (likely invalid env in "
@@ -323,6 +343,7 @@ void ForwardInvocation(id self, SEL _cmd, NSInvocation *invocation) {
323
343
  // We're on a different thread (e.g., Cocoa callback from
324
344
  // ASAuthorizationController) Use NonBlockingCall + runloop pumping to avoid
325
345
  // deadlocks
346
+ NSLog(@"[DEBUG] Taking non-JS thread path for %s", selectorName.c_str());
326
347
  std::mutex completionMutex;
327
348
  std::condition_variable completionCv;
328
349
  bool isComplete = false;
@@ -130,13 +130,33 @@ Napi::Value CreateProtocolImplementation(const Napi::CallbackInfo &info) {
130
130
  // Get the method implementations object's property names
131
131
  Napi::Array propertyNames = methodImplementations.GetPropertyNames();
132
132
 
133
+ // Detect if we're running in Electron by checking process.versions.electron
134
+ bool isElectron = false;
135
+ try {
136
+ Napi::Object global = env.Global();
137
+ if (global.Has("process")) {
138
+ Napi::Object process = global.Get("process").As<Napi::Object>();
139
+ if (process.Has("versions")) {
140
+ Napi::Object versions = process.Get("versions").As<Napi::Object>();
141
+ isElectron = versions.Has("electron");
142
+ }
143
+ }
144
+ } catch (...) {
145
+ // If detection fails, assume not Electron
146
+ }
147
+
148
+ if (isElectron) {
149
+ NSLog(@"[DEBUG] Detected Electron runtime, will always use TSFN path");
150
+ }
151
+
133
152
  // Store callbacks for this instance (we'll set the instance pointer later)
134
153
  ProtocolImplementation impl{
135
154
  .callbacks = {},
136
155
  .typeEncodings = {},
137
156
  .className = className,
138
157
  .env = env,
139
- .js_thread = pthread_self() // Store the current (JS) thread ID
158
+ .js_thread = pthread_self(), // Store the current (JS) thread ID
159
+ .isElectron = isElectron
140
160
  };
141
161
 
142
162
  // Store default type encodings to keep them alive
@@ -43,6 +43,8 @@ struct ProtocolImplementation {
43
43
  napi_env env;
44
44
  // Store the JS thread ID for thread detection
45
45
  pthread_t js_thread;
46
+ // Flag to indicate if running in Electron (requires TSFN path always)
47
+ bool isElectron;
46
48
  };
47
49
 
48
50
  // MARK: - Global Storage