objc-js 0.0.6 → 0.0.8

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/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation } from "./native.js";
2
+ const customInspectSymbol = Symbol.for("nodejs.util.inspect.custom");
2
3
  const NATIVE_OBJC_OBJECT = Symbol("nativeObjcObject");
3
4
  class NobjcLibrary {
4
5
  constructor(library) {
@@ -24,6 +25,7 @@ function ObjcSelectorToNobjcMethodName(selector) {
24
25
  }
25
26
  class NobjcObject {
26
27
  constructor(object) {
28
+ // Create the proxy handler
27
29
  const handler = {
28
30
  has(target, p) {
29
31
  // Return true for the special Symbol to enable unwrapping
@@ -36,7 +38,12 @@ class NobjcObject {
36
38
  if (p === "toString")
37
39
  return true;
38
40
  // check if the object responds to the selector
39
- return target.$msgSend("respondsToSelector:", NobjcMethodNameToObjcSelector(p.toString()));
41
+ try {
42
+ return target.$msgSend("respondsToSelector:", NobjcMethodNameToObjcSelector(p.toString()));
43
+ }
44
+ catch (e) {
45
+ return false;
46
+ }
40
47
  },
41
48
  get(target, methodName, receiver) {
42
49
  // Return the underlying native object when Symbol is accessed
@@ -49,7 +56,29 @@ class NobjcObject {
49
56
  }
50
57
  // handle toString separately
51
58
  if (methodName === "toString") {
52
- return () => String(object.$msgSend("description"));
59
+ // if the receiver has a UTF8String method, use it to get the string representation
60
+ if ("UTF8String" in receiver) {
61
+ return () => String(object.$msgSend("UTF8String"));
62
+ }
63
+ // Otherwise, use the description method
64
+ return () => String(wrapObjCObjectIfNeeded(object.$msgSend("description")));
65
+ }
66
+ // handle other built-in Object.prototype properties
67
+ const builtInProps = [
68
+ "constructor",
69
+ "valueOf",
70
+ "hasOwnProperty",
71
+ "isPrototypeOf",
72
+ "propertyIsEnumerable",
73
+ "toLocaleString",
74
+ "__proto__",
75
+ "__defineGetter__",
76
+ "__defineSetter__",
77
+ "__lookupGetter__",
78
+ "__lookupSetter__"
79
+ ];
80
+ if (builtInProps.includes(methodName)) {
81
+ return Reflect.get(target, methodName);
53
82
  }
54
83
  if (!(methodName in receiver)) {
55
84
  throw new Error(`Method ${methodName} not found on object ${receiver}`);
@@ -57,7 +86,12 @@ class NobjcObject {
57
86
  return NobjcMethod(object, methodName);
58
87
  }
59
88
  };
60
- return new Proxy(object, handler);
89
+ // Create the proxy
90
+ const proxy = new Proxy(object, handler);
91
+ // This is used to override the default inspect behavior for the object. (console.log)
92
+ object[customInspectSymbol] = () => proxy.toString();
93
+ // Return the proxy
94
+ return proxy;
61
95
  }
62
96
  }
63
97
  function unwrapArg(arg) {
@@ -66,6 +100,12 @@ function unwrapArg(arg) {
66
100
  }
67
101
  return arg;
68
102
  }
103
+ function wrapObjCObjectIfNeeded(result) {
104
+ if (typeof result == "object" && result instanceof ObjcObject) {
105
+ return new NobjcObject(result);
106
+ }
107
+ return result;
108
+ }
69
109
  // Note: This is actually a factory function that returns a callable Proxy
70
110
  const NobjcMethod = function (object, methodName) {
71
111
  const selector = NobjcMethodNameToObjcSelector(methodName);
@@ -73,10 +113,7 @@ const NobjcMethod = function (object, methodName) {
73
113
  function methodFunc() {
74
114
  const unwrappedArgs = Array.from(arguments).map(unwrapArg);
75
115
  const result = object.$msgSend(selector, ...unwrappedArgs);
76
- if (typeof result == "object" && result instanceof ObjcObject) {
77
- return new NobjcObject(result);
78
- }
79
- return result;
116
+ return wrapObjCObjectIfNeeded(result);
80
117
  }
81
118
  const handler = {};
82
119
  return new Proxy(methodFunc, handler);
@@ -87,19 +124,15 @@ class NobjcProtocol {
87
124
  const convertedMethods = {};
88
125
  for (const [methodName, impl] of Object.entries(methodImplementations)) {
89
126
  const selector = NobjcMethodNameToObjcSelector(methodName);
90
- // Wrap the implementation to unwrap args and wrap return values
127
+ // Wrap the implementation to wrap args and unwrap return values
91
128
  convertedMethods[selector] = function (...args) {
92
- const unwrappedArgs = args.map(unwrapArg);
93
- const result = impl(...unwrappedArgs);
129
+ // Wrap native ObjcObject arguments in NobjcObject proxies
130
+ const wrappedArgs = args.map((arg) => {
131
+ return wrapObjCObjectIfNeeded(arg);
132
+ });
133
+ const result = impl(...wrappedArgs);
94
134
  // If the result is already a NobjcObject, unwrap it to get the native object
95
- if (result && typeof result === "object" && NATIVE_OBJC_OBJECT in result) {
96
- return result[NATIVE_OBJC_OBJECT];
97
- }
98
- // If the result is a native ObjcObject, return it as-is
99
- if (typeof result === "object" && result instanceof ObjcObject) {
100
- return result;
101
- }
102
- return result;
135
+ return unwrapArg(result);
103
136
  };
104
137
  }
105
138
  // Call native implementation
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.6",
39
+ "version": "0.0.8",
40
40
  "description": "Objective-C bridge for Node.js",
41
41
  "main": "dist/index.js",
42
42
  "devDependencies": {