objc-js 0.0.15 → 1.0.1

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,145 @@
1
+ #ifndef PROTOCOL_MANAGER_H
2
+ #define PROTOCOL_MANAGER_H
3
+
4
+ /**
5
+ * @file protocol-manager.h
6
+ * @brief Singleton manager for protocol implementations.
7
+ *
8
+ * ProtocolManager provides thread-safe access to protocol implementation storage.
9
+ * It replaces the global g_implementations map and g_implementations_mutex with
10
+ * an encapsulated singleton pattern.
11
+ *
12
+ * Usage:
13
+ * // Register a protocol implementation
14
+ * ProtocolManager::Instance().Register(instancePtr, std::move(impl));
15
+ *
16
+ * // Find an implementation
17
+ * auto* impl = ProtocolManager::Instance().Find(instancePtr);
18
+ *
19
+ * // Execute a callback with lock held
20
+ * ProtocolManager::Instance().WithLock([](auto& map) {
21
+ * // ... work with map ...
22
+ * });
23
+ */
24
+
25
+ #include "protocol-storage.h"
26
+ #include <functional>
27
+ #include <mutex>
28
+ #include <optional>
29
+ #include <unordered_map>
30
+
31
+ namespace nobjc {
32
+
33
+ /**
34
+ * @brief Thread-safe singleton manager for protocol implementations.
35
+ *
36
+ * Encapsulates storage and synchronization for protocol implementations,
37
+ * providing a clean API for registration, lookup, and unregistration.
38
+ */
39
+ class ProtocolManager {
40
+ public:
41
+ /**
42
+ * @brief Get the singleton instance.
43
+ * @return Reference to the singleton ProtocolManager.
44
+ */
45
+ static ProtocolManager& Instance() {
46
+ static ProtocolManager instance;
47
+ return instance;
48
+ }
49
+
50
+ // Delete copy/move operations for singleton
51
+ ProtocolManager(const ProtocolManager&) = delete;
52
+ ProtocolManager& operator=(const ProtocolManager&) = delete;
53
+ ProtocolManager(ProtocolManager&&) = delete;
54
+ ProtocolManager& operator=(ProtocolManager&&) = delete;
55
+
56
+ /**
57
+ * @brief Find an implementation by instance pointer.
58
+ * @param instancePtr The Objective-C instance pointer (bridged from id).
59
+ * @return Pointer to implementation if found, nullptr otherwise.
60
+ * @note Caller must NOT hold the lock. This method acquires the lock.
61
+ */
62
+ ProtocolImplementation* Find(void* instancePtr) {
63
+ std::lock_guard<std::mutex> lock(mutex_);
64
+ auto it = implementations_.find(instancePtr);
65
+ if (it != implementations_.end()) {
66
+ return &it->second;
67
+ }
68
+ return nullptr;
69
+ }
70
+
71
+ /**
72
+ * @brief Register a new protocol implementation.
73
+ * @param instancePtr The Objective-C instance pointer.
74
+ * @param impl The implementation to register (moved).
75
+ */
76
+ void Register(void* instancePtr, ProtocolImplementation&& impl) {
77
+ std::lock_guard<std::mutex> lock(mutex_);
78
+ implementations_.emplace(instancePtr, std::move(impl));
79
+ }
80
+
81
+ /**
82
+ * @brief Unregister a protocol implementation.
83
+ * @param instancePtr The Objective-C instance pointer.
84
+ * @return true if the implementation was found and removed, false otherwise.
85
+ */
86
+ bool Unregister(void* instancePtr) {
87
+ std::lock_guard<std::mutex> lock(mutex_);
88
+ return implementations_.erase(instancePtr) > 0;
89
+ }
90
+
91
+ /**
92
+ * @brief Execute a callback with the lock held.
93
+ * @param callback Function to call with the map reference.
94
+ *
95
+ * Use this for complex operations that need to access multiple map entries
96
+ * or perform conditional logic while holding the lock.
97
+ */
98
+ template <typename Callback>
99
+ auto WithLock(Callback&& callback)
100
+ -> decltype(callback(std::declval<std::unordered_map<void*, ProtocolImplementation>&>())) {
101
+ std::lock_guard<std::mutex> lock(mutex_);
102
+ return callback(implementations_);
103
+ }
104
+
105
+ /**
106
+ * @brief Execute a read-only callback with the lock held.
107
+ * @param callback Function to call with const map reference.
108
+ */
109
+ template <typename Callback>
110
+ auto WithLockConst(Callback&& callback) const
111
+ -> decltype(callback(std::declval<const std::unordered_map<void*, ProtocolImplementation>&>())) {
112
+ std::lock_guard<std::mutex> lock(mutex_);
113
+ return callback(implementations_);
114
+ }
115
+
116
+ /**
117
+ * @brief Get the number of registered implementations.
118
+ * @return Count of implementations.
119
+ */
120
+ size_t Size() const {
121
+ std::lock_guard<std::mutex> lock(mutex_);
122
+ return implementations_.size();
123
+ }
124
+
125
+ /**
126
+ * @brief Check if an implementation exists for the given pointer.
127
+ * @param instancePtr The Objective-C instance pointer.
128
+ * @return true if registered, false otherwise.
129
+ */
130
+ bool Contains(void* instancePtr) const {
131
+ std::lock_guard<std::mutex> lock(mutex_);
132
+ return implementations_.find(instancePtr) != implementations_.end();
133
+ }
134
+
135
+ private:
136
+ ProtocolManager() = default;
137
+ ~ProtocolManager() = default;
138
+
139
+ mutable std::mutex mutex_;
140
+ std::unordered_map<void*, ProtocolImplementation> implementations_;
141
+ };
142
+
143
+ } // namespace nobjc
144
+
145
+ #endif // PROTOCOL_MANAGER_H
@@ -89,18 +89,15 @@ struct SubclassImplementation {
89
89
  bool isElectron;
90
90
  };
