objc-js 0.0.5 → 0.0.6
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/README.md +113 -0
- package/build/Release/nobjc_native.node +0 -0
- package/dist/index.d.ts +48 -3
- package/dist/index.js +73 -17
- package/dist/native.d.ts +2 -2
- package/dist/native.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -142,3 +142,116 @@ NobjcProtocol.implement(
|
|
|
142
142
|
- `methodImplementations`: An object mapping method names (using `$` notation) to JavaScript functions
|
|
143
143
|
|
|
144
144
|
**Returns:** A `NobjcObject` that can be passed to Objective-C APIs expecting the protocol
|
|
145
|
+
|
|
146
|
+
#### `getPointer()`
|
|
147
|
+
|
|
148
|
+
Get the raw native pointer for a NobjcObject as a Node Buffer. This is useful for passing Objective-C objects to native APIs that expect raw pointers, such as Electron's native window handles.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
function getPointer(obj: NobjcObject): Buffer;
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Parameters:**
|
|
155
|
+
|
|
156
|
+
- `obj`: The NobjcObject to get the pointer from
|
|
157
|
+
|
|
158
|
+
**Returns:** A Buffer containing the pointer address in little-endian format (8 bytes on 64-bit macOS)
|
|
159
|
+
|
|
160
|
+
**Example: Getting an NSView pointer**
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { NobjcLibrary, getPointer } from "objc-js";
|
|
164
|
+
|
|
165
|
+
// Load AppKit framework
|
|
166
|
+
const appKit = new NobjcLibrary("/System/Library/Frameworks/AppKit.framework/AppKit");
|
|
167
|
+
|
|
168
|
+
// Get an NSWindow and its content view
|
|
169
|
+
const NSApplication = appKit["NSApplication"];
|
|
170
|
+
const app = NSApplication.sharedApplication();
|
|
171
|
+
const window = app.mainWindow();
|
|
172
|
+
const view = window.contentView();
|
|
173
|
+
|
|
174
|
+
// Get the raw pointer as a Buffer
|
|
175
|
+
const pointerBuffer = getPointer(view);
|
|
176
|
+
|
|
177
|
+
// Read the pointer as a BigInt (64-bit unsigned integer)
|
|
178
|
+
const pointer = pointerBuffer.readBigUInt64LE(0);
|
|
179
|
+
console.log(`NSView pointer: 0x${pointer.toString(16)}`);
|
|
180
|
+
|
|
181
|
+
// Use with Electron or other native APIs
|
|
182
|
+
// const { BrowserWindow } = require('electron');
|
|
183
|
+
// const win = BrowserWindow.fromId(pointer);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Note:** The pointer is returned as a Buffer in little-endian format. Use `readBigUInt64LE(0)` to read it as a 64-bit unsigned integer, which is the standard pointer size on macOS.
|
|
187
|
+
|
|
188
|
+
#### `fromPointer()`
|
|
189
|
+
|
|
190
|
+
Create a NobjcObject from a raw native pointer. This is the inverse of `getPointer()` and allows you to reconstruct an Objective-C object from a pointer address.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
function fromPointer(pointer: Buffer | bigint): NobjcObject;
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Parameters:**
|
|
197
|
+
|
|
198
|
+
- `pointer`: A Buffer (8 bytes in little-endian format) or BigInt containing the pointer address
|
|
199
|
+
|
|
200
|
+
**Returns:** A NobjcObject wrapping the native Objective-C object
|
|
201
|
+
|
|
202
|
+
**Example: Round-trip pointer conversion**
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { NobjcLibrary, getPointer, fromPointer } from "objc-js";
|
|
206
|
+
|
|
207
|
+
const foundation = new NobjcLibrary("/System/Library/Frameworks/Foundation.framework/Foundation");
|
|
208
|
+
const NSString = foundation["NSString"];
|
|
209
|
+
|
|
210
|
+
// Create an original object
|
|
211
|
+
const original = NSString.stringWithUTF8String$("Hello, World!");
|
|
212
|
+
console.log(original.toString()); // "Hello, World!"
|
|
213
|
+
|
|
214
|
+
// Get the pointer
|
|
215
|
+
const pointerBuffer = getPointer(original);
|
|
216
|
+
const pointer = pointerBuffer.readBigUInt64LE(0);
|
|
217
|
+
console.log(`Pointer: 0x${pointer.toString(16)}`);
|
|
218
|
+
|
|
219
|
+
// Reconstruct the object from the pointer
|
|
220
|
+
const reconstructed = fromPointer(pointer);
|
|
221
|
+
// or: const reconstructed = fromPointer(pointerBuffer);
|
|
222
|
+
|
|
223
|
+
console.log(reconstructed.toString()); // "Hello, World!"
|
|
224
|
+
|
|
225
|
+
// Both objects reference the same underlying Objective-C object
|
|
226
|
+
const ptr1 = getPointer(original).readBigUInt64LE(0);
|
|
227
|
+
const ptr2 = getPointer(reconstructed).readBigUInt64LE(0);
|
|
228
|
+
console.log(ptr1 === ptr2); // true
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Example: Using with external native APIs**
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// Receive a pointer from an external API
|
|
235
|
+
const externalPointer = 0x12345678n; // Example pointer from native code
|
|
236
|
+
|
|
237
|
+
// Convert it to a NobjcObject
|
|
238
|
+
const nsObject = fromPointer(externalPointer);
|
|
239
|
+
|
|
240
|
+
// Now you can call Objective-C methods on it
|
|
241
|
+
console.log(nsObject.description());
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**⚠️ Safety Warning:**
|
|
245
|
+
|
|
246
|
+
This function is **inherently unsafe** and should be used with extreme caution:
|
|
247
|
+
|
|
248
|
+
- **Invalid pointers will crash your program**: The pointer must point to a valid Objective-C object
|
|
249
|
+
- **Dangling pointers**: The object must still be alive (not deallocated). Accessing a deallocated object will crash
|
|
250
|
+
- **No type checking**: There's no way to verify the pointer points to the expected type of object
|
|
251
|
+
- **Memory management**: Be aware of Objective-C reference counting. The object must remain valid for the lifetime of your usage
|
|
252
|
+
|
|
253
|
+
Only use this function when:
|
|
254
|
+
|
|
255
|
+
- You received the pointer from `getPointer()` and the object is still alive
|
|
256
|
+
- You received the pointer from a trusted native API that guarantees the object's validity
|
|
257
|
+
- You're interfacing with external native code that provides valid Objective-C object pointers
|
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -7,10 +7,55 @@ declare class NobjcObject {
|
|
|
7
7
|
[key: string]: NobjcMethod;
|
|
8
8
|
constructor(object: NobjcNative.ObjcObject);
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
interface NobjcMethod {
|
|
11
|
+
(...args: any[]): any;
|
|
12
12
|
}
|
|
13
|
+
declare const NobjcMethod: (object: NobjcNative.ObjcObject, methodName: string) => NobjcMethod;
|
|
13
14
|
declare class NobjcProtocol {
|
|
14
15
|
static implement(protocolName: string, methodImplementations: Record<string, (...args: any[]) => any>): NobjcObject;
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Get the raw native pointer for a NobjcObject as a Node Buffer.
|
|
19
|
+
* The pointer is stored in little-endian format (8 bytes on 64-bit macOS).
|
|
20
|
+
*
|
|
21
|
+
* @param obj - The NobjcObject to get the pointer from
|
|
22
|
+
* @returns A Buffer containing the pointer address
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const view = window.contentView();
|
|
27
|
+
* const pointerBuffer = getPointer(view);
|
|
28
|
+
* const pointer = pointerBuffer.readBigUInt64LE(0);
|
|
29
|
+
* console.log(`NSView pointer: 0x${pointer.toString(16)}`);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function getPointer(obj: NobjcObject): Buffer;
|
|
33
|
+
/**
|
|
34
|
+
* Create a NobjcObject from a raw native pointer.
|
|
35
|
+
*
|
|
36
|
+
* @param pointer - A Buffer or BigInt containing the pointer address
|
|
37
|
+
* @returns A NobjcObject wrapping the native object
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // From a Buffer
|
|
42
|
+
* const pointerBuffer = getPointer(originalObject);
|
|
43
|
+
* const reconstructed = fromPointer(pointerBuffer);
|
|
44
|
+
*
|
|
45
|
+
* // From a BigInt
|
|
46
|
+
* const pointer = 0x12345678n;
|
|
47
|
+
* const obj = fromPointer(pointer);
|
|
48
|
+
*
|
|
49
|
+
* // Round-trip example
|
|
50
|
+
* const original = NSString.stringWithUTF8String$("Hello");
|
|
51
|
+
* const ptr = getPointer(original).readBigUInt64LE(0);
|
|
52
|
+
* const restored = fromPointer(ptr);
|
|
53
|
+
* console.log(restored.toString()); // "Hello"
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @warning This is unsafe! The pointer must point to a valid Objective-C object.
|
|
57
|
+
* Using an invalid pointer will cause a crash. The object must still be alive
|
|
58
|
+
* (not deallocated) when you call this function.
|
|
59
|
+
*/
|
|
60
|
+
declare function fromPointer(pointer: Buffer | bigint): NobjcObject;
|
|
61
|
+
export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol, getPointer, fromPointer };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LoadLibrary, GetClassObject, ObjcObject, CreateProtocolImplementation } from "./native.js";
|
|
1
|
+
import { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation } from "./native.js";
|
|
2
2
|
const NATIVE_OBJC_OBJECT = Symbol("nativeObjcObject");
|
|
3
3
|
class NobjcLibrary {
|
|
4
4
|
constructor(library) {
|
|
@@ -54,7 +54,7 @@ class NobjcObject {
|
|
|
54
54
|
if (!(methodName in receiver)) {
|
|
55
55
|
throw new Error(`Method ${methodName} not found on object ${receiver}`);
|
|
56
56
|
}
|
|
57
|
-
return
|
|
57
|
+
return NobjcMethod(object, methodName);
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
60
|
return new Proxy(object, handler);
|
|
@@ -66,22 +66,21 @@ function unwrapArg(arg) {
|
|
|
66
66
|
}
|
|
67
67
|
return arg;
|
|
68
68
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
return result;
|
|
69
|
+
// Note: This is actually a factory function that returns a callable Proxy
|
|
70
|
+
const NobjcMethod = function (object, methodName) {
|
|
71
|
+
const selector = NobjcMethodNameToObjcSelector(methodName);
|
|
72
|
+
// This cannot be an arrow function because we need to access `arguments`.
|
|
73
|
+
function methodFunc() {
|
|
74
|
+
const unwrappedArgs = Array.from(arguments).map(unwrapArg);
|
|
75
|
+
const result = object.$msgSend(selector, ...unwrappedArgs);
|
|
76
|
+
if (typeof result == "object" && result instanceof ObjcObject) {
|
|
77
|
+
return new NobjcObject(result);
|
|
80
78
|
}
|
|
81
|
-
|
|
82
|
-
return new Proxy(methodFunc, handler);
|
|
79
|
+
return result;
|
|
83
80
|
}
|
|
84
|
-
}
|
|
81
|
+
const handler = {};
|
|
82
|
+
return new Proxy(methodFunc, handler);
|
|
83
|
+
};
|
|
85
84
|
class NobjcProtocol {
|
|
86
85
|
static implement(protocolName, methodImplementations) {
|
|
87
86
|
// Convert method names from $ notation to : notation
|
|
@@ -109,4 +108,61 @@ class NobjcProtocol {
|
|
|
109
108
|
return new NobjcObject(nativeObj);
|
|
110
109
|
}
|
|
111
110
|
}
|
|
112
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Get the raw native pointer for a NobjcObject as a Node Buffer.
|
|
113
|
+
* The pointer is stored in little-endian format (8 bytes on 64-bit macOS).
|
|
114
|
+
*
|
|
115
|
+
* @param obj - The NobjcObject to get the pointer from
|
|
116
|
+
* @returns A Buffer containing the pointer address
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const view = window.contentView();
|
|
121
|
+
* const pointerBuffer = getPointer(view);
|
|
122
|
+
* const pointer = pointerBuffer.readBigUInt64LE(0);
|
|
123
|
+
* console.log(`NSView pointer: 0x${pointer.toString(16)}`);
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
function getPointer(obj) {
|
|
127
|
+
// Unwrap the NobjcObject to get the native ObjcObject
|
|
128
|
+
if (obj && typeof obj === "object" && NATIVE_OBJC_OBJECT in obj) {
|
|
129
|
+
const nativeObj = obj[NATIVE_OBJC_OBJECT];
|
|
130
|
+
return GetPointer(nativeObj);
|
|
131
|
+
}
|
|
132
|
+
throw new TypeError("Argument must be a NobjcObject instance");
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Create a NobjcObject from a raw native pointer.
|
|
136
|
+
*
|
|
137
|
+
* @param pointer - A Buffer or BigInt containing the pointer address
|
|
138
|
+
* @returns A NobjcObject wrapping the native object
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* // From a Buffer
|
|
143
|
+
* const pointerBuffer = getPointer(originalObject);
|
|
144
|
+
* const reconstructed = fromPointer(pointerBuffer);
|
|
145
|
+
*
|
|
146
|
+
* // From a BigInt
|
|
147
|
+
* const pointer = 0x12345678n;
|
|
148
|
+
* const obj = fromPointer(pointer);
|
|
149
|
+
*
|
|
150
|
+
* // Round-trip example
|
|
151
|
+
* const original = NSString.stringWithUTF8String$("Hello");
|
|
152
|
+
* const ptr = getPointer(original).readBigUInt64LE(0);
|
|
153
|
+
* const restored = fromPointer(ptr);
|
|
154
|
+
* console.log(restored.toString()); // "Hello"
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* @warning This is unsafe! The pointer must point to a valid Objective-C object.
|
|
158
|
+
* Using an invalid pointer will cause a crash. The object must still be alive
|
|
159
|
+
* (not deallocated) when you call this function.
|
|
160
|
+
*/
|
|
161
|
+
function fromPointer(pointer) {
|
|
162
|
+
const nativeObj = FromPointer(pointer);
|
|
163
|
+
if (nativeObj === null) {
|
|
164
|
+
throw new Error("Cannot create object from null pointer");
|
|
165
|
+
}
|
|
166
|
+
return new NobjcObject(nativeObj);
|
|
167
|
+
}
|
|
168
|
+
export { NobjcLibrary, NobjcObject, NobjcMethod, NobjcProtocol, getPointer, fromPointer };
|
package/dist/native.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as _binding from "#nobjc_native";
|
|
2
|
-
declare const LoadLibrary: typeof _binding.LoadLibrary, GetClassObject: typeof _binding.GetClassObject, ObjcObject: typeof _binding.ObjcObject, CreateProtocolImplementation: typeof _binding.CreateProtocolImplementation;
|
|
3
|
-
export { LoadLibrary, GetClassObject, ObjcObject, CreateProtocolImplementation };
|
|
2
|
+
declare const LoadLibrary: typeof _binding.LoadLibrary, GetClassObject: typeof _binding.GetClassObject, ObjcObject: typeof _binding.ObjcObject, GetPointer: typeof _binding.GetPointer, FromPointer: typeof _binding.FromPointer, CreateProtocolImplementation: typeof _binding.CreateProtocolImplementation;
|
|
3
|
+
export { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation };
|
|
4
4
|
export type { _binding as NobjcNative };
|
package/dist/native.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
const require = createRequire(import.meta.url);
|
|
3
3
|
const binding = require("#nobjc_native");
|
|
4
|
-
const { LoadLibrary, GetClassObject, ObjcObject, CreateProtocolImplementation } = binding;
|
|
5
|
-
export { LoadLibrary, GetClassObject, ObjcObject, CreateProtocolImplementation };
|
|
4
|
+
const { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation } = binding;
|
|
5
|
+
export { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation };
|
package/package.json
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"format": "prettier --write \"**/*.{ts,js,json,md}\"",
|
|
37
37
|
"preinstall-disabled": "npm run build-scripts && npm run make-clangd-config"
|
|
38
38
|
},
|
|
39
|
-
"version": "0.0.
|
|
39
|
+
"version": "0.0.6",
|
|
40
40
|
"description": "Objective-C bridge for Node.js",
|
|
41
41
|
"main": "dist/index.js",
|
|
42
42
|
"devDependencies": {
|