packages feed

quickjs-hs 0.1.2.1 → 0.1.2.2

raw patch · 12 files changed

+709/−374 lines, 12 files

Files

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 @@ ![Tests Cabal](https://github.com/goodlyrottenapple/quickjs-hs/workflows/Tests%20Cabal/badge.svg)  -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 */