91
91
 
92
- // MARK: - Global Storage
92
+ // MARK: - Global Storage (DEPRECATED - use ProtocolManager/SubclassManager instead)
93
+ // These declarations are kept for backward compatibility during migration.
94
+ // New code should use:
95
+ // - nobjc::ProtocolManager::Instance() for protocol implementations
96
+ // - nobjc::SubclassManager::Instance() for subclass implementations
93
97
 
94
- // Global map: instance pointer -> implementation details
95
- // This keeps JavaScript callbacks alive for the lifetime of the Objective-C
96
- // object
97
- extern std::unordered_map<void *, ProtocolImplementation> g_implementations;
98
- extern std::mutex g_implementations_mutex;
99
-
100
- // Global map: Class pointer -> subclass implementation details
101
- // This stores information about JS-defined subclasses
102
- extern std::unordered_map<void *, SubclassImplementation> g_subclasses;
103
- extern std::mutex g_subclasses_mutex;
98
+ // Note: These extern declarations are removed as storage is now in the manager singletons.
99
+ // If you need to access the storage, use the manager classes directly.
100
+ // See protocol-manager.h and subclass-manager.h for the new APIs.
104
101
 
105
102
  // MARK: - Storage Access Helpers
106
103
 
@@ -113,29 +110,11 @@ inline void SignalInvocationComplete(InvocationData *data) {
113
110
  }
114
111
  }
115
112
 
116
- // Look up implementation for an instance pointer
117
- // Returns nullptr if not found
118
- // Caller must hold g_implementations_mutex
119
- inline ProtocolImplementation *
120
- FindImplementation(void *instancePtr) {
121
- auto it = g_implementations.find(instancePtr);
122
- if (it != g_implementations.end()) {
123
- return &it->second;
124
- }
125
- return nullptr;
126
- }
113
+ // DEPRECATED: Use nobjc::ProtocolManager::Instance().Find(instancePtr) instead
114
+ // This function is no longer available as global storage has been moved to manager classes.
127
115
 
128
- // Look up subclass implementation for a Class pointer
129
- // Returns nullptr if not found
130
- // Caller must hold g_subclasses_mutex
131
- inline SubclassImplementation *
132
- FindSubclassImplementation(void *classPtr) {
133
- auto it = g_subclasses.find(classPtr);
134
- if (it != g_subclasses.end()) {
135
- return &it->second;
136
- }
137
- return nullptr;
138
- }
116
+ // DEPRECATED: Use nobjc::SubclassManager::Instance().Find(classPtr) instead
117
+ // This function is no longer available as global storage has been moved to manager classes.
139
118
 
140
119
  #endif // PROTOCOL_STORAGE_H
141
120
 
@@ -0,0 +1,54 @@
1
+ #ifndef RUNTIME_DETECTION_H
2
+ #define RUNTIME_DETECTION_H
3
+
4
+ #include <napi.h>
5
+
6
+ /**
7
+ * Detects if the code is running in an Electron environment.
8
+ *
9
+ * This is detected by checking for process.versions.electron.
10
+ * In Electron, direct JS callback invocation may fail due to V8 context
11
+ * issues, so we need to use ThreadSafeFunction for all callbacks.
12
+ *
13
+ * @param env The N-API environment
14
+ * @return true if running in Electron, false otherwise
15
+ */
16
+ inline bool IsElectronRuntime(Napi::Env env) {
17
+ try {
18
+ Napi::Object global = env.Global();
19
+ if (global.Has("process")) {
20
+ Napi::Object process = global.Get("process").As<Napi::Object>();
21
+ if (process.Has("versions")) {
22
+ Napi::Object versions = process.Get("versions").As<Napi::Object>();
23
+ return versions.Has("electron");
24
+ }
25
+ }
26
+ } catch (...) {
27
+ // If detection fails, assume not Electron
28
+ }
29
+ return false;
30
+ }
31
+
32
+ /**
33
+ * Detects if the code is running in Bun.
34
+ *
35
+ * @param env The N-API environment
36
+ * @return true if running in Bun, false otherwise
37
+ */
38
+ inline bool IsBunRuntime(Napi::Env env) {
39
+ try {
40
+ Napi::Object global = env.Global();
41
+ if (global.Has("process")) {
42
+ Napi::Object process = global.Get("process").As<Napi::Object>();
43
+ if (process.Has("versions")) {
44
+ Napi::Object versions = process.Get("versions").As<Napi::Object>();
45
+ return versions.Has("bun");
46
+ }
47
+ }
48
+ } catch (...) {
49
+ // If detection fails, assume not Bun
50
+ }
51
+ return false;
52
+ }
53
+
54
+ #endif // RUNTIME_DETECTION_H