quickjs-hs 0.1.2.1 → 0.1.2.2
raw patch · 12 files changed
+709/−374 lines, 12 files
Files
- ChangeLog.md +4/−0
- README.md +1/−2
- quickjs-hs.cabal +3/−5
- quickjs/cutils.c +16/−5
- quickjs/libregexp.c +17/−3
- quickjs/libregexp.h +1/−0
- quickjs/libunicode.c +22/−4
- quickjs/quickjs-libc.c +58/−42
- quickjs/quickjs-libc.h +2/−1
- quickjs/quickjs-opcode.h +2/−1
- quickjs/quickjs.c +573/−311
- quickjs/quickjs.h +10/−0
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for quickjs-hs +### 0.1.2.2++* Bumped the quickjs library to release 2020-09-06+ ### 0.1.2.1 * This one should compile
README.md view
@@ -3,8 +3,7 @@  -This package provides a Haskell wrapper for the [QuickJS](https://bellard.org/quickjs/) Javascript Engine.-+This package provides a Haskell wrapper for the [QuickJS](https://bellard.org/quickjs/) Javascript Engine. It has been inspired by the [quickjs-rs](https://github.com/theduke/quickjs-rs) and [ocaml-quickjs](https://github.com/dhcmrlchtdj/ocaml-quickjs) libraries. ## Features The functionality is quite basic and is currently limited to:
quickjs-hs.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: quickjs-hs-version: 0.1.2.1+version: 0.1.2.2 homepage: https://github.com/goodlyrottenapple/quickjs-hs#readme bug-reports: https://github.com/goodlyrottenapple/quickjs-hs/issues author: Sam Balco@@ -19,7 +19,6 @@ . To get started, see the ReadMe below. - extra-source-files: quickjs/cutils.h , quickjs/libbf.h@@ -31,7 +30,6 @@ , quickjs/quickjs-atom.h , quickjs/quickjs-opcode.h - source-repository head type: git location: https://github.com/goodlyrottenapple/quickjs-hs@@ -44,7 +42,7 @@ hs-source-dirs: src build-depends:- base >=4.9 && <5+ base >=4.11 && <5 , aeson >=1.1 && <1.6 , bytestring >=0.10 && <0.11 , containers >=0.5 && <0.7@@ -73,7 +71,7 @@ cc-options: -static -D_GNU_SOURCE - -DCONFIG_VERSION="2020-07-05"+ -DCONFIG_VERSION="2020-09-06" -DCONFIG_BIGNUM test-suite quickjs-hs-test
quickjs/cutils.c view
@@ -258,19 +258,30 @@ return c; } switch(c) {- case 0xc0 ... 0xdf:+ case 0xc0: case 0xc1: case 0xc2: case 0xc3:+ case 0xc4: case 0xc5: case 0xc6: case 0xc7:+ case 0xc8: case 0xc9: case 0xca: case 0xcb:+ case 0xcc: case 0xcd: case 0xce: case 0xcf:+ case 0xd0: case 0xd1: case 0xd2: case 0xd3:+ case 0xd4: case 0xd5: case 0xd6: case 0xd7:+ case 0xd8: case 0xd9: case 0xda: case 0xdb:+ case 0xdc: case 0xdd: case 0xde: case 0xdf: l = 1; break;- case 0xe0 ... 0xef:+ case 0xe0: case 0xe1: case 0xe2: case 0xe3:+ case 0xe4: case 0xe5: case 0xe6: case 0xe7:+ case 0xe8: case 0xe9: case 0xea: case 0xeb:+ case 0xec: case 0xed: case 0xee: case 0xef: l = 2; break;- case 0xf0 ... 0xf7:+ case 0xf0: case 0xf1: case 0xf2: case 0xf3:+ case 0xf4: case 0xf5: case 0xf6: case 0xf7: l = 3; break;- case 0xf8 ... 0xfb:+ case 0xf8: case 0xf9: case 0xfa: case 0xfb: l = 4; break;- case 0xfc ... 0xfd:+ case 0xfc: case 0xfd: l = 5; break; default:
quickjs/libregexp.c view
@@ -569,7 +569,8 @@ } } break;- case '0' ... '7':+ case '0': case '1': case '2': case '3':+ case '4': case '5': case '6': case '7': c -= '0'; if (allow_utf16 == 2) { /* only accept \0 not followed by digit */@@ -1410,7 +1411,9 @@ } } goto normal_char;- case '1' ... '9':+ case '1': case '2': case '3': case '4':+ case '5': case '6': case '7': case '8':+ case '9': { const uint8_t *q = ++p; @@ -1434,7 +1437,7 @@ } goto normal_char; }- return re_parse_error(s, "back reference out of range in reguar expression");+ return re_parse_error(s, "back reference out of range in regular expression"); } emit_back_reference: last_atom_start = s->byte_code.size;@@ -2531,6 +2534,17 @@ int lre_get_flags(const uint8_t *bc_buf) { return bc_buf[RE_HEADER_FLAGS];+}++/* Return NULL if no group names. Otherwise, return a pointer to+ 'capture_count - 1' zero terminated UTF-8 strings. */+const char *lre_get_groupnames(const uint8_t *bc_buf)+{+ uint32_t re_bytecode_len;+ if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0)+ return NULL;+ re_bytecode_len = get_u32(bc_buf + 3);+ return (const char *)(bc_buf + 7 + re_bytecode_len); } #ifdef TEST
quickjs/libregexp.h view
@@ -44,6 +44,7 @@ void *opaque); int lre_get_capture_count(const uint8_t *bc_buf); int lre_get_flags(const uint8_t *bc_buf);+const char *lre_get_groupnames(const uint8_t *bc_buf); int lre_exec(uint8_t **capture, const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, int cbuf_type, void *opaque);
quickjs/libunicode.c view
@@ -527,7 +527,13 @@ } else { d = unicode_decomp_data + unicode_decomp_table2[idx]; switch(type) {- case DECOMP_TYPE_L1 ... DECOMP_TYPE_L7:+ case DECOMP_TYPE_L1:+ case DECOMP_TYPE_L2:+ case DECOMP_TYPE_L3:+ case DECOMP_TYPE_L4:+ case DECOMP_TYPE_L5:+ case DECOMP_TYPE_L6:+ case DECOMP_TYPE_L7: l = type - DECOMP_TYPE_L1 + 1; d += (c - code) * l * 2; for(i = 0; i < l; i++) {@@ -535,7 +541,8 @@ return 0; } return l;- case DECOMP_TYPE_LL1 ... DECOMP_TYPE_LL2:+ case DECOMP_TYPE_LL1:+ case DECOMP_TYPE_LL2: { uint32_t k, p; l = type - DECOMP_TYPE_LL1 + 1;@@ -551,7 +558,11 @@ } } return l;- case DECOMP_TYPE_S1 ... DECOMP_TYPE_S5:+ case DECOMP_TYPE_S1:+ case DECOMP_TYPE_S2:+ case DECOMP_TYPE_S3:+ case DECOMP_TYPE_S4:+ case DECOMP_TYPE_S5: l = type - DECOMP_TYPE_S1 + 1; d += (c - code) * l; for(i = 0; i < l; i++) {@@ -582,7 +593,14 @@ case DECOMP_TYPE_B18: l = 18; goto decomp_type_b;- case DECOMP_TYPE_B1 ... DECOMP_TYPE_B8:+ case DECOMP_TYPE_B1:+ case DECOMP_TYPE_B2:+ case DECOMP_TYPE_B3:+ case DECOMP_TYPE_B4:+ case DECOMP_TYPE_B5:+ case DECOMP_TYPE_B6:+ case DECOMP_TYPE_B7:+ case DECOMP_TYPE_B8: l = type - DECOMP_TYPE_B1 + 1; decomp_type_b: {
quickjs/quickjs-libc.c view
@@ -2993,9 +2993,8 @@ } JSWorkerData; typedef struct {- /* source code of the worker */- char *eval_buf;- size_t eval_buf_len;+ char *filename; /* module filename */+ char *basename; /* module base name */ JSWorkerMessagePipe *recv_pipe, *send_pipe; } WorkerFuncArgs; @@ -3005,6 +3004,7 @@ } JSSABHeader; static JSClassID js_worker_class_id;+static JSContext *(*js_worker_new_context_func)(JSRuntime *rt); static int atomic_add_int(int *ptr, int v) {@@ -3136,7 +3136,6 @@ JSRuntime *rt; JSThreadState *ts; JSContext *ctx;- JSValue retval; rt = JS_NewRuntime(); if (rt == NULL) {@@ -3145,12 +3144,16 @@ } js_std_init_handlers(rt); + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);+ /* set the pipe to communicate with the parent */ ts = JS_GetRuntimeOpaque(rt); ts->recv_pipe = args->recv_pipe; ts->send_pipe = args->send_pipe; - ctx = JS_NewContext(rt);+ /* function pointer to avoid linking the whole JS_NewContext() if+ not needed */+ ctx = js_worker_new_context_func(rt); if (ctx == NULL) { fprintf(stderr, "JS_NewContext failure"); }@@ -3159,18 +3162,11 @@ js_std_add_helpers(ctx, -1, NULL); - /* system modules */- js_init_module_std(ctx, "std");- js_init_module_os(ctx, "os");-- retval = JS_Eval(ctx, args->eval_buf, args->eval_buf_len,- "<worker>", JS_EVAL_TYPE_MODULE);- free(args->eval_buf);- free(args);-- if (JS_IsException(retval))+ if (!JS_RunModule(ctx, args->basename, args->filename)) js_std_dump_error(ctx);- JS_FreeValue(ctx, retval);+ free(args->filename);+ free(args->basename);+ free(args); js_std_loop(ctx); @@ -3216,52 +3212,53 @@ int argc, JSValueConst *argv) { JSRuntime *rt = JS_GetRuntime(ctx);- WorkerFuncArgs *args;- const char *str;- size_t str_len;+ WorkerFuncArgs *args = NULL; pthread_t tid; pthread_attr_t attr; JSValue obj = JS_UNDEFINED; int ret;+ const char *filename = NULL, *basename;+ JSAtom basename_atom; /* XXX: in order to avoid problems with resource liberation, we don't support creating workers inside workers */ if (!is_main_thread(rt)) return JS_ThrowTypeError(ctx, "cannot create a worker inside a worker");- - /* script source */++ /* base name, assuming the calling function is a normal JS+ function */+ basename_atom = JS_GetScriptOrModuleName(ctx, 1);+ if (basename_atom == JS_ATOM_NULL) {+ return JS_ThrowTypeError(ctx, "could not determine calling script or module name");+ }+ basename = JS_AtomToCString(ctx, basename_atom);+ JS_FreeAtom(ctx, basename_atom);+ if (!basename)+ goto fail; - str = JS_ToCStringLen(ctx, &str_len, argv[0]);- if (!str)- return JS_EXCEPTION;+ /* module name */+ filename = JS_ToCString(ctx, argv[0]);+ if (!filename)+ goto fail; args = malloc(sizeof(*args));- if (!args) {- JS_ThrowOutOfMemory(ctx);- goto fail;- }+ if (!args)+ goto oom_fail; memset(args, 0, sizeof(*args));- args->eval_buf = malloc(str_len + 1);- if (!args->eval_buf) {- JS_ThrowOutOfMemory(ctx);- goto fail;- }- memcpy(args->eval_buf, str, str_len + 1);- args->eval_buf_len = str_len;- JS_FreeCString(ctx, str);- str = NULL;+ args->filename = strdup(filename);+ args->basename = strdup(basename); /* ports */ args->recv_pipe = js_new_message_pipe(); if (!args->recv_pipe)- goto fail;+ goto oom_fail; args->send_pipe = js_new_message_pipe(); if (!args->send_pipe)- goto fail;+ goto oom_fail; obj = js_worker_ctor_internal(ctx, new_target, args->send_pipe, args->recv_pipe);- if (JS_IsUndefined(obj))+ if (JS_IsException(obj)) goto fail; pthread_attr_init(&attr);@@ -3273,11 +3270,17 @@ JS_ThrowTypeError(ctx, "could not create worker"); goto fail; }+ JS_FreeCString(ctx, basename);+ JS_FreeCString(ctx, filename); return obj;+ oom_fail:+ JS_ThrowOutOfMemory(ctx); fail:- JS_FreeCString(ctx, str);+ JS_FreeCString(ctx, basename);+ JS_FreeCString(ctx, filename); if (args) {- free(args->eval_buf);+ free(args->filename);+ free(args->basename); js_free_message_pipe(args->recv_pipe); js_free_message_pipe(args->send_pipe); free(args);@@ -3417,6 +3420,13 @@ #endif /* USE_WORKER */ +void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt))+{+#ifdef USE_WORKER+ js_worker_new_context_func = func;+#endif+}+ #if defined(_WIN32) #define OS_PLATFORM "win32" #elif defined(__APPLE__)@@ -3667,6 +3677,12 @@ if (!th->has_object) free_timer(rt, th); }++#ifdef USE_WORKER+ /* XXX: free port_list ? */+ js_free_message_pipe(ts->recv_pipe);+ js_free_message_pipe(ts->send_pipe);+#endif free(ts); JS_SetRuntimeOpaque(rt, NULL); /* fail safe */
quickjs/quickjs-libc.h view
@@ -50,7 +50,8 @@ void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, JSValueConst reason, JS_BOOL is_handled, void *opaque);-+void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt));+ #ifdef __cplusplus } /* extern "C" { */ #endif
quickjs/quickjs-opcode.h view
@@ -359,7 +359,8 @@ DEF( is_undefined, 1, 1, 1, none) DEF( is_null, 1, 1, 1, none)-DEF( is_function, 1, 1, 1, none)+DEF(typeof_is_undefined, 1, 1, 1, none)+DEF( typeof_is_function, 1, 1, 1, none) #endif #undef DEF
quickjs/quickjs.c view
@@ -825,8 +825,8 @@ } JSShapeProperty; struct JSShape {- uint32_t prop_hash_end[0]; /* hash table of size hash_mask + 1- before the start of the structure. */+ /* hash table of size hash_mask + 1 before the start of the+ structure (see prop_hash_end()). */ JSGCObjectHeader header; /* true if the shape is inserted in the shape hash table. If not, JSShape.hash is not valid */@@ -859,6 +859,7 @@ uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */+ uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ uint16_t class_id; /* see JS_CLASS_x */ }; };@@ -931,7 +932,7 @@ /* byte sizes: 40/48/72 */ }; enum {- JS_ATOM_NULL,+ __JS_ATOM_NULL = JS_ATOM_NULL, #define DEF(name, str) JS_ATOM_ ## name, #include "quickjs-atom.h" #undef DEF@@ -4198,9 +4199,14 @@ return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); } +static inline uint32_t *prop_hash_end(JSShape *sh)+{+ return (uint32_t *)sh;+}+ static inline void *get_alloc_from_shape(JSShape *sh) {- return sh->prop_hash_end - ((intptr_t)sh->prop_hash_mask + 1);+ return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); } static inline JSShapeProperty *get_shape_prop(JSShape *sh)@@ -4311,7 +4317,7 @@ if (proto) JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto)); sh->proto = proto;- memset(sh->prop_hash_end - hash_size, 0, sizeof(sh->prop_hash_end[0]) *+ memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * hash_size); sh->prop_hash_mask = hash_size - 1; sh->prop_size = prop_size;@@ -4440,13 +4446,13 @@ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); new_hash_mask = new_hash_size - 1; sh->prop_hash_mask = new_hash_mask;- memset(sh->prop_hash_end - new_hash_size, 0,- sizeof(sh->prop_hash_end[0]) * new_hash_size);+ memset(prop_hash_end(sh) - new_hash_size, 0,+ sizeof(prop_hash_end(sh)[0]) * new_hash_size); for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { if (pr->atom != JS_ATOM_NULL) { h = ((uintptr_t)pr->atom & new_hash_mask);- pr->hash_next = sh->prop_hash_end[-h - 1];- sh->prop_hash_end[-h - 1] = i + 1;+ pr->hash_next = prop_hash_end(sh)[-h - 1];+ prop_hash_end(sh)[-h - 1] = i + 1; } } js_free(ctx, get_alloc_from_shape(old_sh));@@ -4500,8 +4506,8 @@ memcpy(sh, old_sh, sizeof(JSShape)); list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); - memset(sh->prop_hash_end - new_hash_size, 0,- sizeof(sh->prop_hash_end[0]) * new_hash_size);+ memset(prop_hash_end(sh) - new_hash_size, 0,+ sizeof(prop_hash_end(sh)[0]) * new_hash_size); j = 0; old_pr = old_sh->prop;@@ -4512,8 +4518,8 @@ pr->atom = old_pr->atom; pr->flags = old_pr->flags; h = ((uintptr_t)old_pr->atom & new_hash_mask);- pr->hash_next = sh->prop_hash_end[-h - 1];- sh->prop_hash_end[-h - 1] = j + 1;+ pr->hash_next = prop_hash_end(sh)[-h - 1];+ prop_hash_end(sh)[-h - 1] = j + 1; prop[j] = prop[i]; j++; pr++;@@ -4575,8 +4581,8 @@ /* add in hash table */ hash_mask = sh->prop_hash_mask; h = atom & hash_mask;- pr->hash_next = sh->prop_hash_end[-h - 1];- sh->prop_hash_end[-h - 1] = sh->prop_count;+ pr->hash_next = prop_hash_end(sh)[-h - 1];+ prop_hash_end(sh)[-h - 1] = sh->prop_count; return 0; } @@ -4693,6 +4699,7 @@ p->is_constructor = 0; p->is_uncatchable_error = 0; p->tmp_mark = 0;+ p->is_HTMLDDA = 0; p->first_weak_ref = NULL; p->u.opaque = NULL; p->shape = sh;@@ -4731,7 +4738,19 @@ p->prop[0].u.value = JS_UNDEFINED; break; case JS_CLASS_ARGUMENTS:- case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY:+ case JS_CLASS_UINT8C_ARRAY:+ case JS_CLASS_INT8_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ case JS_CLASS_INT16_ARRAY:+ case JS_CLASS_UINT16_ARRAY:+ case JS_CLASS_INT32_ARRAY:+ case JS_CLASS_UINT32_ARRAY:+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT64_ARRAY:+ case JS_CLASS_BIG_UINT64_ARRAY:+#endif+ case JS_CLASS_FLOAT32_ARRAY:+ case JS_CLASS_FLOAT64_ARRAY: p->is_exotic = 1; p->fast_array = 1; p->u.array.u.ptr = NULL;@@ -5133,7 +5152,7 @@ intptr_t h; sh = p->shape; h = (uintptr_t)atom & sh->prop_hash_mask;- h = sh->prop_hash_end[-h - 1];+ h = prop_hash_end(sh)[-h - 1]; prop = get_shape_prop(sh); while (h) { pr = &prop[h - 1];@@ -5154,7 +5173,7 @@ intptr_t h; sh = p->shape; h = (uintptr_t)atom & sh->prop_hash_mask;- h = sh->prop_hash_end[-h - 1];+ h = prop_hash_end(sh)[-h - 1]; prop = get_shape_prop(sh); while (h) { pr = &prop[h - 1];@@ -7994,7 +8013,7 @@ redo: sh = p->shape; h1 = atom & sh->prop_hash_mask;- h = sh->prop_hash_end[-h1 - 1];+ h = prop_hash_end(sh)[-h1 - 1]; prop = get_shape_prop(sh); lpr = NULL; lpr_idx = 0; /* prevent warning */@@ -8015,7 +8034,7 @@ lpr = get_shape_prop(sh) + lpr_idx; lpr->hash_next = pr->hash_next; } else {- sh->prop_hash_end[-h1 - 1] = pr->hash_next;+ prop_hash_end(sh)[-h1 - 1] = pr->hash_next; } sh->deleted_prop_count++; /* free the entry */@@ -8095,15 +8114,16 @@ } /* set the array length and remove the array elements if necessary. */-static int set_array_length(JSContext *ctx, JSObject *p, JSProperty *prop,- JSValue val, int flags)+static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, int flags) { uint32_t len, idx, cur_len; int i, ret; + /* Note: this call can reallocate the properties of 'p' */ ret = JS_ToArrayLengthFree(ctx, &len, val); if (ret) return -1;+ if (likely(p->fast_array)) { uint32_t old_len = p->u.array.count; if (len < old_len) {@@ -8112,11 +8132,11 @@ } p->u.array.count = len; }- prop->u.value = JS_NewUint32(ctx, len);+ p->prop[0].u.value = JS_NewUint32(ctx, len); } else { /* Note: length is always a uint32 because the object is an array */- JS_ToUint32(ctx, &cur_len, prop->u.value);+ JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); if (len < cur_len) { uint32_t d; JSShape *sh;@@ -8366,7 +8386,7 @@ (JS_PROP_LENGTH | JS_PROP_WRITABLE)) { assert(p->class_id == JS_CLASS_ARRAY); assert(prop == JS_ATOM_length);- return set_array_length(ctx, p, pr, val, flags);+ return set_array_length(ctx, p, val, flags); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {@@ -9047,7 +9067,7 @@ } if (prs->flags & JS_PROP_LENGTH) { if (flags & JS_PROP_HAS_VALUE) {- res = set_array_length(ctx, p, pr, JS_DupValue(ctx, val),+ res = set_array_length(ctx, p, JS_DupValue(ctx, val), flags); } else { res = TRUE;@@ -9806,6 +9826,24 @@ return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint); } +void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return;+ p = JS_VALUE_GET_OBJ(obj);+ p->is_HTMLDDA = TRUE;+}++static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return FALSE;+ p = JS_VALUE_GET_OBJ(obj);+ return p->is_HTMLDDA;+}+ static int JS_ToBoolFree(JSContext *ctx, JSValue val) { uint32_t tag = JS_VALUE_GET_TAG(val);@@ -9843,6 +9881,15 @@ return ret; } #endif+ case JS_TAG_OBJECT:+ {+ JSObject *p = JS_VALUE_GET_OBJ(val);+ BOOL ret;+ ret = !p->is_HTMLDDA;+ JS_FreeValue(ctx, val);+ return ret;+ }+ break; default: if (JS_TAG_IS_FLOAT64(tag)) { double d = JS_VALUE_GET_FLOAT64(val);@@ -11602,7 +11649,19 @@ case JS_CLASS_ARGUMENTS: JS_DumpValueShort(rt, p->u.array.u.values[i]); break;- case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY:+ case JS_CLASS_UINT8C_ARRAY:+ case JS_CLASS_INT8_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ case JS_CLASS_INT16_ARRAY:+ case JS_CLASS_UINT16_ARRAY:+ case JS_CLASS_INT32_ARRAY:+ case JS_CLASS_UINT32_ARRAY:+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT64_ARRAY:+ case JS_CLASS_BIG_UINT64_ARRAY:+#endif+ case JS_CLASS_FLOAT32_ARRAY:+ case JS_CLASS_FLOAT64_ARRAY: { int size = 1 << typed_array_size_log2(p->class_id); const uint8_t *b = p->u.array.u.uint8_ptr + i * size;@@ -13668,33 +13727,8 @@ JS_FreeValue(ctx, op2); } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {- /* can use floating point comparison */- double d1, d2;- if (tag1 == JS_TAG_FLOAT64) {- d1 = JS_VALUE_GET_FLOAT64(op1);- } else {- d1 = JS_VALUE_GET_INT(op1);- }- if (tag2 == JS_TAG_FLOAT64) {- d2 = JS_VALUE_GET_FLOAT64(op2);- } else {- d2 = JS_VALUE_GET_INT(op2);- }- switch(op) {- case OP_lt:- res = (d1 < d2); /* if NaN return false */- break;- case OP_lte:- res = (d1 <= d2); /* if NaN return false */- break;- case OP_gt:- res = (d1 > d2); /* if NaN return false */- break;- default:- case OP_gte:- res = (d1 >= d2); /* if NaN return false */- break;- }+ /* fast path for float64/int */+ goto float64_compare; } else { if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&@@ -13727,20 +13761,51 @@ } } - if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_DECIMAL ||- JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_DECIMAL) {+ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);++ if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2); if (res < 0) goto exception;- } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_FLOAT ||- JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_FLOAT) {+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2); if (res < 0) goto exception;- } else {+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2); if (res < 0) goto exception;+ } else {+ double d1, d2;++ float64_compare:+ /* can use floating point comparison */+ if (tag1 == JS_TAG_FLOAT64) {+ d1 = JS_VALUE_GET_FLOAT64(op1);+ } else {+ d1 = JS_VALUE_GET_INT(op1);+ }+ if (tag2 == JS_TAG_FLOAT64) {+ d2 = JS_VALUE_GET_FLOAT64(op2);+ } else {+ d2 = JS_VALUE_GET_INT(op2);+ }+ switch(op) {+ case OP_lt:+ res = (d1 < d2); /* if NaN return false */+ break;+ case OP_lte:+ res = (d1 <= d2); /* if NaN return false */+ break;+ case OP_gt:+ res = (d1 > d2); /* if NaN return false */+ break;+ default:+ case OP_gte:+ res = (d1 >= d2); /* if NaN return false */+ break;+ } } } done:@@ -13895,9 +13960,17 @@ } goto redo; } else {+ /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */+ if ((JS_IsHTMLDDA(ctx, op1) &&+ (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||+ (JS_IsHTMLDDA(ctx, op2) &&+ (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {+ res = TRUE;+ } else {+ res = FALSE;+ } JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2);- res = FALSE; } done: sp[-2] = JS_NewBool(ctx, res ^ is_neq);@@ -14348,9 +14421,17 @@ } goto redo; } else {+ /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */+ if ((JS_IsHTMLDDA(ctx, op1) &&+ (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||+ (JS_IsHTMLDDA(ctx, op2) &&+ (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {+ res = TRUE;+ } else {+ res = FALSE;+ } JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2);- res = FALSE; } sp[-2] = JS_NewBool(ctx, res ^ is_neq); return 0;@@ -14631,7 +14712,7 @@ return 0; } -static __exception int js_operator_typeof(JSContext *ctx, JSValue op1)+static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) { JSAtom atom; uint32_t tag;@@ -14663,10 +14744,16 @@ atom = JS_ATOM_string; break; case JS_TAG_OBJECT:- if (JS_IsFunction(ctx, op1))- atom = JS_ATOM_function;- else- goto obj_type;+ {+ JSObject *p;+ p = JS_VALUE_GET_OBJ(op1);+ if (unlikely(p->is_HTMLDDA)) + atom = JS_ATOM_undefined;+ else if (JS_IsFunction(ctx, op1))+ atom = JS_ATOM_function;+ else+ goto obj_type;+ } break; case JS_TAG_NULL: obj_type:@@ -15231,7 +15318,10 @@ return 0; } -/* enum_rec -> enum_rec value done */+/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'+ objs. If 'done' is true or in case of exception, 'enum_rec' is set+ to undefined. If 'done' is true, 'value' is always set to+ undefined. */ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) { JSValue value = JS_UNDEFINED;@@ -15246,8 +15336,12 @@ /* replace the iteration object with undefined */ JS_FreeValue(ctx, sp[offset]); sp[offset] = JS_UNDEFINED;- if (done < 0)+ if (done < 0) { return -1;+ } else {+ JS_FreeValue(ctx, value);+ value = JS_UNDEFINED;+ } } } sp[0] = value;@@ -15363,10 +15457,10 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) { JSValue iterator, enumobj, method, value;- int pos, is_array_iterator;+ int is_array_iterator; JSValue *arrp;- uint32_t i, count32;-+ uint32_t i, count32, pos;+ if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { JS_ThrowInternalError(ctx, "invalid index for append"); return -1;@@ -15399,24 +15493,21 @@ if (is_array_iterator && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0) && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {- int64_t len;- /* Handle fast arrays explicitly */- if (js_get_length64(ctx, &len, sp[-1]))+ uint32_t len;+ if (js_get_length32(ctx, &len, sp[-1])) goto exception;+ /* if len > count32, the elements >= count32 might be read in+ the prototypes and might have side effects */+ if (len != count32)+ goto general_case;+ /* Handle fast arrays explicitly */ for (i = 0; i < count32; i++) { if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) goto exception; }- if (len > count32) {- /* This is not strictly correct because the trailing elements are- empty instead of undefined. Append undefined entries instead.- */- pos += len - count32;- if (JS_SetProperty(ctx, sp[-3], JS_ATOM_length, JS_NewUint32(ctx, pos)) < 0)- goto exception;- } } else {+ general_case: for (;;) { BOOL done; value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);@@ -15430,6 +15521,7 @@ goto exception; } }+ /* Note: could raise an error if too many elements */ sp[-2] = JS_NewInt32(ctx, pos); JS_FreeValue(ctx, enumobj); JS_FreeValue(ctx, method);@@ -17742,47 +17834,42 @@ BREAK; CASE(OP_add_loc): {- JSValue ops[2];+ JSValue *pv; int idx; idx = *pc; pc += 1; - ops[0] = var_buf[idx];- ops[1] = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(ops[0], ops[1]))) {+ pv = &var_buf[idx];+ if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { int64_t r;- r = (int64_t)JS_VALUE_GET_INT(ops[0]) + JS_VALUE_GET_INT(ops[1]);+ r = (int64_t)JS_VALUE_GET_INT(*pv) ++ JS_VALUE_GET_INT(sp[-1]); if (unlikely((int)r != r)) goto add_loc_slow;- var_buf[idx] = JS_NewInt32(ctx, r);+ *pv = JS_NewInt32(ctx, r); sp--;- } else if (JS_VALUE_GET_TAG(ops[0]) == JS_TAG_STRING) {+ } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {+ JSValue op1;+ op1 = sp[-1]; sp--;- ops[1] = JS_ToPrimitiveFree(ctx, ops[1], HINT_NONE);- if (JS_IsException(ops[1])) {+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);+ if (JS_IsException(op1)) goto exception;- }- /* XXX: should not modify the variable in case of- exception */- ops[0] = JS_ConcatString(ctx, ops[0], ops[1]);- if (JS_IsException(ops[0])) {- var_buf[idx] = JS_UNDEFINED;+ op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1);+ if (JS_IsException(op1)) goto exception;- }- var_buf[idx] = ops[0];+ set_value(ctx, pv, op1); } else {+ JSValue ops[2]; add_loc_slow:- /* XXX: should not modify the variable in case of- exception */- sp--; /* In case of exception, js_add_slow frees ops[0]- and ops[1]. */- /* XXX: change API */- if (js_add_slow(ctx, ops + 2)) {- var_buf[idx] = JS_UNDEFINED;+ and ops[1], so we must duplicate *pv */+ ops[0] = JS_DupValue(ctx, *pv);+ ops[1] = sp[-1];+ sp--;+ if (js_add_slow(ctx, ops + 2)) goto exception;- }- var_buf[idx] = ops[0];+ set_value(ctx, pv, ops[0]); } } BREAK;@@ -18427,7 +18514,15 @@ } else { goto free_and_set_false; }- CASE(OP_is_function):+ /* XXX: could merge to a single opcode */+ CASE(OP_typeof_is_undefined):+ /* different from OP_is_undefined because of isHTMLDDA */+ if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {+ goto free_and_set_true;+ } else {+ goto free_and_set_false;+ }+ CASE(OP_typeof_is_function): if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { goto free_and_set_true; } else {@@ -19656,6 +19751,9 @@ TOK_MATH_POW_ASSIGN, #endif TOK_POW_ASSIGN,+ TOK_LAND_ASSIGN,+ TOK_LOR_ASSIGN,+ TOK_DOUBLE_QUESTION_MARK_ASSIGN, TOK_DEC, TOK_INC, TOK_SHL,@@ -20015,11 +20113,14 @@ JS_FreeValue(s->ctx, token->u.regexp.flags); break; case TOK_IDENT:- case TOK_FIRST_KEYWORD ... TOK_LAST_KEYWORD: case TOK_PRIVATE_NAME: JS_FreeAtom(s->ctx, token->u.ident.atom); break; default:+ if (token->val >= TOK_FIRST_KEYWORD &&+ token->val <= TOK_LAST_KEYWORD) {+ JS_FreeAtom(s->ctx, token->u.ident.atom);+ } break; } }@@ -20233,6 +20334,8 @@ } if (c == '\\') { c = *p;+ /* XXX: need a specific JSON case to avoid+ accepting invalid escapes */ switch(c) { case '\0': if (p >= s->buf_end)@@ -20256,18 +20359,23 @@ s->line_num++; continue; default:- if (c >= '0' && c <= '7') {+ if (c >= '0' && c <= '9') { if (!s->cur_func)- goto invalid_octal; /* JSON case */+ goto invalid_escape; /* JSON case */ if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') goto parse_escape; if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { p++; c = '\0'; } else {- invalid_octal:- if (do_throw)- js_parse_error(s, "invalid octal syntax in strict mode");+ if (c >= '8' || sep == '`') {+ /* Note: according to ES2021, \8 and \9 are not+ accepted in strict mode or in templates. */+ goto invalid_escape;+ } else {+ if (do_throw)+ js_parse_error(s, "octal escape sequences are not allowed in strict mode");+ } goto fail; } } else if (c >= 0x80) {@@ -20284,6 +20392,7 @@ parse_escape: ret = lre_parse_escape(&p, TRUE); if (ret == -1) {+ invalid_escape: if (do_throw) js_parse_error(s, "malformed escape sequence in string literal"); goto fail;@@ -20634,8 +20743,20 @@ } } goto def_token;- case 'a' ... 'z':- case 'A' ... 'Z':+ case 'a': case 'b': case 'c': case 'd':+ case 'e': case 'f': case 'g': case 'h':+ case 'i': case 'j': case 'k': case 'l':+ case 'm': case 'n': case 'o': case 'p':+ case 'q': case 'r': case 's': case 't':+ case 'u': case 'v': case 'w': case 'x':+ case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D':+ case 'E': case 'F': case 'G': case 'H':+ case 'I': case 'J': case 'K': case 'L':+ case 'M': case 'N': case 'O': case 'P':+ case 'Q': case 'R': case 'S': case 'T':+ case 'U': case 'V': case 'W': case 'X':+ case 'Y': case 'Z': case '_': case '$': /* identifier */@@ -20717,7 +20838,9 @@ goto fail; } goto parse_number;- case '1' ... '9':+ case '1': case '2': case '3': case '4':+ case '5': case '6': case '7': case '8':+ case '9': /* number */ parse_number: {@@ -20887,8 +21010,13 @@ p += 2; s->token.val = TOK_AND_ASSIGN; } else if (p[1] == '&') {- p += 2;- s->token.val = TOK_LAND;+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_LAND_ASSIGN;+ } else {+ p += 2;+ s->token.val = TOK_LAND;+ } } else { goto def_token; }@@ -20934,16 +21062,26 @@ p += 2; s->token.val = TOK_OR_ASSIGN; } else if (p[1] == '|') {- p += 2;- s->token.val = TOK_LOR;+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_LOR_ASSIGN;+ } else {+ p += 2;+ s->token.val = TOK_LOR;+ } } else { goto def_token; } break; case '?': if (p[1] == '?') {- p += 2;- s->token.val = TOK_DOUBLE_QUESTION_MARK;+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;+ } else {+ p += 2;+ s->token.val = TOK_DOUBLE_QUESTION_MARK;+ } } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) { p += 2; s->token.val = TOK_QUESTION_MARK_DOT;@@ -21135,8 +21273,20 @@ goto def_token; } break;- case 'a' ... 'z':- case 'A' ... 'Z':+ case 'a': case 'b': case 'c': case 'd':+ case 'e': case 'f': case 'g': case 'h':+ case 'i': case 'j': case 'k': case 'l':+ case 'm': case 'n': case 'o': case 'p':+ case 'q': case 'r': case 's': case 't':+ case 'u': case 'v': case 'w': case 'x':+ case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D':+ case 'E': case 'F': case 'G': case 'H':+ case 'I': case 'J': case 'K': case 'L':+ case 'M': case 'N': case 'O': case 'P':+ case 'Q': case 'R': case 'S': case 'T':+ case 'U': case 'V': case 'W': case 'X':+ case 'Y': case 'Z': case '_': case '$': /* identifier : only pure ascii characters are accepted */@@ -21161,7 +21311,9 @@ if (!is_digit(p[1])) goto def_token; goto parse_number;- case '1' ... '9':+ case '1': case '2': case '3': case '4':+ case '5': case '6': case '7': case '8':+ case '9': /* number */ parse_number: {@@ -23267,15 +23419,6 @@ return FALSE; } -typedef struct JSLValue {- int opcode;- int scope;- int label;- int depth;- int tok;- JSAtom name;-} JSLValue;- static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, JSAtom *pname, int *plabel, int *pdepth, BOOL keep, int tok)@@ -23400,84 +23543,112 @@ return 0; } -/* if special = TRUE: specific post inc/dec case */-/* name has a live reference */+typedef enum {+ PUT_LVALUE_NOKEEP, /* [depth] v -> */+ PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently+ just disable optimizations) */+ PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */+ PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */+ PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */+} PutLValueEnum;++/* name has a live reference. 'is_let' is only used with opcode =+ OP_scope_get_var which is never generated by get_lvalue(). */ static void put_lvalue(JSParseState *s, int opcode, int scope,- JSAtom name, int label, BOOL special)+ JSAtom name, int label, PutLValueEnum special,+ BOOL is_let) { switch(opcode) { case OP_get_field:- if (!special)- emit_op(s, OP_insert2); /* obj v -> v obj v */- else- emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */- emit_op(s, OP_put_field);- emit_u32(s, name); /* name has refcount */- break; case OP_scope_get_private_field:- if (!special)+ /* depth = 1 */+ switch(special) {+ case PUT_LVALUE_NOKEEP:+ case PUT_LVALUE_NOKEEP_DEPTH:+ break;+ case PUT_LVALUE_KEEP_TOP: emit_op(s, OP_insert2); /* obj v -> v obj v */- else+ break;+ case PUT_LVALUE_KEEP_SECOND: emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */- emit_op(s, OP_scope_put_private_field);- emit_u32(s, name); /* name has refcount */- emit_u16(s, scope);+ break;+ case PUT_LVALUE_NOKEEP_BOTTOM:+ emit_op(s, OP_swap);+ break;+ default:+ abort();+ } break; case OP_get_array_el:- if (!special)- emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */- else- emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */- emit_op(s, OP_put_array_el);- break; case OP_get_ref_value:- JS_FreeAtom(s->ctx, name);- emit_label(s, label);- if (!special)+ /* depth = 2 */+ if (opcode == OP_get_ref_value) {+ JS_FreeAtom(s->ctx, name);+ emit_label(s, label);+ }+ switch(special) {+ case PUT_LVALUE_NOKEEP:+ emit_op(s, OP_nop); /* will trigger optimization */+ break;+ case PUT_LVALUE_NOKEEP_DEPTH:+ break;+ case PUT_LVALUE_KEEP_TOP: emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */- else+ break;+ case PUT_LVALUE_KEEP_SECOND: emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */- emit_op(s, OP_put_ref_value);+ break;+ case PUT_LVALUE_NOKEEP_BOTTOM:+ emit_op(s, OP_rot3l);+ break;+ default:+ abort();+ } break; case OP_get_super_value:- if (!special)+ /* depth = 3 */+ switch(special) {+ case PUT_LVALUE_NOKEEP:+ case PUT_LVALUE_NOKEEP_DEPTH:+ break;+ case PUT_LVALUE_KEEP_TOP: emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */- else+ break;+ case PUT_LVALUE_KEEP_SECOND: emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */- emit_op(s, OP_put_super_value);+ break;+ case PUT_LVALUE_NOKEEP_BOTTOM:+ emit_op(s, OP_rot4l);+ break;+ default:+ abort();+ } break; default:- abort();+ break; }-}--static void put_lvalue_nokeep(JSParseState *s, int opcode, int scope,- JSAtom name, int label, int var_tok)-{+ switch(opcode) { case OP_scope_get_var: /* val -- */- emit_op(s, (var_tok == TOK_CONST || var_tok == TOK_LET) ?- OP_scope_put_var_init : OP_scope_put_var);+ assert(special == PUT_LVALUE_NOKEEP ||+ special == PUT_LVALUE_NOKEEP_DEPTH);+ emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var); emit_u32(s, name); /* has refcount */ emit_u16(s, scope); break;- case OP_get_field: /* obj val -- */+ case OP_get_field: emit_op(s, OP_put_field);- emit_u32(s, name); /* has refcount */+ emit_u32(s, name); /* name has refcount */ break; case OP_scope_get_private_field: emit_op(s, OP_scope_put_private_field);- emit_u32(s, name); /* has refcount */+ emit_u32(s, name); /* name has refcount */ emit_u16(s, scope); break;- case OP_get_array_el: /* obj prop val -- */+ case OP_get_array_el: emit_op(s, OP_put_array_el); break;- case OP_get_ref_value: /* obj prop val -- */- /* XXX: currently this reference is never optimized */- JS_FreeAtom(s->ctx, name);- emit_label(s, label);- //emit_op(s, OP_nop); /* emit 2 bytes for optimizer */+ case OP_get_ref_value: emit_op(s, OP_put_ref_value); break; case OP_get_super_value:@@ -23869,7 +24040,9 @@ emit_label(s, label_hasval); } /* store value into lvalue object */- put_lvalue_nokeep(s, opcode, scope, var_name, label_lvalue, tok);+ put_lvalue(s, opcode, scope, var_name, label_lvalue,+ PUT_LVALUE_NOKEEP_DEPTH,+ (tok == TOK_CONST || tok == TOK_LET)); if (s->token.val == '}') break; /* accept a trailing comma before the '}' */@@ -23969,8 +24142,9 @@ emit_label(s, label_hasval); } /* store value into lvalue object */- put_lvalue_nokeep(s, opcode, scope, var_name,- label_lvalue, tok);+ put_lvalue(s, opcode, scope, var_name,+ label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,+ (tok == TOK_CONST || tok == TOK_LET)); } if (s->token.val == ']') break;@@ -24786,7 +24960,8 @@ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) return -1; emit_op(s, OP_dec + op - TOK_DEC);- put_lvalue(s, opcode, scope, name, label, FALSE);+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,+ FALSE); } break; case TOK_TYPEOF:@@ -24834,7 +25009,8 @@ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) return -1; emit_op(s, OP_post_dec + op - TOK_DEC);- put_lvalue(s, opcode, scope, name, label, TRUE);+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,+ FALSE); if (next_token(s)) return -1; }@@ -25298,7 +25474,61 @@ #endif emit_op(s, op); }- put_lvalue(s, opcode, scope, name, label, FALSE);+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);+ } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {+ int label, label1, depth_lvalue, label2;+ + if (next_token(s))+ return -1;+ if (get_lvalue(s, &opcode, &scope, &name, &label,+ &depth_lvalue, TRUE, op) < 0)+ return -1;++ emit_op(s, OP_dup);+ if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)+ emit_op(s, OP_is_undefined_or_null);+ label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,+ -1);+ emit_op(s, OP_drop);+ + if (js_parse_assign_expr(s, in_accepted)) {+ JS_FreeAtom(s->ctx, name);+ return -1;+ }++ if (opcode == OP_get_ref_value && name == name0) {+ set_object_name(s, name);+ }+ + switch(depth_lvalue) {+ case 1:+ emit_op(s, OP_insert2);+ break;+ case 2:+ emit_op(s, OP_insert3);+ break;+ case 3:+ emit_op(s, OP_insert4);+ break;+ default:+ abort();+ }++ /* XXX: we disable the OP_put_ref_value optimization by not+ using put_lvalue() otherwise depth_lvalue is not correct */+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,+ FALSE);+ label2 = emit_goto(s, OP_goto, -1);+ + emit_label(s, label1);++ /* remove the lvalue stack entries */+ while (depth_lvalue != 0) {+ emit_op(s, OP_nip);+ depth_lvalue--;+ }++ emit_label(s, label2); } return 0; }@@ -25563,8 +25793,8 @@ goto var_error; } set_object_name(s, name);- put_lvalue(s, opcode, scope, name1, label, FALSE);- emit_op(s, OP_drop);+ put_lvalue(s, opcode, scope, name1, label,+ PUT_LVALUE_NOKEEP, FALSE); } else { if (js_parse_assign_expr(s, in_accepted)) goto var_error;@@ -25751,29 +25981,14 @@ if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE)) return -1; } else {- int lvalue_label, depth;+ int lvalue_label; if (js_parse_postfix_expr(s, TRUE)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,- &depth, FALSE, TOK_FOR))+ NULL, FALSE, TOK_FOR)) return -1;- /* swap value and lvalue object and store it into lvalue object */- /* enum_rec val [depth] -- enum_rec */- switch(depth) {- case 1:- emit_op(s, OP_swap);- break;- case 2:- emit_op(s, OP_rot3l);- break;- case 3:- emit_op(s, OP_rot4l);- break;- default:- abort();- }- put_lvalue_nokeep(s, opcode, scope, var_name, lvalue_label,- TOK_FOR /* not used */);+ put_lvalue(s, opcode, scope, var_name, lvalue_label,+ PUT_LVALUE_NOKEEP_BOTTOM, FALSE); } var_name = JS_ATOM_NULL; }@@ -26933,32 +27148,20 @@ /* return NULL in case of exception (e.g. module could not be loaded) */ static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,- JSAtom base_module_name,- JSAtom module_name1)+ const char *base_cname,+ const char *cname1) { JSRuntime *rt = ctx->rt; JSModuleDef *m; char *cname;- const char *base_cname, *cname1; JSAtom module_name; - base_cname = JS_AtomToCString(ctx, base_module_name);- if (!base_cname)- return NULL;- cname1 = JS_AtomToCString(ctx, module_name1);- if (!cname1) {- JS_FreeCString(ctx, base_cname);- return NULL;- }- if (!rt->module_normalize_func) { cname = js_default_module_normalize_name(ctx, base_cname, cname1); } else { cname = rt->module_normalize_func(ctx, base_cname, cname1, rt->module_loader_opaque); }- JS_FreeCString(ctx, base_cname);- JS_FreeCString(ctx, cname1); if (!cname) return NULL; @@ -26992,6 +27195,27 @@ return m; } +static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,+ JSAtom base_module_name,+ JSAtom module_name1)+{+ const char *base_cname, *cname;+ JSModuleDef *m;+ + base_cname = JS_AtomToCString(ctx, base_module_name);+ if (!base_cname)+ return NULL;+ cname = JS_AtomToCString(ctx, module_name1);+ if (!cname) {+ JS_FreeCString(ctx, base_cname);+ return NULL;+ }+ m = js_host_resolve_imported_module(ctx, base_cname, cname);+ JS_FreeCString(ctx, base_cname);+ JS_FreeCString(ctx, cname);+ return m;+}+ typedef struct JSResolveEntry { JSModuleDef *module; JSAtom name;@@ -27435,8 +27659,8 @@ /* resolve each requested module */ for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i];- m1 = js_host_resolve_imported_module(ctx, m->module_name,- rme->module_name);+ m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,+ rme->module_name); if (!m1) return -1; rme->module = m1;@@ -27711,8 +27935,9 @@ return -1; } -/* warning: the returned atom is not allocated */-static JSAtom js_get_script_or_module_name(JSContext *ctx)+/* return JS_ATOM_NULL if the name cannot be found. Only works with+ not striped bytecode functions. */+JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) { JSStackFrame *sf; JSFunctionBytecode *b;@@ -27721,14 +27946,22 @@ function. It does not work for eval(). Need to add a ScriptOrModule info in JSFunctionBytecode */ sf = ctx->rt->current_stack_frame;- assert(sf != NULL);- assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);+ if (!sf)+ return JS_ATOM_NULL;+ while (n_stack_levels-- > 0) {+ sf = sf->prev_frame;+ if (!sf)+ return JS_ATOM_NULL;+ }+ if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)+ return JS_ATOM_NULL; p = JS_VALUE_GET_OBJ(sf->cur_func);- assert(js_class_has_bytecode(p->class_id));+ if (!js_class_has_bytecode(p->class_id))+ return JS_ATOM_NULL; b = p->u.func.function_bytecode; if (!b->has_debug) return JS_ATOM_NULL;- return b->debug.filename;+ return JS_DupAtom(ctx, b->debug.filename); } JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)@@ -27755,13 +27988,14 @@ JSAtom filename; JSModuleDef *m; - filename = js_get_script_or_module_name(ctx);+ filename = JS_GetScriptOrModuleName(ctx, 0); if (filename == JS_ATOM_NULL) goto fail; /* XXX: inefficient, need to add a module or script pointer in JSFunctionBytecode */ m = js_find_loaded_module(ctx, filename);+ JS_FreeAtom(ctx, filename); if (!m) { fail: JS_ThrowTypeError(ctx, "import.meta not supported in this context");@@ -27770,6 +28004,31 @@ return JS_GetImportMeta(ctx, m); } +/* used by os.Worker() and import() */+JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,+ const char *filename)+{+ JSModuleDef *m;+ JSValue ret, func_obj;+ + m = js_host_resolve_imported_module(ctx, basename, filename);+ if (!m)+ return NULL;+ + if (js_resolve_module(ctx, m) < 0) {+ js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);+ return NULL;+ }++ /* Evaluate the module code */+ func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));+ ret = JS_EvalFunction(ctx, func_obj);+ if (JS_IsException(ret))+ return NULL;+ JS_FreeValue(ctx, ret);+ return m;+}+ static JSValue js_dynamic_import_job(JSContext *ctx, int argc, JSValueConst *argv) {@@ -27777,53 +28036,36 @@ JSValueConst basename_val = argv[2]; JSValueConst specifier = argv[3]; JSModuleDef *m;- JSAtom basename = JS_ATOM_NULL, filename;- JSValue specifierString, ret, func_obj, err, ns;+ const char *basename = NULL, *filename;+ JSValue ret, err, ns; if (!JS_IsString(basename_val)) { JS_ThrowTypeError(ctx, "no function filename for import()"); goto exception; }- basename = JS_ValueToAtom(ctx, basename_val);- if (basename == JS_ATOM_NULL)+ basename = JS_ToCString(ctx, basename_val);+ if (!basename) goto exception; - specifierString = JS_ToString(ctx, specifier);- if (JS_IsException(specifierString))- goto exception;- filename = JS_ValueToAtom(ctx, specifierString);- JS_FreeValue(ctx, specifierString);- if (filename == JS_ATOM_NULL)+ filename = JS_ToCString(ctx, specifier);+ if (!filename) goto exception; - m = js_host_resolve_imported_module(ctx, basename, filename);- JS_FreeAtom(ctx, filename);- if (!m) {- goto exception;- }- - if (js_resolve_module(ctx, m) < 0) {- js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);+ m = JS_RunModule(ctx, basename, filename);+ JS_FreeCString(ctx, filename);+ if (!m) goto exception;- } - /* Evaluate the module code */- func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));- ret = JS_EvalFunction(ctx, func_obj);- if (JS_IsException(ret))- goto exception;- JS_FreeValue(ctx, ret);- /* return the module namespace */ ns = js_get_module_ns(ctx, m);- if (JS_IsException(ret))+ if (JS_IsException(ns)) goto exception; ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, 1, (JSValueConst *)&ns); JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ JS_FreeValue(ctx, ns);- JS_FreeAtom(ctx, basename);+ JS_FreeCString(ctx, basename); return JS_UNDEFINED; exception: @@ -27832,7 +28074,7 @@ 1, (JSValueConst *)&err); JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ JS_FreeValue(ctx, err);- JS_FreeAtom(ctx, basename);+ JS_FreeCString(ctx, basename); return JS_UNDEFINED; } @@ -27842,11 +28084,12 @@ JSValue promise, resolving_funcs[2], basename_val; JSValueConst args[4]; - basename = js_get_script_or_module_name(ctx);+ basename = JS_GetScriptOrModuleName(ctx, 0); if (basename == JS_ATOM_NULL) basename_val = JS_NULL; else basename_val = JS_AtomToValue(ctx, basename);+ JS_FreeAtom(ctx, basename); if (JS_IsException(basename_val)) return basename_val; @@ -28957,7 +29200,7 @@ return (bc_buf[pos + 1] == OP_put_ref_value && (opcode == OP_insert3 || opcode == OP_perm4 ||- //opcode == OP_nop ||+ opcode == OP_nop || opcode == OP_rot3l)); } @@ -28967,7 +29210,7 @@ return (bc_buf[pos + 1] == OP_put_ref_value && (opcode == OP_insert3 || opcode == OP_perm4 ||- //opcode == OP_nop ||+ opcode == OP_nop || opcode == OP_rot3l)); } @@ -29049,20 +29292,22 @@ end_pos = label_pos + 2; op = bc_buf[label_pos]; if (is_strict) {- switch(op) {- case OP_insert3:- op = OP_insert2;- break;- case OP_perm4:- op = OP_perm3;- break;- case OP_rot3l:- op = OP_swap;- break;- default:- abort();+ if (op != OP_nop) {+ switch(op) {+ case OP_insert3:+ op = OP_insert2;+ break;+ case OP_perm4:+ op = OP_perm3;+ break;+ case OP_rot3l:+ op = OP_swap;+ break;+ default:+ abort();+ }+ bc_buf[pos++] = op; }- bc_buf[pos++] = op; } else { if (op == OP_insert3) bc_buf[pos++] = OP_dup;@@ -31567,20 +31812,20 @@ } goto no_change; +#if SHORT_OPCODES case OP_typeof: if (OPTIMIZE) { /* simplify typeof tests */ if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) { if (cc.line_num >= 0) line_num = cc.line_num; int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;-#if SHORT_OPCODES int op2 = -1; switch (cc.atom) { case JS_ATOM_undefined:- op2 = OP_is_undefined;+ op2 = OP_typeof_is_undefined; break; case JS_ATOM_function:- op2 = OP_is_function;+ op2 = OP_typeof_is_function; break; } if (op2 >= 0) {@@ -31604,19 +31849,10 @@ goto has_label; } }-#endif- if (cc.atom == JS_ATOM_undefined) {- /* transform typeof(s) == "undefined" into s === void 0 */- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_undefined);- dbuf_putc(&bc_out, op1);- JS_FreeAtom(ctx, cc.atom);- pos_next = cc.pos;- break;- } } } goto no_change;+#endif default: no_change:@@ -37067,10 +37303,9 @@ int argc, JSValueConst *argv) { JSBoundFunction *bf;- JSValue func_obj, name1;+ JSValue func_obj, name1, len_val; JSObject *p;- int arg_count, i;- uint32_t len1;+ int arg_count, i, ret; if (check_function(ctx, this_val)) return JS_EXCEPTION;@@ -37093,6 +37328,44 @@ } p->u.bound_function = bf; + /* XXX: the spec could be simpler by only using GetOwnProperty */+ ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);+ if (ret < 0)+ goto exception;+ if (!ret) {+ len_val = JS_NewInt32(ctx, 0);+ } else {+ len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);+ if (JS_IsException(len_val))+ goto exception;+ if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {+ /* most common case */+ int len1 = JS_VALUE_GET_INT(len_val);+ if (len1 <= arg_count)+ len1 = 0;+ else+ len1 -= arg_count;+ len_val = JS_NewInt32(ctx, len1);+ } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {+ double d = JS_VALUE_GET_FLOAT64(len_val);+ if (isnan(d)) {+ d = 0.0;+ } else {+ d = trunc(d);+ if (d <= (double)arg_count)+ d = 0.0;+ else+ d -= (double)arg_count; /* also converts -0 to +0 */+ }+ len_val = JS_NewFloat64(ctx, d);+ } else {+ JS_FreeValue(ctx, len_val);+ len_val = JS_NewInt32(ctx, 0);+ }+ }+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,+ len_val, JS_PROP_CONFIGURABLE);+ name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name); if (JS_IsException(name1)) goto exception;@@ -37105,15 +37378,6 @@ goto exception; JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1, JS_PROP_CONFIGURABLE);- if (js_get_length32(ctx, &len1, this_val))- goto exception;- if (len1 <= (uint32_t)arg_count)- len1 = 0;- else- len1 -= arg_count;- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,- JS_NewUint32(ctx, len1),- JS_PROP_CONFIGURABLE); return func_obj; exception: JS_FreeValue(ctx, func_obj);@@ -40455,10 +40719,6 @@ len = p->len; if (len >= n) return str;- if (n > JS_STRING_LEN_MAX) {- JS_ThrowInternalError(ctx, "string too long");- goto fail2;- } if (argc > 1 && !JS_IsUndefined(argv[1])) { v = JS_ToString(ctx, argv[1]); if (JS_IsException(v))@@ -40473,6 +40733,10 @@ p1 = NULL; } }+ if (n > JS_STRING_LEN_MAX) {+ JS_ThrowInternalError(ctx, "string too long");+ goto fail2;+ } if (string_buffer_init(ctx, b, n)) goto fail3; n -= len;@@ -41932,14 +42196,11 @@ if (JS_IsException(obj)) goto fail; prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;- group_name_ptr = NULL;- if (re_flags & LRE_FLAG_NAMED_GROUPS) {- uint32_t re_bytecode_len;+ group_name_ptr = lre_get_groupnames(re_bytecode);+ if (group_name_ptr) { groups = JS_NewObjectProto(ctx, JS_NULL); if (JS_IsException(groups)) goto fail;- re_bytecode_len = get_u32(re_bytecode + 3);- group_name_ptr = (char *)(re_bytecode + 7 + re_bytecode_len); } for(i = 0; i < capture_count; i++) {@@ -43724,6 +43985,7 @@ JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ), JS_CFUNC_DEF("set", 3, js_reflect_set ), JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ), }; static const JSCFunctionListEntry js_reflect_obj[] = {
quickjs/quickjs.h view
@@ -412,6 +412,8 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); /* atom support */+#define JS_ATOM_NULL 0+ JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len); JSAtom JS_NewAtom(JSContext *ctx, const char *str); JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n);@@ -835,6 +837,8 @@ void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque); /* if can_block is TRUE, Atomics.wait() can be used */ void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);+/* set the [IsHTMLDDA] internal slot */+void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj); typedef struct JSModuleDef JSModuleDef; @@ -885,6 +889,12 @@ /* load the dependencies of the module 'obj'. Useful when JS_ReadObject() returns a module. */ int JS_ResolveModule(JSContext *ctx, JSValueConst obj);++/* only exported for os.Worker() */+JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels);+/* only exported for os.Worker() */+JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,+ const char *filename); /* C function definition */ typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */