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.

add navigator module and tests

+482 -1
+96
examples/spec/navigator.js
··· 1 + import { test, testDeep, summary } from './helpers.js'; 2 + 3 + console.log('Navigator Tests\n'); 4 + 5 + test('navigator exists', typeof navigator, 'object'); 6 + test('navigator toStringTag', Object.prototype.toString.call(navigator), '[object Navigator]'); 7 + 8 + test('navigator.hardwareConcurrency exists', typeof navigator.hardwareConcurrency, 'number'); 9 + test('navigator.hardwareConcurrency is positive', navigator.hardwareConcurrency >= 1, true); 10 + test('navigator.hardwareConcurrency is integer', Number.isInteger(navigator.hardwareConcurrency), true); 11 + 12 + test('navigator.language exists', typeof navigator.language, 'string'); 13 + test('navigator.language is en-US', navigator.language, 'en-US'); 14 + 15 + test('navigator.languages exists', Array.isArray(navigator.languages), true); 16 + test('navigator.languages length', navigator.languages.length >= 1, true); 17 + test('navigator.languages contains language', navigator.languages.includes(navigator.language), true); 18 + 19 + test('navigator.platform exists', typeof navigator.platform, 'string'); 20 + test('navigator.platform is non-empty', navigator.platform.length > 0, true); 21 + 22 + test('navigator.userAgent exists', typeof navigator.userAgent, 'string'); 23 + test('navigator.userAgent starts with Ant', navigator.userAgent.startsWith('Ant/'), true); 24 + 25 + console.log('\nLockManager Tests\n'); 26 + 27 + test('navigator.locks exists', typeof navigator.locks, 'object'); 28 + test('navigator.locks toStringTag', Object.prototype.toString.call(navigator.locks), '[object LockManager]'); 29 + test('navigator.locks.request exists', typeof navigator.locks.request, 'function'); 30 + test('navigator.locks.query exists', typeof navigator.locks.query, 'function'); 31 + 32 + (async () => { 33 + let exclusiveLockAcquired = false; 34 + let exclusiveLockName = null; 35 + let exclusiveLockMode = null; 36 + let exclusiveLockReleased = false; 37 + 38 + await navigator.locks 39 + .request('test_resource', async lock => { 40 + exclusiveLockAcquired = true; 41 + exclusiveLockName = lock.name; 42 + exclusiveLockMode = lock.mode; 43 + return 'done'; 44 + }) 45 + .then(() => { 46 + exclusiveLockReleased = true; 47 + }); 48 + 49 + test('exclusive lock was acquired', exclusiveLockAcquired, true); 50 + test('exclusive lock has correct name', exclusiveLockName, 'test_resource'); 51 + test('exclusive lock has correct mode', exclusiveLockMode, 'exclusive'); 52 + test('exclusive lock was released', exclusiveLockReleased, true); 53 + 54 + let sharedLockMode = null; 55 + 56 + await navigator.locks.request('shared_resource', { mode: 'shared' }, async lock => { 57 + sharedLockMode = lock.mode; 58 + }); 59 + 60 + test('shared lock has correct mode', sharedLockMode, 'shared'); 61 + 62 + let ifAvailableResult = null; 63 + 64 + await navigator.locks.request('available_resource', { ifAvailable: true }, async lock => { 65 + ifAvailableResult = lock ? 'acquired' : 'not available'; 66 + }); 67 + 68 + test('ifAvailable lock was acquired', ifAvailableResult, 'acquired'); 69 + 70 + const queryResult = await navigator.locks.query(); 71 + 72 + test('query returns object', typeof queryResult, 'object'); 73 + test('query has held array', Array.isArray(queryResult.held), true); 74 + test('query has pending array', Array.isArray(queryResult.pending), true); 75 + 76 + let lockOrder = []; 77 + 78 + await navigator.locks.request('order_test', async lock => { 79 + lockOrder.push(1); 80 + }); 81 + 82 + await navigator.locks.request('order_test', async lock => { 83 + lockOrder.push(2); 84 + }); 85 + 86 + testDeep('locks execute in order', lockOrder, [1, 2]); 87 + 88 + let lockToStringTag = null; 89 + await navigator.locks.request('tostring_test', async lock => { 90 + lockToStringTag = Object.prototype.toString.call(lock); 91 + }); 92 + 93 + test('Lock toStringTag', lockToStringTag, '[object Lock]'); 94 + 95 + summary(); 96 + })();
+6
include/modules/navigator.h
··· 1 + #ifndef NAVIGATOR_H 2 + #define NAVIGATOR_H 3 + 4 + void init_navigator_module(void); 5 + 6 + #endif
+1 -1
meson.build
··· 96 96 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 97 97 98 98 version_conf = configuration_data() 99 - version_conf.set('ANT_VERSION', '0.3.2.24') 99 + version_conf.set('ANT_VERSION', '0.3.2.25') 100 100 version_conf.set('ANT_GIT_HASH', git_hash) 101 101 version_conf.set('ANT_BUILD_DATE', build_date) 102 102
+2
src/main.c
··· 38 38 #include "modules/textcodec.h" 39 39 #include "modules/sessionstorage.h" 40 40 #include "modules/localstorage.h" 41 + #include "modules/navigator.h" 41 42 42 43 int js_result = EXIT_SUCCESS; 43 44 ··· 210 211 init_textcodec_module(); 211 212 init_sessionstorage_module(); 212 213 init_localstorage_module(); 214 + init_navigator_module(); 213 215 214 216 ant_register_library(shell_library, "ant:shell", NULL); 215 217 ant_register_library(ffi_library, "ant:ffi", NULL);
+377
src/modules/navigator.c
··· 1 + #include <stdlib.h> 2 + #include <stdio.h> 3 + #include <string.h> 4 + #include <unistd.h> 5 + #include <uthash.h> 6 + 7 + #ifdef __APPLE__ 8 + #include <sys/sysctl.h> 9 + #elif defined(__linux__) 10 + #include <sys/sysinfo.h> 11 + #endif 12 + 13 + #include "ant.h" 14 + #include "config.h" 15 + #include "runtime.h" 16 + #include "modules/navigator.h" 17 + #include "modules/symbol.h" 18 + 19 + typedef enum { 20 + LOCK_MODE_EXCLUSIVE, 21 + LOCK_MODE_SHARED 22 + } lock_mode_t; 23 + 24 + typedef struct lock_entry { 25 + char *name; 26 + lock_mode_t mode; 27 + int shared_count; 28 + UT_hash_handle hh; 29 + } lock_entry_t; 30 + 31 + typedef struct lock_request { 32 + struct js *js; 33 + char *name; 34 + lock_mode_t mode; 35 + jsval_t callback; 36 + jsval_t promise; 37 + struct lock_request *next; 38 + } lock_request_t; 39 + 40 + static lock_entry_t *locks = NULL; 41 + static lock_request_t *pending_requests = NULL; 42 + 43 + static int get_hardware_concurrency(void) { 44 + #ifdef __APPLE__ 45 + int count; 46 + size_t size = sizeof(count); 47 + if (sysctlbyname("hw.ncpu", &count, &size, NULL, 0) == 0) { 48 + return count; 49 + } 50 + return 1; 51 + #elif defined(__linux__) 52 + int count = (int)sysconf(_SC_NPROCESSORS_ONLN); 53 + return count > 0 ? count : 1; 54 + #elif defined(_WIN32) || defined(_WIN64) 55 + SYSTEM_INFO sysinfo; 56 + GetSystemInfo(&sysinfo); 57 + return (int)sysinfo.dwNumberOfProcessors; 58 + #else 59 + return 1; 60 + #endif 61 + } 62 + 63 + static const char *get_platform_string(void) { 64 + #if defined(__APPLE__) 65 + return "MacIntel"; 66 + #elif defined(__linux__) 67 + return "Linux x86_64"; 68 + #elif defined(_WIN32) || defined(_WIN64) 69 + return "Win32"; 70 + #elif defined(__FreeBSD__) 71 + return "FreeBSD"; 72 + #else 73 + return "Unknown"; 74 + #endif 75 + } 76 + 77 + static lock_entry_t *find_lock(const char *name) { 78 + lock_entry_t *entry = NULL; 79 + HASH_FIND_STR(locks, name, entry); 80 + return entry; 81 + } 82 + 83 + static lock_entry_t *create_lock(const char *name, lock_mode_t mode) { 84 + lock_entry_t *entry = calloc(1, sizeof(lock_entry_t)); 85 + if (!entry) return NULL; 86 + 87 + entry->name = strdup(name); 88 + entry->mode = mode; 89 + entry->shared_count = (mode == LOCK_MODE_SHARED) ? 1 : 0; 90 + 91 + HASH_ADD_STR(locks, name, entry); 92 + return entry; 93 + } 94 + 95 + static bool can_acquire_lock(const char *name, lock_mode_t mode) { 96 + lock_entry_t *entry = find_lock(name); 97 + if (!entry) return true; 98 + if (mode == LOCK_MODE_SHARED && entry->mode == LOCK_MODE_SHARED) return true; 99 + return false; 100 + } 101 + 102 + static void release_lock(const char *name) { 103 + lock_entry_t *entry = find_lock(name); 104 + if (!entry) return; 105 + 106 + if (entry->mode == LOCK_MODE_SHARED) { 107 + entry->shared_count--; 108 + if (entry->shared_count > 0) return; 109 + } 110 + 111 + HASH_DEL(locks, entry); 112 + free(entry->name); 113 + free(entry); 114 + } 115 + 116 + static void process_pending_requests(struct js *js); 117 + 118 + static jsval_t lock_then_handler(struct js *js, jsval_t *args, int nargs) { 119 + jsval_t current_func = js_getcurrentfunc(js); 120 + jsval_t lock_name_val = js_get(js, current_func, "_lockName"); 121 + jsval_t outer_promise = js_get(js, current_func, "_outerPromise"); 122 + jsval_t result_val = (nargs > 0) ? args[0] : js_mkundef(); 123 + 124 + size_t name_len; 125 + char *name = js_getstr(js, lock_name_val, &name_len); 126 + 127 + if (name) release_lock(name); 128 + 129 + js_resolve_promise(js, outer_promise, result_val); 130 + process_pending_requests(js); 131 + 132 + return js_mkundef(); 133 + } 134 + 135 + static jsval_t lock_catch_handler(struct js *js, jsval_t *args, int nargs) { 136 + jsval_t current_func = js_getcurrentfunc(js); 137 + jsval_t lock_name_val = js_get(js, current_func, "_lockName"); 138 + jsval_t outer_promise = js_get(js, current_func, "_outerPromise"); 139 + jsval_t error_val = (nargs > 0) ? args[0] : js_mkundef(); 140 + 141 + size_t name_len; 142 + char *name = js_getstr(js, lock_name_val, &name_len); 143 + 144 + if (name) release_lock(name); 145 + 146 + js_reject_promise(js, outer_promise, error_val); 147 + process_pending_requests(js); 148 + 149 + return js_mkundef(); 150 + } 151 + 152 + static void execute_lock_callback(struct js *js, const char *name, lock_mode_t mode, jsval_t callback, jsval_t outer_promise) { 153 + jsval_t lock_obj = js_mkobj(js); 154 + js_set(js, lock_obj, "name", js_mkstr(js, name, strlen(name))); 155 + js_set(js, lock_obj, "mode", js_mkstr(js, mode == LOCK_MODE_EXCLUSIVE ? "exclusive" : "shared", mode == LOCK_MODE_EXCLUSIVE ? 9 : 6)); 156 + js_set(js, lock_obj, get_toStringTag_sym_key(), js_mkstr(js, "Lock", 4)); 157 + 158 + jsval_t result = js_call(js, callback, &lock_obj, 1); 159 + 160 + if (js_type(result) == JS_ERR) { 161 + release_lock(name); 162 + js_reject_promise(js, outer_promise, result); 163 + process_pending_requests(js); 164 + return; 165 + } 166 + 167 + if (js_type(result) == JS_PROMISE) { 168 + jsval_t then_fn = js_get(js, result, "then"); 169 + 170 + if (js_type(then_fn) == JS_FUNC) { 171 + jsval_t name_str = js_mkstr(js, name, strlen(name)); 172 + 173 + jsval_t on_resolve = js_mkfun(lock_then_handler); 174 + js_set(js, on_resolve, "_lockName", name_str); 175 + js_set(js, on_resolve, "_outerPromise", outer_promise); 176 + 177 + jsval_t on_reject = js_mkfun(lock_catch_handler); 178 + js_set(js, on_reject, "_lockName", name_str); 179 + js_set(js, on_reject, "_outerPromise", outer_promise); 180 + 181 + jsval_t then_args[2] = { on_resolve, on_reject }; 182 + js_call_with_this(js, then_fn, result, then_args, 2); 183 + return; 184 + } 185 + } 186 + 187 + release_lock(name); 188 + js_resolve_promise(js, outer_promise, result); 189 + process_pending_requests(js); 190 + } 191 + 192 + static void process_pending_requests(struct js *js) { 193 + lock_request_t *prev = NULL; 194 + lock_request_t *req = pending_requests; 195 + 196 + while (req) { 197 + if (can_acquire_lock(req->name, req->mode)) { 198 + lock_entry_t *entry = find_lock(req->name); 199 + 200 + if (entry && entry->mode == LOCK_MODE_SHARED && req->mode == LOCK_MODE_SHARED) { 201 + entry->shared_count++; 202 + } else { 203 + create_lock(req->name, req->mode); 204 + } 205 + 206 + lock_request_t *to_process = req; 207 + 208 + if (prev) { 209 + prev->next = req->next; 210 + } else { 211 + pending_requests = req->next; 212 + } 213 + req = req->next; 214 + 215 + execute_lock_callback(js, to_process->name, to_process->mode, to_process->callback, to_process->promise); 216 + free(to_process->name); 217 + free(to_process); 218 + return; 219 + } else { 220 + prev = req; 221 + req = req->next; 222 + } 223 + } 224 + } 225 + 226 + static jsval_t locks_request(struct js *js, jsval_t *args, int nargs) { 227 + if (nargs < 2) { 228 + return js_mkerr_typed(js, JS_ERR_TYPE, "locks.request requires at least 2 arguments"); 229 + } 230 + 231 + size_t name_len; 232 + char *name = js_getstr(js, args[0], &name_len); 233 + if (!name) { 234 + return js_mkerr_typed(js, JS_ERR_TYPE, "First argument must be a string"); 235 + } 236 + 237 + jsval_t options = js_mkundef(); 238 + jsval_t callback; 239 + lock_mode_t mode = LOCK_MODE_EXCLUSIVE; 240 + bool if_available = false; 241 + 242 + if (nargs == 2) { 243 + callback = args[1]; 244 + } else { 245 + options = args[1]; 246 + callback = args[2]; 247 + 248 + if (js_type(options) == JS_OBJ) { 249 + jsval_t mode_val = js_get(js, options, "mode"); 250 + if (js_type(mode_val) == JS_STR) { 251 + size_t mode_len; 252 + char *mode_str = js_getstr(js, mode_val, &mode_len); 253 + if (mode_str && strcmp(mode_str, "shared") == 0) mode = LOCK_MODE_SHARED; 254 + } 255 + 256 + jsval_t if_avail_val = js_get(js, options, "ifAvailable"); 257 + if (js_type(if_avail_val) == JS_TRUE) if_available = true; 258 + } 259 + } 260 + 261 + if (js_type(callback) != JS_FUNC) { 262 + return js_mkerr_typed(js, JS_ERR_TYPE, "Callback must be a function"); 263 + } 264 + 265 + jsval_t promise = js_mkpromise(js); 266 + 267 + if (if_available && !can_acquire_lock(name, mode)) { 268 + jsval_t null_val = js_mknull(); 269 + jsval_t result = js_call(js, callback, &null_val, 1); 270 + 271 + if (js_type(result) == JS_PROMISE) { 272 + jsval_t then_fn = js_get(js, result, "then"); 273 + if (js_type(then_fn) == JS_FUNC) { 274 + jsval_t on_resolve = js_mkfun(lock_then_handler); 275 + js_set(js, on_resolve, "_lockName", js_mkstr(js, "", 0)); 276 + js_set(js, on_resolve, "_outerPromise", promise); 277 + js_call_with_this(js, then_fn, result, &on_resolve, 1); 278 + return promise; 279 + } 280 + } 281 + js_resolve_promise(js, promise, result); 282 + return promise; 283 + } 284 + 285 + if (can_acquire_lock(name, mode)) { 286 + lock_entry_t *entry = find_lock(name); 287 + 288 + if (entry && entry->mode == LOCK_MODE_SHARED && mode == LOCK_MODE_SHARED) { 289 + entry->shared_count++; 290 + } else { 291 + create_lock(name, mode); 292 + } 293 + 294 + execute_lock_callback(js, name, mode, callback, promise); 295 + } else { 296 + lock_request_t *req = calloc(1, sizeof(lock_request_t)); 297 + req->js = js; 298 + req->name = strdup(name); 299 + req->mode = mode; 300 + req->callback = callback; 301 + req->promise = promise; 302 + req->next = NULL; 303 + 304 + lock_request_t *tail = pending_requests; 305 + if (!tail) { 306 + pending_requests = req; 307 + } else { 308 + while (tail->next) tail = tail->next; 309 + tail->next = req; 310 + } 311 + } 312 + 313 + return promise; 314 + } 315 + 316 + static jsval_t locks_query(struct js *js, jsval_t *args, int nargs) { 317 + (void)args; 318 + (void)nargs; 319 + 320 + jsval_t result = js_mkobj(js); 321 + jsval_t held_arr = js_mkarr(js); 322 + jsval_t pending_arr = js_mkarr(js); 323 + 324 + lock_entry_t *entry, *tmp; 325 + HASH_ITER(hh, locks, entry, tmp) { 326 + jsval_t lock_info = js_mkobj(js); 327 + js_set(js, lock_info, "name", js_mkstr(js, entry->name, strlen(entry->name))); 328 + js_set(js, lock_info, "mode", js_mkstr(js, entry->mode == LOCK_MODE_EXCLUSIVE ? "exclusive" : "shared", entry->mode == LOCK_MODE_EXCLUSIVE ? 9 : 6)); 329 + js_arr_push(js, held_arr, lock_info); 330 + } 331 + 332 + lock_request_t *req = pending_requests; 333 + while (req) { 334 + jsval_t req_info = js_mkobj(js); 335 + js_set(js, req_info, "name", js_mkstr(js, req->name, strlen(req->name))); 336 + js_set(js, req_info, "mode", js_mkstr(js, req->mode == LOCK_MODE_EXCLUSIVE ? "exclusive" : "shared", req->mode == LOCK_MODE_EXCLUSIVE ? 9 : 6)); 337 + js_arr_push(js, pending_arr, req_info); 338 + req = req->next; 339 + } 340 + 341 + js_set(js, result, "held", held_arr); 342 + js_set(js, result, "pending", pending_arr); 343 + 344 + jsval_t promise = js_mkpromise(js); 345 + js_resolve_promise(js, promise, result); 346 + 347 + return promise; 348 + } 349 + 350 + void init_navigator_module(void) { 351 + struct js *js = rt->js; 352 + 353 + jsval_t navigator_obj = js_mkobj(js); 354 + 355 + js_set(js, navigator_obj, "hardwareConcurrency", js_mknum((double)get_hardware_concurrency())); 356 + js_set(js, navigator_obj, "language", js_mkstr(js, "en-US", 5)); 357 + 358 + jsval_t languages_arr = js_mkarr(js); 359 + js_arr_push(js, languages_arr, js_mkstr(js, "en-US", 5)); 360 + js_set(js, navigator_obj, "languages", languages_arr); 361 + 362 + const char *platform = get_platform_string(); 363 + js_set(js, navigator_obj, "platform", js_mkstr(js, platform, strlen(platform))); 364 + 365 + char user_agent[64]; 366 + snprintf(user_agent, sizeof(user_agent), "Ant/%s", ANT_VERSION); 367 + js_set(js, navigator_obj, "userAgent", js_mkstr(js, user_agent, strlen(user_agent))); 368 + 369 + jsval_t locks_obj = js_mkobj(js); 370 + js_set(js, locks_obj, "request", js_mkfun(locks_request)); 371 + js_set(js, locks_obj, "query", js_mkfun(locks_query)); 372 + js_set(js, locks_obj, get_toStringTag_sym_key(), js_mkstr(js, "LockManager", 11)); 373 + js_set(js, navigator_obj, "locks", locks_obj); 374 + 375 + js_set(js, navigator_obj, get_toStringTag_sym_key(), js_mkstr(js, "Navigator", 9)); 376 + js_set(js, js_glob(js), "navigator", navigator_obj); 377 + }