1 #include <JsiHostObject.h>
2 #include <functional>
3 #include <vector>
4 
5 // To be able to find objects that aren't cleaned up correctly,
6 // we can set this value to 1 and debug the constructor/destructor
7 #define JSI_DEBUG_ALLOCATIONS 0
8 
9 namespace RNJsi {
10 
11 #if JSI_DEBUG_ALLOCATIONS
12 int objCounter = 0;
13 std::vector<JsiHostObject *> objects;
14 #endif
15 
JsiHostObject()16 JsiHostObject::JsiHostObject() {
17 #if JSI_DEBUG_ALLOCATIONS
18   objects.push_back(this);
19   objCounter++;
20 #endif
21 }
~JsiHostObject()22 JsiHostObject::~JsiHostObject() {
23 #if JSI_DEBUG_ALLOCATIONS
24   for (size_t i = 0; i < objects.size(); ++i) {
25     if (objects.at(i) == this) {
26       objects.erase(objects.begin() + i);
27       break;
28     }
29   }
30   objCounter--;
31 #endif
32 }
33 
set(jsi::Runtime & rt,const jsi::PropNameID & name,const jsi::Value & value)34 void JsiHostObject::set(jsi::Runtime &rt, const jsi::PropNameID &name,
35                         const jsi::Value &value) {
36 
37   auto nameStr = name.utf8(rt);
38 
39   /** Check the static setters map */
40   const JsiPropertySettersMap &setters = getExportedPropertySettersMap();
41   auto setter = setters.find(nameStr);
42   if (setter != setters.end()) {
43     auto dispatcher = std::bind(setter->second, this, std::placeholders::_1,
44                                 std::placeholders::_2);
45     return dispatcher(rt, value);
46   }
47 
48   if (_propMap.count(nameStr) > 0) {
49     auto prop = _propMap.at(nameStr);
50     (prop.set)(rt, value);
51   }
52 }
53 
get(jsi::Runtime & runtime,const jsi::PropNameID & name)54 jsi::Value JsiHostObject::get(jsi::Runtime &runtime,
55                               const jsi::PropNameID &name) {
56   auto nameStr = name.utf8(runtime);
57 
58   // Happy path - cached host functions are cheapest to look up
59   const JsiFunctionMap &funcs = getExportedFunctionMap();
60   auto func = funcs.find(nameStr);
61 
62   // Check function cache
63   if (func != funcs.end()) {
64     std::map<std::string, jsi::Function> &runtimeCache =
65         _hostFunctionCache.get(runtime);
66     auto cachedFunc = runtimeCache.find(nameStr);
67     if (cachedFunc != runtimeCache.end()) {
68       return cachedFunc->second.asFunction(runtime);
69     }
70   }
71 
72   // Check the static getters map
73   const JsiPropertyGettersMap &getters = getExportedPropertyGettersMap();
74   auto getter = getters.find(nameStr);
75   if (getter != getters.end()) {
76     auto dispatcher = std::bind(getter->second, this, std::placeholders::_1);
77     return dispatcher(runtime);
78   }
79 
80   // Check the static function map
81   if (func != funcs.end()) {
82 
83     // Create dispatcher
84     auto dispatcher =
85         std::bind(func->second, reinterpret_cast<JsiHostObject *>(this),
86                   std::placeholders::_1, std::placeholders::_2,
87                   std::placeholders::_3, std::placeholders::_4);
88 
89     // Add to cache - it is important to cache the results from the
90     // createFromHostFunction function which takes some time.
91     return _hostFunctionCache.get(runtime)
92         .emplace(nameStr, jsi::Function::createFromHostFunction(runtime, name,
93                                                                 0, dispatcher))
94         .first->second.asFunction(runtime);
95   }
96 
97   if (_funcMap.count(nameStr) > 0) {
98     return jsi::Function::createFromHostFunction(runtime, name, 0,
99                                                  _funcMap.at(nameStr));
100   }
101 
102   if (_propMap.count(nameStr) > 0) {
103     auto prop = _propMap.at(nameStr);
104     return (prop.get)(runtime);
105   }
106 
107   return jsi::Value::undefined();
108 }
109 
110 std::vector<jsi::PropNameID>
getPropertyNames(jsi::Runtime & runtime)111 JsiHostObject::getPropertyNames(jsi::Runtime &runtime) {
112   // statically exported functions
113   const auto &funcs = getExportedFunctionMap();
114 
115   // Statically exported property getters
116   const auto &getters = getExportedPropertyGettersMap();
117 
118   // Statically exported property setters
119   const auto &setters = getExportedPropertySettersMap();
120 
121   std::vector<jsi::PropNameID> propNames;
122   propNames.reserve(funcs.size() + getters.size() + setters.size() +
123                     _funcMap.size() + _propMap.size());
124 
125   for (auto it = funcs.cbegin(); it != funcs.cend(); ++it) {
126     propNames.push_back(jsi::PropNameID::forAscii(runtime, it->first));
127   }
128 
129   for (auto it = getters.cbegin(); it != getters.cend(); ++it) {
130     propNames.push_back(jsi::PropNameID::forUtf8(runtime, it->first));
131   }
132 
133   for (auto it = getters.cbegin(); it != getters.cend(); ++it) {
134     if (getters.count(it->first) == 0) {
135       propNames.push_back(jsi::PropNameID::forUtf8(runtime, it->first));
136     }
137   }
138 
139   // functions
140   for (auto it = _funcMap.cbegin(); it != _funcMap.cend(); ++it) {
141     propNames.push_back(jsi::PropNameID::forAscii(runtime, it->first));
142   }
143   // props
144   for (auto it = _propMap.cbegin(); it != _propMap.cend(); ++it) {
145     propNames.push_back(jsi::PropNameID::forAscii(runtime, it->first));
146   }
147   return propNames;
148 }
149 
150 } // namespace RNJsi
151