88#include < cassert>
99#include < cmath>
1010#include < cstdlib>
11+ #include < map>
12+ #include < mutex>
1113#include < stdexcept>
1214
1315#include < jsi/instrumentation.h>
@@ -18,6 +20,63 @@ namespace jsi {
1820
1921namespace {
2022
23+ // / A global map used to store custom runtime data for VMs that do not provide
24+ // / their own default implementation of setRuntimeData and getRuntimeData.
25+ struct RuntimeDataGlobal {
26+ // / Mutex protecting the Runtime data map
27+ std::mutex mutex_{};
28+ // / Maps a runtime pointer to a map of its custom data. At destruction of the
29+ // / runtime, its entry will be removed from the global map.
30+ std::unordered_map<
31+ Runtime*,
32+ std::unordered_map<
33+ UUID ,
34+ std::pair<const void *, void (*)(const void * data)>,
35+ UUID ::Hash>>
36+ dataMap_;
37+ };
38+
39+ RuntimeDataGlobal& getRuntimeDataGlobal () {
40+ static RuntimeDataGlobal runtimeData{};
41+ return runtimeData;
42+ }
43+
44+ // / A host object that, when destructed, will remove the runtime's custom data
45+ // / entry from the global map of custom data.
46+ class RemoveRuntimeDataHostObject : public jsi ::HostObject {
47+ public:
48+ explicit RemoveRuntimeDataHostObject (Runtime* runtime) : runtime_(runtime) {}
49+
50+ RemoveRuntimeDataHostObject (const RemoveRuntimeDataHostObject&) = default ;
51+ RemoveRuntimeDataHostObject (RemoveRuntimeDataHostObject&&) = default ;
52+ RemoveRuntimeDataHostObject& operator =(const RemoveRuntimeDataHostObject&) =
53+ default ;
54+ RemoveRuntimeDataHostObject& operator =(RemoveRuntimeDataHostObject&&) =
55+ default ;
56+
57+ ~RemoveRuntimeDataHostObject () override {
58+ auto & runtimeDataGlobal = getRuntimeDataGlobal ();
59+ std::lock_guard<std::mutex> lock (runtimeDataGlobal.mutex_ );
60+ auto runtimeMapIt = runtimeDataGlobal.dataMap_ .find (runtime_);
61+ // We install the RemoveRuntimeDataHostObject only when the first custom
62+ // data for the runtime is added, and only this object is responsible for
63+ // clearing runtime data. Thus, we should always be able to find the data
64+ // entry.
65+ assert (
66+ runtimeMapIt != runtimeDataGlobal.dataMap_ .end () &&
67+ " Custom runtime data not found for this runtime" );
68+
69+ for (auto [_, entry] : runtimeMapIt->second ) {
70+ auto * deleter = entry.second ;
71+ deleter (entry.first );
72+ }
73+ runtimeDataGlobal.dataMap_ .erase (runtime_);
74+ }
75+
76+ private:
77+ Runtime* runtime_;
78+ };
79+
2180// This is used for generating short exception strings.
2281std::string kindToString (const Value& v, Runtime* rt = nullptr ) {
2382 if (v.isUndefined ()) {
@@ -353,6 +412,62 @@ Object Runtime::createObjectWithPrototype(const Value& prototype) {
353412 return createFn.call (*this , prototype).asObject (*this );
354413}
355414
415+ void Runtime::setRuntimeDataImpl (
416+ const UUID & uuid,
417+ const void * data,
418+ void (*deleter)(const void * data)) {
419+ auto & runtimeDataGlobal = getRuntimeDataGlobal ();
420+ std::lock_guard<std::mutex> lock (runtimeDataGlobal.mutex_ );
421+ if (auto it = runtimeDataGlobal.dataMap_ .find (this );
422+ it != runtimeDataGlobal.dataMap_ .end ()) {
423+ auto & map = it->second ;
424+ if (auto entryIt = map.find (uuid); entryIt != map.end ()) {
425+ // Free the old data
426+ auto oldData = entryIt->second .first ;
427+ auto oldDataDeleter = entryIt->second .second ;
428+ oldDataDeleter (oldData);
429+ }
430+ map[uuid] = {data, deleter};
431+ return ;
432+ }
433+ // No custom data entry exist for this runtime in the global map, so create
434+ // one.
435+ runtimeDataGlobal.dataMap_ [this ][uuid] = {data, deleter};
436+
437+ // The first time data is added for this runtime is added to the map, install
438+ // a host object on the global object of the runtime. This host object is used
439+ // to release the runtime's entry from the global custom data map when the
440+ // runtime is destroyed.
441+ // Also, try to protect the host object by making it non-configurable,
442+ // non-enumerable, and non-writable. These JSI operations are purposely
443+ // performed after runtime-specific data map is added and the host object is
444+ // created to prevent data leaks if any operations fail.
445+ Object ho = Object::createFromHostObject (
446+ *this , std::make_shared<RemoveRuntimeDataHostObject>(this ));
447+ global ().setProperty (*this , " _jsiRuntimeDataCleanUp" , ho);
448+ auto definePropertyFn = global ()
449+ .getPropertyAsObject (*this , " Object" )
450+ .getPropertyAsFunction (*this , " defineProperty" );
451+ auto desc = Object (*this );
452+ desc.setProperty (*this , " configurable" , Value (false ));
453+ desc.setProperty (*this , " enumerable" , Value (false ));
454+ desc.setProperty (*this , " writable" , Value (false ));
455+ definePropertyFn.call (*this , global (), " _jsiRuntimeDataCleanUp" , desc);
456+ }
457+
458+ const void * Runtime::getRuntimeDataImpl (const UUID & uuid) {
459+ auto & runtimeDataGlobal = getRuntimeDataGlobal ();
460+ std::lock_guard<std::mutex> lock (runtimeDataGlobal.mutex_ );
461+ if (auto runtimeMapIt = runtimeDataGlobal.dataMap_ .find (this );
462+ runtimeMapIt != runtimeDataGlobal.dataMap_ .end ()) {
463+ if (auto customDataIt = runtimeMapIt->second .find (uuid);
464+ customDataIt != runtimeMapIt->second .end ()) {
465+ return customDataIt->second .first ;
466+ }
467+ }
468+ return nullptr ;
469+ }
470+
356471Pointer& Pointer::operator =(Pointer&& other) noexcept {
357472 if (ptr_) {
358473 ptr_->invalidate ();
0 commit comments