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 388 lines 12 kB view raw
1#include <compat.h> // IWYU pragma: keep 2 3#include <stdlib.h> 4#include <stdio.h> 5#include <string.h> 6#include <uthash.h> 7 8#ifdef __APPLE__ 9#include <sys/sysctl.h> 10#elif defined(__linux__) 11#include <sys/sysinfo.h> 12#endif 13 14#include "ant.h" 15#include "errors.h" 16#include "runtime.h" 17#include "internal.h" 18#include "silver/engine.h" 19#include "modules/navigator.h" 20#include "modules/symbol.h" 21#include "gc/modules.h" 22 23typedef enum { 24 LOCK_MODE_EXCLUSIVE, 25 LOCK_MODE_SHARED 26} lock_mode_t; 27 28typedef struct lock_entry { 29 char *name; 30 lock_mode_t mode; 31 int shared_count; 32 UT_hash_handle hh; 33} lock_entry_t; 34 35typedef struct lock_request { 36 ant_t *js; 37 char *name; 38 lock_mode_t mode; 39 ant_value_t callback; 40 ant_value_t promise; 41 struct lock_request *next; 42} lock_request_t; 43 44static lock_entry_t *locks = NULL; 45static lock_request_t *pending_requests = NULL; 46 47static int get_hardware_concurrency(void) { 48#ifdef __APPLE__ 49 int count; 50 size_t size = sizeof(count); 51 if (sysctlbyname("hw.ncpu", &count, &size, NULL, 0) == 0) { 52 return count; 53 } 54 return 1; 55#elif defined(__linux__) 56 int count = (int)sysconf(_SC_NPROCESSORS_ONLN); 57 return count > 0 ? count : 1; 58#elif defined(_WIN32) || defined(_WIN64) 59 SYSTEM_INFO sysinfo; 60 GetSystemInfo(&sysinfo); 61 return (int)sysinfo.dwNumberOfProcessors; 62#else 63 return 1; 64#endif 65} 66 67static const char *get_platform_string(void) { 68#if defined(__APPLE__) 69 return "MacIntel"; 70#elif defined(__linux__) 71 return "Linux x86_64"; 72#elif defined(_WIN32) || defined(_WIN64) 73 return "Win32"; 74#elif defined(__FreeBSD__) 75 return "FreeBSD"; 76#else 77 return "Unknown"; 78#endif 79} 80 81static lock_entry_t *find_lock(const char *name) { 82 lock_entry_t *entry = NULL; 83 HASH_FIND_STR(locks, name, entry); 84 return entry; 85} 86 87static lock_entry_t *create_lock(const char *name, lock_mode_t mode) { 88 lock_entry_t *entry = calloc(1, sizeof(lock_entry_t)); 89 if (!entry) return NULL; 90 91 entry->name = strdup(name); 92 entry->mode = mode; 93 entry->shared_count = (mode == LOCK_MODE_SHARED) ? 1 : 0; 94 95 HASH_ADD_STR(locks, name, entry); 96 return entry; 97} 98 99static bool can_acquire_lock(const char *name, lock_mode_t mode) { 100 lock_entry_t *entry = find_lock(name); 101 if (!entry) return true; 102 if (mode == LOCK_MODE_SHARED && entry->mode == LOCK_MODE_SHARED) return true; 103 return false; 104} 105 106static void release_lock(const char *name) { 107 lock_entry_t *entry = find_lock(name); 108 if (!entry) return; 109 110 if (entry->mode == LOCK_MODE_SHARED) { 111 entry->shared_count--; 112 if (entry->shared_count > 0) return; 113 } 114 115 HASH_DEL(locks, entry); 116 free(entry->name); 117 free(entry); 118} 119 120static void process_pending_requests(ant_t *js); 121 122static ant_value_t make_lock_handler(ant_t *js, ant_value_t cfunc, ant_value_t lock_name, ant_value_t outer_promise) { 123 ant_value_t fn_obj = js_mkobj(js); 124 125 ant_value_t data_obj = js_mkobj(js); 126 js_set(js, data_obj, "lockName", lock_name); 127 js_set(js, data_obj, "outerPromise", outer_promise); 128 js_set_slot(fn_obj, SLOT_DATA, data_obj); 129 js_set_slot(fn_obj, SLOT_CFUNC, cfunc); 130 131 return js_obj_to_func(fn_obj); 132} 133 134static ant_value_t lock_then_handler(ant_t *js, ant_value_t *args, int nargs) { 135 ant_value_t current_func = js_getcurrentfunc(js); 136 ant_value_t data_obj = js_get_slot(current_func, SLOT_DATA); 137 138 ant_value_t lock_name_val = js_get(js, data_obj, "lockName"); 139 ant_value_t outer_promise = js_get(js, data_obj, "outerPromise"); 140 ant_value_t result_val = (nargs > 0) ? args[0] : js_mkundef(); 141 142 size_t name_len; 143 char *name = js_getstr(js, lock_name_val, &name_len); 144 145 if (name) release_lock(name); 146 js_resolve_promise(js, outer_promise, result_val); 147 process_pending_requests(js); 148 149 return js_mkundef(); 150} 151 152static ant_value_t lock_catch_handler(ant_t *js, ant_value_t *args, int nargs) { 153 ant_value_t current_func = js_getcurrentfunc(js); 154 ant_value_t data_obj = js_get_slot(current_func, SLOT_DATA); 155 156 ant_value_t lock_name_val = js_get(js, data_obj, "lockName"); 157 ant_value_t outer_promise = js_get(js, data_obj, "outerPromise"); 158 ant_value_t error_val = (nargs > 0) ? args[0] : js_mkundef(); 159 160 size_t name_len; 161 char *name = js_getstr(js, lock_name_val, &name_len); 162 163 if (name) release_lock(name); 164 165 js_reject_promise(js, outer_promise, error_val); 166 process_pending_requests(js); 167 168 return js_mkundef(); 169} 170 171static void execute_lock_callback(ant_t *js, const char *name, lock_mode_t mode, ant_value_t callback, ant_value_t outer_promise) { 172 ant_value_t lock_obj = js_mkobj(js); 173 js_set(js, lock_obj, "name", js_mkstr(js, name, strlen(name))); 174 js_set(js, lock_obj, "mode", js_mkstr(js, mode == LOCK_MODE_EXCLUSIVE ? "exclusive" : "shared", mode == LOCK_MODE_EXCLUSIVE ? 9 : 6)); 175 js_set_sym(js, lock_obj, get_toStringTag_sym(), js_mkstr(js, "Lock", 4)); 176 177 ant_value_t result = sv_vm_call(js->vm, js, callback, js_mkundef(), &lock_obj, 1, NULL, false); 178 179 if (vtype(result) == T_ERR) { 180 release_lock(name); 181 js_reject_promise(js, outer_promise, result); 182 process_pending_requests(js); 183 return; 184 } 185 186 if (vtype(result) == T_PROMISE) { 187 ant_value_t name_str = js_mkstr(js, name, strlen(name)); 188 ant_value_t on_resolve = make_lock_handler(js, js_mkfun(lock_then_handler), name_str, outer_promise); 189 ant_value_t on_reject = make_lock_handler(js, js_mkfun(lock_catch_handler), name_str, outer_promise); 190 js_promise_then(js, result, on_resolve, on_reject); 191 return; 192 } 193 194 release_lock(name); 195 js_resolve_promise(js, outer_promise, result); 196 process_pending_requests(js); 197} 198 199static void process_pending_requests(ant_t *js) { 200 lock_request_t *prev = NULL; 201 lock_request_t *req = pending_requests; 202 203 while (req) { 204 if (can_acquire_lock(req->name, req->mode)) { 205 lock_entry_t *entry = find_lock(req->name); 206 207 if (entry && entry->mode == LOCK_MODE_SHARED && req->mode == LOCK_MODE_SHARED) { 208 entry->shared_count++; 209 } else { 210 create_lock(req->name, req->mode); 211 } 212 213 lock_request_t *to_process = req; 214 215 if (prev) { 216 prev->next = req->next; 217 } else { 218 pending_requests = req->next; 219 } 220 req = req->next; 221 222 execute_lock_callback(js, to_process->name, to_process->mode, to_process->callback, to_process->promise); 223 free(to_process->name); 224 free(to_process); 225 return; 226 } else { 227 prev = req; 228 req = req->next; 229 } 230 } 231} 232 233static ant_value_t locks_request(ant_t *js, ant_value_t *args, int nargs) { 234 if (nargs < 2) { 235 return js_mkerr_typed(js, JS_ERR_TYPE, "locks.request requires at least 2 arguments"); 236 } 237 238 size_t name_len; 239 char *name = js_getstr(js, args[0], &name_len); 240 if (!name) { 241 return js_mkerr_typed(js, JS_ERR_TYPE, "First argument must be a string"); 242 } 243 244 ant_value_t options = js_mkundef(); 245 ant_value_t callback; 246 lock_mode_t mode = LOCK_MODE_EXCLUSIVE; 247 bool if_available = false; 248 249 if (nargs == 2) { 250 callback = args[1]; 251 } else { 252 options = args[1]; 253 callback = args[2]; 254 255 if (is_special_object(options)) { 256 ant_value_t mode_val = js_get(js, options, "mode"); 257 if (vtype(mode_val) == T_STR) { 258 size_t mode_len; 259 char *mode_str = js_getstr(js, mode_val, &mode_len); 260 if (mode_str && strcmp(mode_str, "shared") == 0) mode = LOCK_MODE_SHARED; 261 } 262 263 if (js_get(js, options, "ifAvailable") == js_true) if_available = true; 264 } 265 } 266 267 if (vtype(callback) != T_FUNC) { 268 return js_mkerr_typed(js, JS_ERR_TYPE, "Callback must be a function"); 269 } 270 271 ant_value_t promise = js_mkpromise(js); 272 273 if (if_available && !can_acquire_lock(name, mode)) { 274 ant_value_t null_val = js_mknull(); 275 ant_value_t result = sv_vm_call(js->vm, js, callback, js_mkundef(), &null_val, 1, NULL, false); 276 277 if (vtype(result) == T_PROMISE) { 278 ant_value_t on_resolve = make_lock_handler( 279 js, js_mkfun(lock_then_handler), 280 js_mkstr(js, "", 0), promise 281 ); 282 js_promise_then(js, result, on_resolve, js_mkundef()); 283 return promise; 284 } 285 js_resolve_promise(js, promise, result); 286 return promise; 287 } 288 289 if (can_acquire_lock(name, mode)) { 290 lock_entry_t *entry = find_lock(name); 291 292 if (entry && entry->mode == LOCK_MODE_SHARED && mode == LOCK_MODE_SHARED) { 293 entry->shared_count++; 294 } else { 295 create_lock(name, mode); 296 } 297 298 execute_lock_callback(js, name, mode, callback, promise); 299 } else { 300 lock_request_t *req = calloc(1, sizeof(lock_request_t)); 301 req->js = js; 302 req->name = strdup(name); 303 req->mode = mode; 304 req->callback = callback; 305 req->promise = promise; 306 req->next = NULL; 307 308 lock_request_t *tail = pending_requests; 309 if (!tail) { 310 pending_requests = req; 311 } else { 312 while (tail->next) tail = tail->next; 313 tail->next = req; 314 } 315 } 316 317 return promise; 318} 319 320static ant_value_t locks_query(ant_t *js, ant_value_t *args, int nargs) { 321 (void)args; 322 (void)nargs; 323 324 ant_value_t result = js_mkobj(js); 325 ant_value_t held_arr = js_mkarr(js); 326 ant_value_t pending_arr = js_mkarr(js); 327 328 lock_entry_t *entry, *tmp; 329 HASH_ITER(hh, locks, entry, tmp) { 330 ant_value_t lock_info = js_mkobj(js); 331 js_set(js, lock_info, "name", js_mkstr(js, entry->name, strlen(entry->name))); 332 js_set(js, lock_info, "mode", js_mkstr(js, entry->mode == LOCK_MODE_EXCLUSIVE ? "exclusive" : "shared", entry->mode == LOCK_MODE_EXCLUSIVE ? 9 : 6)); 333 js_arr_push(js, held_arr, lock_info); 334 } 335 336 lock_request_t *req = pending_requests; 337 while (req) { 338 ant_value_t req_info = js_mkobj(js); 339 js_set(js, req_info, "name", js_mkstr(js, req->name, strlen(req->name))); 340 js_set(js, req_info, "mode", js_mkstr(js, req->mode == LOCK_MODE_EXCLUSIVE ? "exclusive" : "shared", req->mode == LOCK_MODE_EXCLUSIVE ? 9 : 6)); 341 js_arr_push(js, pending_arr, req_info); 342 req = req->next; 343 } 344 345 js_set(js, result, "held", held_arr); 346 js_set(js, result, "pending", pending_arr); 347 348 ant_value_t promise = js_mkpromise(js); 349 js_resolve_promise(js, promise, result); 350 351 return promise; 352} 353 354void init_navigator_module(void) { 355 ant_t *js = rt->js; 356 357 ant_value_t navigator_obj = js_mkobj(js); 358 359 js_set(js, navigator_obj, "hardwareConcurrency", js_mknum((double)get_hardware_concurrency())); 360 js_set(js, navigator_obj, "language", js_mkstr(js, "en-US", 5)); 361 362 ant_value_t languages_arr = js_mkarr(js); 363 js_arr_push(js, languages_arr, js_mkstr(js, "en-US", 5)); 364 js_set(js, navigator_obj, "languages", languages_arr); 365 366 const char *platform = get_platform_string(); 367 js_set(js, navigator_obj, "platform", js_mkstr(js, platform, strlen(platform))); 368 369 char user_agent[64]; 370 snprintf(user_agent, sizeof(user_agent), "Ant/%s", ANT_VERSION); 371 js_set(js, navigator_obj, "userAgent", js_mkstr(js, user_agent, strlen(user_agent))); 372 373 ant_value_t locks_obj = js_mkobj(js); 374 js_set(js, locks_obj, "request", js_mkfun(locks_request)); 375 js_set(js, locks_obj, "query", js_mkfun(locks_query)); 376 js_set_sym(js, locks_obj, get_toStringTag_sym(), js_mkstr(js, "LockManager", 11)); 377 js_set(js, navigator_obj, "locks", locks_obj); 378 379 js_set_sym(js, navigator_obj, get_toStringTag_sym(), js_mkstr(js, "Navigator", 9)); 380 js_set(js, js_glob(js), "navigator", navigator_obj); 381} 382 383void gc_mark_navigator(ant_t *js, gc_mark_fn mark) { 384 for (lock_request_t *req = pending_requests; req; req = req->next) { 385 mark(js, req->callback); 386 mark(js, req->promise); 387 } 388}