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