MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 299 lines 7.9 kB view raw
1#include <stdlib.h> 2#include <string.h> 3#include <stdio.h> 4#include <uthash.h> 5#include <yyjson.h> 6 7#include "ant.h" 8#include "errors.h" 9#include "runtime.h" 10#include "internal.h" 11#include "descriptors.h" 12 13#include "modules/symbol.h" 14#include "modules/localstorage.h" 15 16typedef struct storage_entry { 17 char *key; 18 char *value; 19 UT_hash_handle hh; 20} storage_entry_t; 21 22static storage_entry_t *local_storage = NULL; 23static char *storage_file_path = NULL; 24 25static void storage_save(void) { 26 if (!storage_file_path) return; 27 28 yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL); 29 if (!doc) return; 30 31 yyjson_mut_val *root = yyjson_mut_obj(doc); 32 yyjson_mut_doc_set_root(doc, root); 33 34 storage_entry_t *entry, *tmp; 35 HASH_ITER(hh, local_storage, entry, tmp) { 36 yyjson_mut_obj_add_str(doc, root, entry->key, entry->value); 37 } 38 39 yyjson_write_err err; 40 yyjson_mut_write_file(storage_file_path, doc, YYJSON_WRITE_PRETTY, NULL, &err); 41 yyjson_mut_doc_free(doc); 42} 43 44static void storage_load(void) { 45 if (!storage_file_path) return; 46 47 yyjson_read_err err; 48 yyjson_doc *doc = yyjson_read_file(storage_file_path, 0, NULL, &err); 49 if (!doc) return; 50 51 yyjson_val *root = yyjson_doc_get_root(doc); 52 if (!yyjson_is_obj(root)) { 53 yyjson_doc_free(doc); 54 return; 55 } 56 57 yyjson_obj_iter iter; 58 yyjson_obj_iter_init(root, &iter); 59 yyjson_val *key, *val; 60 61 while ((key = yyjson_obj_iter_next(&iter))) { 62 val = yyjson_obj_iter_get_val(key); 63 if (yyjson_is_str(key) && yyjson_is_str(val)) { 64 const char *k = yyjson_get_str(key); 65 const char *v = yyjson_get_str(val); 66 size_t klen = yyjson_get_len(key); 67 size_t vlen = yyjson_get_len(val); 68 69 storage_entry_t *entry = ant_calloc(sizeof(storage_entry_t) + klen + 1 + vlen + 1); 70 if (!entry) continue; 71 72 entry->key = (char *)(entry + 1); 73 entry->value = entry->key + klen + 1; 74 75 memcpy(entry->key, k, klen); 76 entry->key[klen] = '\0'; 77 memcpy(entry->value, v, vlen); 78 entry->value[vlen] = '\0'; 79 80 HASH_ADD_KEYPTR(hh, local_storage, entry->key, klen, entry); 81 } 82 } 83 84 yyjson_doc_free(doc); 85} 86 87static void storage_clear(void) { 88 storage_entry_t *entry, *tmp; 89 HASH_ITER(hh, local_storage, entry, tmp) { 90 HASH_DEL(local_storage, entry); 91 free(entry); 92 } 93} 94 95static void storage_set_item(const char *key, size_t key_len, const char *value, size_t value_len) { 96 storage_entry_t *entry = NULL; 97 98 HASH_FIND(hh, local_storage, key, key_len, entry); 99 100 if (entry) { 101 HASH_DEL(local_storage, entry); 102 free(entry); 103 } 104 105 entry = ant_calloc(sizeof(storage_entry_t) + key_len + 1 + value_len + 1); 106 if (!entry) return; 107 108 entry->key = (char *)(entry + 1); 109 entry->value = entry->key + key_len + 1; 110 111 memcpy(entry->key, key, key_len); 112 entry->key[key_len] = '\0'; 113 memcpy(entry->value, value, value_len); 114 entry->value[value_len] = '\0'; 115 116 HASH_ADD_KEYPTR(hh, local_storage, entry->key, key_len, entry); 117 118 storage_save(); 119} 120 121static char *storage_get_item(const char *key, size_t key_len) { 122 storage_entry_t *entry = NULL; 123 HASH_FIND(hh, local_storage, key, key_len, entry); 124 return entry ? entry->value : NULL; 125} 126 127static void storage_remove_item(const char *key, size_t key_len) { 128 storage_entry_t *entry = NULL; 129 HASH_FIND(hh, local_storage, key, key_len, entry); 130 131 if (entry) { 132 HASH_DEL(local_storage, entry); 133 free(entry); 134 storage_save(); 135 } 136} 137 138static size_t storage_length(void) { 139 return HASH_COUNT(local_storage); 140} 141 142static char *storage_key(size_t index) { 143 storage_entry_t *entry; 144 size_t i = 0; 145 146 for (entry = local_storage; entry != NULL; entry = entry->hh.next) { 147 if (i == index) return entry->key; 148 i++; 149 } 150 151 return NULL; 152} 153 154#define CHECK_FILE_SET(js) \ 155 if (!storage_file_path) { \ 156 return js_mkerr(js, "Warning: `--localstorage-file` or `localStorage.setFile` were not provided with valid paths."); \ 157 } 158 159// localStorage.setItem(key, value) 160static ant_value_t js_localstorage_setItem(ant_t *js, ant_value_t *args, int nargs) { 161 CHECK_FILE_SET(js); 162 163 if (nargs < 2) { 164 return js_mkerr(js, "Failed to execute 'setItem' on 'Storage': 2 arguments required"); 165 } 166 167 size_t key_len, value_len; 168 char *key = js_getstr(js, args[0], &key_len); 169 char *value = js_getstr(js, js_tostring_val(js, args[1]), &value_len); 170 171 storage_set_item(key, key_len, value, value_len); 172 173 return js_mkundef(); 174} 175 176// localStorage.getItem(key) 177static ant_value_t js_localstorage_getItem(ant_t *js, ant_value_t *args, int nargs) { 178 CHECK_FILE_SET(js); 179 180 if (nargs < 1) { 181 return js_mkerr(js, "Failed to execute 'getItem' on 'Storage': 1 argument required"); 182 } 183 184 size_t key_len; 185 char *key = js_getstr(js, args[0], &key_len); 186 char *value = storage_get_item(key, key_len); 187 188 if (!value) return js_mknull(); 189 190 return js_mkstr(js, value, strlen(value)); 191} 192 193// localStorage.removeItem(key) 194static ant_value_t js_localstorage_removeItem(ant_t *js, ant_value_t *args, int nargs) { 195 CHECK_FILE_SET(js); 196 197 if (nargs < 1) { 198 return js_mkerr(js, "Failed to execute 'removeItem' on 'Storage': 1 argument required"); 199 } 200 201 size_t key_len; 202 char *key = js_getstr(js, args[0], &key_len); 203 storage_remove_item(key, key_len); 204 205 return js_mkundef(); 206} 207 208// localStorage.clear() 209static ant_value_t js_localstorage_clear(ant_t *js, ant_value_t *args, int nargs) { 210 CHECK_FILE_SET(js); 211 (void)args; (void)nargs; 212 storage_clear(); 213 storage_save(); 214 return js_mkundef(); 215} 216 217// localStorage.key(index) 218static ant_value_t js_localstorage_key(ant_t *js, ant_value_t *args, int nargs) { 219 CHECK_FILE_SET(js); 220 221 if (nargs < 1) { 222 return js_mkerr(js, "Failed to execute 'key' on 'Storage': 1 argument required"); 223 } 224 225 if (vtype(args[0]) != T_NUM) { 226 return js_mknull(); 227 } 228 229 double idx = js_getnum(args[0]); 230 if (idx < 0) return js_mknull(); 231 232 size_t index = (size_t)idx; 233 char *key = storage_key(index); 234 235 if (!key) return js_mknull(); 236 237 return js_mkstr(js, key, strlen(key)); 238} 239 240// localStorage.length 241static ant_value_t js_localstorage_length(ant_t *js, ant_value_t *args, int nargs) { 242 (void)args; (void)nargs; 243 CHECK_FILE_SET(js) 244 return js_mknum((double)storage_length()); 245} 246 247// localStorage.setFile(path) 248static ant_value_t js_localstorage_setFile(ant_t *js, ant_value_t *args, int nargs) { 249 if (nargs < 1) { 250 return js_mkerr(js, "Failed to execute 'setFile' on 'Storage': 1 argument required"); 251 } 252 253 size_t path_len; 254 char *path = js_getstr(js, args[0], &path_len); 255 256 if (storage_file_path) { 257 free(storage_file_path); 258 } 259 260 storage_file_path = malloc(path_len + 1); 261 if (!storage_file_path) { 262 return js_mkerr(js, "Failed to allocate memory for file path"); 263 } 264 265 memcpy(storage_file_path, path, path_len); 266 storage_file_path[path_len] = '\0'; 267 268 // Load existing data from the new file 269 storage_load(); 270 271 return js_mkundef(); 272} 273 274void init_localstorage_module() { 275 ant_t *js = rt->js; 276 277 ant_value_t glob = js_glob(js); 278 const char *file_path = rt->ls_fp; 279 280 if (file_path) { 281 storage_file_path = strdup(file_path); 282 storage_load(); 283 } 284 285 ant_value_t storage_obj = js_mkobj(js); 286 287 js_set(js, storage_obj, "setItem", js_mkfun(js_localstorage_setItem)); 288 js_set(js, storage_obj, "getItem", js_mkfun(js_localstorage_getItem)); 289 js_set(js, storage_obj, "removeItem", js_mkfun(js_localstorage_removeItem)); 290 js_set(js, storage_obj, "clear", js_mkfun(js_localstorage_clear)); 291 js_set(js, storage_obj, "key", js_mkfun(js_localstorage_key)); 292 js_set(js, storage_obj, "setFile", js_mkfun(js_localstorage_setFile)); 293 294 ant_value_t length_getter = js_mkfun(js_localstorage_length); 295 js_set_getter_desc(js, storage_obj, "length", 6, length_getter, JS_DESC_E); 296 297 js_set_sym(js, storage_obj, get_toStringTag_sym(), js_mkstr(js, "Storage", 7)); 298 js_set(js, glob, "localStorage", storage_obj); 299}