• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "napi/native_api.h"
17 #include "napi/native_node_api.h"
18 
19 #include "securec.h"
20 
21 #include <map>
22 #include <string>
23 
24 namespace {
25 constexpr size_t EVENT_TYPE_SIZE = 32;
26 constexpr size_t KEY_BUFFER_SIZE = 32;
27 constexpr size_t VALUE_BUFFER_SIZE = 128;
28 } // namespace
29 
30 static std::map<std::string, std::string> g_keyValueStorage;
31 
32 /***********************************************
33  * Storage Constructor
34  ***********************************************/
35 struct EventHandler {
36     napi_ref callbackRef = nullptr;
37     EventHandler* next = nullptr;
38 };
39 
40 class EventListener {
41 public:
EventListener()42     EventListener() : type_(nullptr), handlers_(nullptr) {}
~EventListener()43     virtual ~EventListener() {}
Add(napi_env env,napi_value handler)44     void Add(napi_env env, napi_value handler)
45     {
46         if (Find(env, handler) != nullptr)
47             return;
48 
49         if (handlers_ == nullptr) {
50             handlers_ = new EventHandler();
51             handlers_->next = nullptr;
52         } else {
53             auto temp = new EventHandler();
54             temp->next = handlers_;
55             handlers_ = temp;
56         }
57         napi_create_reference(env, handler, 1, &handlers_->callbackRef);
58     }
59 
Del(napi_env env,napi_value handler)60     void Del(napi_env env, napi_value handler)
61     {
62         EventHandler* temp = nullptr;
63         for (EventHandler* i = handlers_; i != nullptr; i = handlers_) {
64             napi_value callback = nullptr;
65             napi_get_reference_value(env, i->callbackRef, &callback);
66             bool isEquals = false;
67             napi_strict_equals(env, handler, callback, &isEquals);
68             if (isEquals) {
69                 if (temp == nullptr) {
70                     handlers_ = i->next;
71                 } else {
72                     temp->next = i->next;
73                 }
74                 napi_delete_reference(env, i->callbackRef);
75                 delete i;
76             } else {
77                 temp = i;
78             }
79         }
80     }
81 
Clear(napi_env env)82     void Clear(napi_env env)
83     {
84         for (EventHandler* i = handlers_; i != nullptr; i = handlers_) {
85             handlers_ = i->next;
86             delete i;
87         }
88     }
89 
90     const char* type_;
91     EventHandler* handlers_;
92 
93 protected:
Find(napi_env env,napi_value handler)94     EventHandler* Find(napi_env env, napi_value handler)
95     {
96         EventHandler* result = nullptr;
97         for (EventHandler* i = handlers_; i != nullptr; i = i->next) {
98             napi_value callback = nullptr;
99             napi_get_reference_value(env, i->callbackRef, &callback);
100             bool isEquals = false;
101             napi_strict_equals(env, handler, callback, &isEquals);
102             if (isEquals) {
103                 result = i;
104             }
105         }
106         return result;
107     }
108 };
109 
110 enum StorageEvent {
111     STORAGE_EVENT_UNKNOWN = -1,
112     STORAGE_EVENT_CHANGE,
113     STORAGE_EVENT_CLEAR,
114     STORAGE_EVENT_ERROR,
115 };
116 
117 class StorageObjectInfo {
118 public:
StorageObjectInfo(napi_env env)119     explicit StorageObjectInfo(napi_env env) : env_(env), listeners_()
120     {
121         listeners_[STORAGE_EVENT_CHANGE].type_ = "change";
122         listeners_[STORAGE_EVENT_CLEAR].type_ = "clear";
123         listeners_[STORAGE_EVENT_ERROR].type_ = "error";
124     }
125 
~StorageObjectInfo()126     virtual ~StorageObjectInfo()
127     {
128         listeners_[STORAGE_EVENT_CHANGE].Clear(env_);
129         listeners_[STORAGE_EVENT_CLEAR].Clear(env_);
130         listeners_[STORAGE_EVENT_ERROR].Clear(env_);
131     }
132 
On(const char * type,napi_value handler)133     void On(const char* type, napi_value handler)
134     {
135         StorageEvent event = Find(type);
136         if (event == STORAGE_EVENT_UNKNOWN) {
137             return;
138         }
139         listeners_[event].Add(env_, handler);
140     }
141 
Off(const char * type,napi_value handler=nullptr)142     void Off(const char* type, napi_value handler = nullptr)
143     {
144         StorageEvent event = Find(type);
145         if (event == STORAGE_EVENT_UNKNOWN) {
146             return;
147         }
148         if (handler == nullptr) {
149             listeners_[event].Clear(env_);
150         } else {
151             listeners_[event].Del(env_, handler);
152         }
153     }
154 
Emit(napi_value thisArg,const char * type)155     void Emit(napi_value thisArg, const char* type)
156     {
157         StorageEvent event = Find(type);
158         if (event == STORAGE_EVENT_UNKNOWN) {
159             return;
160         }
161         for (EventHandler* handler = listeners_[event].handlers_; handler != nullptr; handler = handler->next) {
162             if (thisArg == nullptr) {
163                 napi_get_undefined(env_, &thisArg);
164             }
165             napi_value callback = nullptr;
166             napi_value result = nullptr;
167             napi_get_reference_value(env_, handler->callbackRef, &callback);
168             napi_call_function(env_, thisArg, callback, 0, nullptr, &result);
169         }
170     }
171 
172 protected:
Find(const char * type) const173     StorageEvent Find(const char* type) const
174     {
175         StorageEvent result = STORAGE_EVENT_UNKNOWN;
176         if (!strcmp(listeners_[STORAGE_EVENT_CHANGE].type_, type)) {
177             result = STORAGE_EVENT_CHANGE;
178         } else if (!strcmp(listeners_[STORAGE_EVENT_CLEAR].type_, type)) {
179             result = STORAGE_EVENT_CLEAR;
180         } else if (!strcmp(listeners_[STORAGE_EVENT_ERROR].type_, type)) {
181             result = STORAGE_EVENT_ERROR;
182         }
183         return result;
184     }
185 
186 private:
187     napi_env env_;
188     EventListener listeners_[3];
189 };
190 
JSStorageConstructor(napi_env env,napi_callback_info info)191 static napi_value JSStorageConstructor(napi_env env, napi_callback_info info)
192 {
193     napi_value thisVar = nullptr;
194     void* data = nullptr;
195     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
196 
197     auto objectInfo = new StorageObjectInfo(env);
198     napi_wrap(
199         env, thisVar, objectInfo,
200         [](napi_env env, void* data, void* hint) {
201             auto objectInfo = (StorageObjectInfo*)data;
202             if (objectInfo != nullptr) {
203                 delete objectInfo;
204             }
205         },
206         nullptr, nullptr);
207 
208     return thisVar;
209 }
210 
211 /***********************************************
212  * Async Function Set
213  ***********************************************/
214 struct StorageAsyncContext {
215     napi_env env = nullptr;
216     napi_async_work work = nullptr;
217 
218     char key[KEY_BUFFER_SIZE] = { 0 };
219     size_t keyLen = 0;
220     char value[VALUE_BUFFER_SIZE] = { 0 };
221     size_t valueLen = 0;
222     napi_deferred deferred = nullptr;
223     napi_ref callbackRef = nullptr;
224 
225     int status = 0;
226     StorageObjectInfo* objectInfo = nullptr;
227 };
228 
229 // storage.get(key: string, defaultValue?: string, callback?: Function): void | Promise<string>
JSStorageGet(napi_env env,napi_callback_info info)230 static napi_value JSStorageGet(napi_env env, napi_callback_info info)
231 {
232     size_t requireArgc = 1;
233     size_t argc = 3;
234     napi_value argv[3] = { 0 };
235     napi_value thisVar = nullptr;
236     void* data = nullptr;
237     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
238 
239     NAPI_ASSERT(env, argc >= requireArgc, "requires 1 parameter");
240 
241     auto asyncContext = new StorageAsyncContext();
242 
243     asyncContext->env = env;
244 
245     for (size_t i = 0; i < argc; i++) {
246         napi_valuetype valueType = napi_undefined;
247         napi_typeof(env, argv[i], &valueType);
248 
249         if ((i == 0) && (valueType == napi_string)) {
250             napi_get_value_string_utf8(env, argv[i], asyncContext->key, KEY_BUFFER_SIZE, &asyncContext->keyLen);
251         } else if (valueType == napi_string) {
252             napi_get_value_string_utf8(env, argv[i], asyncContext->value, VALUE_BUFFER_SIZE, &asyncContext->valueLen);
253         } else if (valueType == napi_function) {
254             napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
255             break;
256         } else {
257             NAPI_ASSERT(env, false, "type mismatch");
258         }
259     }
260 
261     napi_value result = nullptr;
262 
263     if (asyncContext->callbackRef == nullptr) {
264         napi_create_promise(env, &asyncContext->deferred, &result);
265     } else {
266         napi_get_undefined(env, &result);
267     }
268 
269     napi_unwrap(env, thisVar, (void**)&asyncContext->objectInfo);
270 
271     napi_value resource = nullptr;
272     napi_create_string_utf8(env, "JSStorageGet", NAPI_AUTO_LENGTH, &resource);
273 
274     napi_create_async_work(
275         env, nullptr, resource,
276         [](napi_env env, void* data) {
277             StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
278             auto itr = g_keyValueStorage.find(asyncContext->key);
279             if (itr != g_keyValueStorage.end()) {
280                 if (strncpy_s(asyncContext->value, VALUE_BUFFER_SIZE, itr->second.c_str(), itr->second.length()) !=
281                     EOK) {
282                     asyncContext->status = 1;
283                 } else {
284                     asyncContext->status = 0;
285                 }
286             } else {
287                 asyncContext->status = 1;
288             }
289         },
290         [](napi_env env, napi_status status, void* data) {
291             StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
292             napi_value result[2] = { 0 };
293             if (!asyncContext->status) {
294                 napi_get_undefined(env, &result[0]);
295                 napi_create_string_utf8(env, asyncContext->value, strlen(asyncContext->value), &result[1]);
296             } else {
297                 napi_value message = nullptr;
298                 napi_create_string_utf8(env, "key does not exist", NAPI_AUTO_LENGTH, &message);
299                 napi_create_error(env, nullptr, message, &result[0]);
300                 napi_get_undefined(env, &result[1]);
301                 asyncContext->objectInfo->Emit(nullptr, "error");
302             }
303             if (asyncContext->deferred) {
304                 if (!asyncContext->status) {
305                     napi_resolve_deferred(env, asyncContext->deferred, result[1]);
306                 } else {
307                     napi_reject_deferred(env, asyncContext->deferred, result[0]);
308                 }
309             } else {
310                 napi_value callback = nullptr;
311                 napi_get_reference_value(env, asyncContext->callbackRef, &callback);
312                 napi_call_function(env, nullptr, callback, sizeof(result) / sizeof(result[0]), result, nullptr);
313                 napi_delete_reference(env, asyncContext->callbackRef);
314             }
315             napi_delete_async_work(env, asyncContext->work);
316             delete asyncContext;
317         },
318         (void*)asyncContext, &asyncContext->work);
319     napi_queue_async_work(env, asyncContext->work);
320 
321     return result;
322 }
323 
324 // storage.set(key: string, value: string, callback?: Function): void | Promise<void>
JSStorageSet(napi_env env,napi_callback_info info)325 static napi_value JSStorageSet(napi_env env, napi_callback_info info)
326 {
327     size_t requireArgc = 2;
328     size_t argc = 3;
329     napi_value argv[3] = { 0 };
330     napi_value thisVar = nullptr;
331     void* data = nullptr;
332     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
333 
334     NAPI_ASSERT(env, argc >= requireArgc, "requires 2 parameters");
335     auto asyncContext = new StorageAsyncContext();
336     asyncContext->env = env;
337     for (size_t i = 0; i < argc; i++) {
338         napi_valuetype valueType = napi_undefined;
339         napi_typeof(env, argv[i], &valueType);
340 
341         if (i == 0 && valueType == napi_string) {
342             napi_get_value_string_utf8(env, argv[i], asyncContext->key, KEY_BUFFER_SIZE, &asyncContext->keyLen);
343         } else if (i == 1 && valueType == napi_string) {
344             napi_get_value_string_utf8(env, argv[i], asyncContext->value, VALUE_BUFFER_SIZE, &asyncContext->valueLen);
345         } else if (i == 2 && valueType == napi_function) {
346             napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
347         } else {
348             NAPI_ASSERT(env, false, "type mismatch");
349         }
350     }
351 
352     napi_value result = nullptr;
353 
354     if (asyncContext->callbackRef == nullptr) {
355         napi_create_promise(env, &asyncContext->deferred, &result);
356     } else {
357         napi_get_undefined(env, &result);
358     }
359 
360     napi_unwrap(env, thisVar, (void**)&asyncContext->objectInfo);
361 
362     napi_value resource = nullptr;
363     napi_create_string_utf8(env, "JStorageSet", NAPI_AUTO_LENGTH, &resource);
364 
365     napi_create_async_work(
366         env, nullptr, resource,
367         [](napi_env env, void* data) {
368             StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
369             auto itr = g_keyValueStorage.find(asyncContext->key);
370             if (itr == g_keyValueStorage.end()) {
371                 g_keyValueStorage.insert(std::pair<std::string, std::string>(asyncContext->key, asyncContext->value));
372                 asyncContext->status = 0;
373             } else {
374                 asyncContext->status = 1;
375             }
376         },
377         [](napi_env env, napi_status status, void* data) {
378             StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
379             napi_value result[2] = { 0 };
380             if (!asyncContext->status) {
381                 napi_get_undefined(env, &result[0]);
382                 napi_get_undefined(env, &result[1]);
383                 asyncContext->objectInfo->Emit(nullptr, "change");
384             } else {
385                 napi_value message = nullptr;
386                 napi_create_string_utf8(env, "key already exists", NAPI_AUTO_LENGTH, &message);
387                 napi_create_error(env, nullptr, message, &result[0]);
388                 napi_get_undefined(env, &result[1]);
389                 asyncContext->objectInfo->Emit(nullptr, "error");
390             }
391 
392             if (asyncContext->deferred) {
393                 if (!asyncContext->status) {
394                     napi_resolve_deferred(env, asyncContext->deferred, result[1]);
395                 } else {
396                     napi_reject_deferred(env, asyncContext->deferred, result[0]);
397                 }
398             } else {
399                 napi_value callback = nullptr;
400                 napi_get_reference_value(env, asyncContext->callbackRef, &callback);
401                 napi_call_function(env, nullptr, callback, sizeof(result) / sizeof(result[0]), result, nullptr);
402                 napi_delete_reference(env, asyncContext->callbackRef);
403             }
404             napi_delete_async_work(env, asyncContext->work);
405             delete asyncContext;
406         },
407         (void*)asyncContext, &asyncContext->work);
408     napi_queue_async_work(env, asyncContext->work);
409 
410     return result;
411 }
412 
413 // storage.delete(key: string, callback?: Function): void | Promise<void>
JSStorageDelete(napi_env env,napi_callback_info info)414 static napi_value JSStorageDelete(napi_env env, napi_callback_info info)
415 {
416     size_t requireArgc = 1;
417     size_t argc = 2;
418     napi_value argv[2] = { 0 };
419     napi_value thisVar = nullptr;
420     void* data = nullptr;
421     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
422 
423     NAPI_ASSERT(env, argc >= requireArgc, "requires 1 parameter");
424 
425     auto asyncContext = new StorageAsyncContext();
426 
427     asyncContext->env = env;
428 
429     for (size_t i = 0; i < argc; i++) {
430         napi_valuetype valueType = napi_undefined;
431         napi_typeof(env, argv[i], &valueType);
432 
433         if (i == 0 && valueType == napi_string) {
434             napi_get_value_string_utf8(env, argv[i], asyncContext->key, KEY_BUFFER_SIZE, &asyncContext->keyLen);
435         } else if (i == 1 && valueType == napi_function) {
436             napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
437         } else {
438             NAPI_ASSERT(env, false, "type mismatch");
439         }
440     }
441 
442     napi_value result = nullptr;
443 
444     if (asyncContext->callbackRef == nullptr) {
445         napi_create_promise(env, &asyncContext->deferred, &result);
446     } else {
447         napi_get_undefined(env, &result);
448     }
449 
450     napi_unwrap(env, thisVar, (void**)&asyncContext->objectInfo);
451 
452     napi_value resource = nullptr;
453     napi_create_string_utf8(env, "JSStorageDelete", NAPI_AUTO_LENGTH, &resource);
454 
455     napi_create_async_work(
456         env, nullptr, resource,
457         [](napi_env env, void* data) {
458             StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
459             auto itr = g_keyValueStorage.find(asyncContext->key);
460             if (itr != g_keyValueStorage.end()) {
461                 g_keyValueStorage.erase(itr);
462                 asyncContext->status = 0;
463             } else {
464                 asyncContext->status = 1;
465             }
466         },
467         [](napi_env env, napi_status status, void* data) {
468             StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
469             napi_value result[2] = { 0 };
470             if (!asyncContext->status) {
471                 napi_get_undefined(env, &result[0]);
472                 napi_get_undefined(env, &result[1]);
473                 asyncContext->objectInfo->Emit(nullptr, "change");
474             } else {
475                 napi_value message = nullptr;
476                 napi_create_string_utf8(env, "key does not exist", NAPI_AUTO_LENGTH, &message);
477                 napi_create_error(env, nullptr, message, &result[0]);
478                 napi_get_undefined(env, &result[1]);
479                 asyncContext->objectInfo->Emit(nullptr, "error");
480             }
481 
482             if (asyncContext->deferred) {
483                 if (!asyncContext->status) {
484                     napi_resolve_deferred(env, asyncContext->deferred, result[1]);
485                 } else {
486                     napi_reject_deferred(env, asyncContext->deferred, result[0]);
487                 }
488             } else {
489                 napi_value callback = nullptr;
490                 napi_get_reference_value(env, asyncContext->callbackRef, &callback);
491                 napi_call_function(env, nullptr, callback, sizeof(result) / sizeof(result[0]), result, nullptr);
492                 napi_delete_reference(env, asyncContext->callbackRef);
493             }
494             napi_delete_async_work(env, asyncContext->work);
495             delete asyncContext;
496         },
497         (void*)asyncContext, &asyncContext->work);
498     napi_queue_async_work(env, asyncContext->work);
499 
500     return result;
501 }
502 
503 // storage.clear(callback?: Function): void | Promise<void>
JSStorageClear(napi_env env,napi_callback_info info)504 static napi_value JSStorageClear(napi_env env, napi_callback_info info)
505 {
506     size_t argc = 1;
507     napi_value argv[1] = { 0 };
508     napi_value thisVar = nullptr;
509     void* data = nullptr;
510     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
511 
512     auto asyncContext = new StorageAsyncContext();
513 
514     asyncContext->env = env;
515 
516     for (size_t i = 0; i < argc; i++) {
517         napi_valuetype valueType = napi_undefined;
518         napi_typeof(env, argv[i], &valueType);
519         if (i == 0 && valueType == napi_function) {
520             napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
521         } else {
522             NAPI_ASSERT(env, false, "type mismatch");
523         }
524     }
525 
526     napi_value result = nullptr;
527 
528     if (asyncContext->callbackRef == nullptr) {
529         napi_create_promise(env, &asyncContext->deferred, &result);
530     } else {
531         napi_get_undefined(env, &result);
532     }
533 
534     napi_unwrap(env, thisVar, (void**)&asyncContext->objectInfo);
535 
536     napi_value resource = nullptr;
537     napi_create_string_utf8(env, "JSStorageClear", NAPI_AUTO_LENGTH, &resource);
538 
539     napi_create_async_work(
540         env, nullptr, resource,
541         [](napi_env env, void* data) {
542             StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
543             g_keyValueStorage.clear();
544             asyncContext->status = 0;
545         },
546         [](napi_env env, napi_status status, void* data) {
547             StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
548             napi_value result[2] = { 0 };
549             if (!asyncContext->status) {
550                 napi_get_undefined(env, &result[0]);
551                 napi_get_undefined(env, &result[1]);
552                 asyncContext->objectInfo->Emit(nullptr, "clear");
553             } else {
554                 napi_value message = nullptr;
555                 napi_create_string_utf8(env, "key does not exist", NAPI_AUTO_LENGTH, &message);
556                 napi_create_error(env, nullptr, message, &result[0]);
557                 napi_get_undefined(env, &result[1]);
558                 asyncContext->objectInfo->Emit(nullptr, "error");
559             }
560 
561             if (asyncContext->deferred) {
562                 if (!asyncContext->status) {
563                     napi_resolve_deferred(env, asyncContext->deferred, result[1]);
564                 } else {
565 
566                     napi_reject_deferred(env, asyncContext->deferred, result[0]);
567                 }
568             } else {
569                 napi_value callback = nullptr;
570                 napi_get_reference_value(env, asyncContext->callbackRef, &callback);
571                 napi_call_function(env, nullptr, callback, sizeof(result) / sizeof(result[0]), result, nullptr);
572                 napi_delete_reference(env, asyncContext->callbackRef);
573             }
574             napi_delete_async_work(env, asyncContext->work);
575             delete asyncContext;
576         },
577         (void*)asyncContext, &asyncContext->work);
578     napi_queue_async_work(env, asyncContext->work);
579 
580     return result;
581 }
582 
583 /***********************************************
584  * Sync Function Set
585  ***********************************************/
586 // storage.getSync(key: string, defaultValue?: string): string
JSStorageGetSync(napi_env env,napi_callback_info info)587 static napi_value JSStorageGetSync(napi_env env, napi_callback_info info)
588 {
589     size_t requireArgc = 1;
590     size_t argc = 2;
591     napi_value argv[2] = { 0 };
592     napi_value thisVar = nullptr;
593     void* data = nullptr;
594     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
595 
596     NAPI_ASSERT(env, argc >= requireArgc, "requires 1 parameter");
597 
598     char key[KEY_BUFFER_SIZE] = { 0 };
599     size_t keyLen = 0;
600     char value[VALUE_BUFFER_SIZE] = { 0 };
601     size_t valueLen = 0;
602     for (size_t i = 0; i < argc; i++) {
603         napi_valuetype valueType = napi_undefined;
604         napi_typeof(env, argv[i], &valueType);
605 
606         if (i == 0 && valueType == napi_string) {
607             napi_get_value_string_utf8(env, argv[i], key, KEY_BUFFER_SIZE, &keyLen);
608         } else if (i == 1 && valueType == napi_string) {
609             napi_get_value_string_utf8(env, argv[i], value, VALUE_BUFFER_SIZE, &valueLen);
610             break;
611         } else {
612             NAPI_ASSERT(env, false, "type mismatch");
613         }
614     }
615     StorageObjectInfo* objectInfo = nullptr;
616     napi_unwrap(env, thisVar, (void**)&objectInfo);
617     auto itr = g_keyValueStorage.find(key);
618     napi_value result = nullptr;
619     if (itr != g_keyValueStorage.end()) {
620         napi_create_string_utf8(env, itr->second.c_str(), itr->second.length(), &result);
621     } else if (valueLen > 0) {
622         napi_create_string_utf8(env, value, valueLen, &result);
623     } else {
624         objectInfo->Emit(nullptr, "error");
625         NAPI_ASSERT(env, false, "key does not exist");
626     }
627     return result;
628 }
629 
630 // storage.setSync(key: string, value: string): void
JSStorageSetSync(napi_env env,napi_callback_info info)631 static napi_value JSStorageSetSync(napi_env env, napi_callback_info info)
632 {
633     size_t requireArgc = 2;
634     size_t argc = 2;
635     napi_value argv[2] = { 0 };
636     napi_value thisVar = nullptr;
637     void* data = nullptr;
638     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
639 
640     NAPI_ASSERT(env, argc >= requireArgc, "requires 2 parameters");
641 
642     char key[KEY_BUFFER_SIZE] = { 0 };
643     size_t keyLen = 0;
644     char value[VALUE_BUFFER_SIZE] = { 0 };
645     size_t valueLen = 0;
646     for (size_t i = 0; i < argc; i++) {
647         napi_valuetype valueType = napi_undefined;
648         napi_typeof(env, argv[i], &valueType);
649 
650         if (i == 0 && valueType == napi_string) {
651             napi_get_value_string_utf8(env, argv[i], key, KEY_BUFFER_SIZE, &keyLen);
652         } else if (i == 1 && valueType == napi_string) {
653             napi_get_value_string_utf8(env, argv[i], value, VALUE_BUFFER_SIZE, &valueLen);
654             break;
655         } else {
656             NAPI_ASSERT(env, false, "type mismatch");
657         }
658     }
659     StorageObjectInfo* objectInfo = nullptr;
660     napi_unwrap(env, thisVar, (void**)&objectInfo);
661     auto itr = g_keyValueStorage.find(key);
662     if (itr == g_keyValueStorage.end()) {
663         g_keyValueStorage.insert(std::pair<std::string, std::string>(key, value));
664         objectInfo->Emit(nullptr, "change");
665 
666     } else {
667         objectInfo->Emit(nullptr, "error");
668         NAPI_ASSERT(env, false, "key already exists");
669     }
670     napi_value result = nullptr;
671     napi_get_undefined(env, &result);
672     return result;
673 }
674 
675 // storage.deleteSync(key: string): void
JSStorageDeleteSync(napi_env env,napi_callback_info info)676 static napi_value JSStorageDeleteSync(napi_env env, napi_callback_info info)
677 {
678     size_t requireArgc = 1;
679     size_t argc = 2;
680     napi_value argv[2] = { 0 };
681     napi_value thisVar = nullptr;
682     void* data = nullptr;
683     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
684 
685     NAPI_ASSERT(env, argc >= requireArgc, "requires 1 parameter");
686 
687     char key[KEY_BUFFER_SIZE] = { 0 };
688     size_t keyLen = 0;
689 
690     napi_valuetype keyType = napi_undefined;
691     napi_typeof(env, argv[0], &keyType);
692     NAPI_ASSERT(env, keyType == napi_string, "type mismatch");
693     napi_get_value_string_utf8(env, argv[0], key, KEY_BUFFER_SIZE, &keyLen);
694 
695     StorageObjectInfo* objectInfo = nullptr;
696     napi_unwrap(env, thisVar, (void**)&objectInfo);
697 
698     auto itr = g_keyValueStorage.find(key);
699 
700     if (itr != g_keyValueStorage.end()) {
701         g_keyValueStorage.erase(itr);
702         objectInfo->Emit(nullptr, "change");
703     } else {
704         objectInfo->Emit(nullptr, "error");
705         NAPI_ASSERT(env, itr != g_keyValueStorage.end(), "key does not exist");
706     }
707 
708     napi_value result = nullptr;
709     napi_get_undefined(env, &result);
710     return result;
711 }
712 
713 // storage.clearSync(): void
JSStorageClearSync(napi_env env,napi_callback_info info)714 static napi_value JSStorageClearSync(napi_env env, napi_callback_info info)
715 {
716     napi_value thisVar = nullptr;
717     void* data = nullptr;
718     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
719 
720     StorageObjectInfo* objectInfo = nullptr;
721     napi_unwrap(env, thisVar, (void**)&objectInfo);
722     g_keyValueStorage.clear();
723     objectInfo->Emit(nullptr, "clear");
724     napi_value result = nullptr;
725     napi_get_undefined(env, &result);
726     return result;
727 }
728 
729 /***********************************************
730  * Event Function Set
731  ***********************************************/
732 // storage.on(event: "change" | "clear", callback: Function): void
JSStorageOn(napi_env env,napi_callback_info info)733 static napi_value JSStorageOn(napi_env env, napi_callback_info info)
734 {
735     size_t requireArgc = 2;
736     size_t argc = 2;
737     napi_value argv[2] = { 0 };
738     napi_value thisVar = nullptr;
739     void* data = nullptr;
740     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
741 
742     NAPI_ASSERT(env, argc >= requireArgc, "requires 2 parameters");
743 
744     char eventType[EVENT_TYPE_SIZE] = { 0 };
745     size_t eventTypeLen = 0;
746     napi_valuetype eventValueType = napi_undefined;
747     napi_typeof(env, argv[0], &eventValueType);
748     NAPI_ASSERT(env, eventValueType == napi_string, "parameter 1 type mismatch");
749     napi_get_value_string_utf8(env, argv[0], eventType, EVENT_TYPE_SIZE, &eventTypeLen);
750 
751     napi_valuetype callbackType = napi_undefined;
752     napi_typeof(env, argv[1], &callbackType);
753     NAPI_ASSERT(env, callbackType == napi_function, "parameter 2 type mismatch");
754 
755     StorageObjectInfo* objectInfo = nullptr;
756     napi_unwrap(env, thisVar, (void**)&objectInfo);
757 
758     objectInfo->On(eventType, argv[1]);
759 
760     napi_value result = nullptr;
761     napi_get_undefined(env, &result);
762     return result;
763 }
764 
765 // storage.off(event: "change" | "clear", callback?: Function): void
JSStorageOff(napi_env env,napi_callback_info info)766 static napi_value JSStorageOff(napi_env env, napi_callback_info info)
767 {
768     size_t requireArgc = 1;
769     size_t argc = 2;
770     napi_value argv[2] = { 0 };
771     napi_value thisVar = nullptr;
772     void* data = nullptr;
773     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
774 
775     NAPI_ASSERT(env, argc >= requireArgc, "requires 1 parameter");
776 
777     char eventType[EVENT_TYPE_SIZE] = { 0 };
778     size_t eventTypeLen = 0;
779     napi_valuetype eventValueType = napi_undefined;
780     napi_typeof(env, argv[0], &eventValueType);
781     NAPI_ASSERT(env, eventValueType == napi_string, "parameter 1 type mismatch");
782     napi_get_value_string_utf8(env, argv[0], eventType, EVENT_TYPE_SIZE, &eventTypeLen);
783 
784     StorageObjectInfo* objectInfo = nullptr;
785     napi_unwrap(env, thisVar, (void**)&objectInfo);
786 
787     if (argc > requireArgc) {
788         napi_valuetype callbackType = napi_undefined;
789         napi_typeof(env, argv[1], &callbackType);
790         NAPI_ASSERT(env, callbackType == napi_function, "parameter 2 type mismatch");
791         objectInfo->Off(eventType, argv[1]);
792     } else {
793         objectInfo->Off(eventType);
794     }
795 
796     napi_value result = nullptr;
797     napi_get_undefined(env, &result);
798     return result;
799 }
800 
801 /***********************************************
802  * Module export and register
803  ***********************************************/
StorageExport(napi_env env,napi_value exports)804 static napi_value StorageExport(napi_env env, napi_value exports)
805 {
806     const char* storageClassName = "Storage";
807     napi_value storageClass = nullptr;
808     static napi_property_descriptor storageDesc[] = {
809         DECLARE_NAPI_FUNCTION("get", JSStorageGet),
810         DECLARE_NAPI_FUNCTION("set", JSStorageSet),
811         DECLARE_NAPI_FUNCTION("delete", JSStorageDelete),
812         DECLARE_NAPI_FUNCTION("clear", JSStorageClear),
813         DECLARE_NAPI_FUNCTION("getSync", JSStorageGetSync),
814         DECLARE_NAPI_FUNCTION("setSync", JSStorageSetSync),
815         DECLARE_NAPI_FUNCTION("deleteSync", JSStorageDeleteSync),
816         DECLARE_NAPI_FUNCTION("clearSync", JSStorageClearSync),
817         DECLARE_NAPI_FUNCTION("on", JSStorageOn),
818         DECLARE_NAPI_FUNCTION("off", JSStorageOff),
819     };
820     napi_define_class(env, storageClassName, strlen(storageClassName), JSStorageConstructor, nullptr,
821                       sizeof(storageDesc) / sizeof(storageDesc[0]), storageDesc, &storageClass);
822 
823     static napi_property_descriptor desc[] = {
824         DECLARE_NAPI_PROPERTY("Storage", storageClass),
825     };
826 
827     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
828     return exports;
829 }
830 
831 // storage module define
832 static napi_module storageModule = {
833     .nm_version = 1,
834     .nm_flags = 0,
835     .nm_filename = nullptr,
836     .nm_register_func = StorageExport,
837     .nm_modname = "storage",
838     .nm_priv = ((void*)0),
839     .reserved = { 0 },
840 };
841 
842 // storage module register
StorageRegister()843 extern "C" __attribute__((constructor)) void StorageRegister()
844 {
845     napi_module_register(&storageModule);
846 }
847