quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

run-test262.c (76325B)


      1 /*
      2  * ECMA Test 262 Runner for QuickJS
      3  *
      4  * Copyright (c) 2017-2021 Fabrice Bellard
      5  * Copyright (c) 2017-2021 Charlie Gordon
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a copy
      8  * of this software and associated documentation files (the "Software"), to deal
      9  * in the Software without restriction, including without limitation the rights
     10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11  * copies of the Software, and to permit persons to whom the Software is
     12  * furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be included in
     15  * all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     23  * THE SOFTWARE.
     24  */
     25 #include <stdlib.h>
     26 #include <stdio.h>
     27 #include <stdarg.h>
     28 #include <inttypes.h>
     29 #include <string.h>
     30 #include <assert.h>
     31 #include <ctype.h>
     32 #include <unistd.h>
     33 #include <errno.h>
     34 #include <time.h>
     35 #include <dirent.h>
     36 #include <ftw.h>
     37 #include <stdatomic.h>
     38 #include <pthread.h>
     39 #ifdef _WIN32
     40 #include <windows.h>
     41 #endif
     42 
     43 #include "cutils.h"
     44 #include "list.h"
     45 #include "quickjs-libc.h"
     46 
     47 #define CMD_NAME "run-test262"
     48 
     49 typedef struct namelist_t {
     50     char **array;
     51     int count;
     52     int size;
     53 } namelist_t;
     54 
     55 /* per execution thread context */
     56 typedef struct {
     57     pthread_mutex_t agent_mutex;
     58     pthread_cond_t agent_cond;
     59     /* list of Test262Agent.link */
     60     struct list_head agent_list;
     61 
     62     pthread_mutex_t report_mutex;
     63     /* list of AgentReport.link */
     64     struct list_head report_list;
     65 
     66     int async_done;
     67 } ThreadLocalStorage;
     68 
     69 typedef struct {
     70     struct list_head link;
     71     ThreadLocalStorage *tls;
     72     pthread_t tid;
     73     char *script;
     74     JSValue broadcast_func;
     75     BOOL broadcast_pending;
     76     JSValue broadcast_sab; /* in the main context */
     77     uint8_t *broadcast_sab_buf;
     78     size_t broadcast_sab_size;
     79     int32_t broadcast_val;
     80 } Test262Agent;
     81 
     82 typedef struct {
     83     struct list_head link;
     84     char *str;
     85 } AgentReport;
     86 
     87 namelist_t test_list;
     88 namelist_t exclude_list;
     89 namelist_t exclude_dir_list;
     90 
     91 int nthreads;
     92 pthread_t progress_thread;
     93 BOOL progress_exit_request;
     94 pthread_cond_t progress_cond;
     95 pthread_mutex_t progress_mutex;
     96 
     97 FILE *outfile;
     98 enum test_mode_t {
     99     TEST_DEFAULT_NOSTRICT, /* run tests as nostrict unless test is flagged as strictonly */
    100     TEST_DEFAULT_STRICT,   /* run tests as strict unless test is flagged as nostrict */
    101     TEST_NOSTRICT,         /* run tests as nostrict, skip strictonly tests */
    102     TEST_STRICT,           /* run tests as strict, skip nostrict tests */
    103     TEST_ALL,              /* run tests in both strict and nostrict, unless restricted by spec */
    104 } test_mode = TEST_DEFAULT_NOSTRICT;
    105 int compact;
    106 int show_timings;
    107 int skip_async;
    108 int skip_module;
    109 int new_style;
    110 int dump_memory;
    111 int stats_count;
    112 JSMemoryUsage stats_all, stats_avg, stats_min, stats_max;
    113 char *stats_min_filename;
    114 char *stats_max_filename;
    115 pthread_mutex_t stats_mutex;
    116 int verbose;
    117 char *harness_dir;
    118 char *harness_exclude;
    119 char *harness_features;
    120 char *harness_skip_features;
    121 int *harness_skip_features_count;
    122 char *error_filename;
    123 char *error_file;
    124 FILE *error_out;
    125 char *report_filename;
    126 int update_errors;
    127 int slow_test_threshold;
    128 int start_index, stop_index;
    129 int test_excluded;
    130 _Atomic int test_count, test_failed, test_skipped;
    131 _Atomic int new_errors, changed_errors, fixed_errors;
    132 
    133 void warning(const char *, ...) __attribute__((__format__(__printf__, 1, 2)));
    134 void fatal(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3)));
    135 
    136 void atomic_inc(volatile _Atomic int *p)
    137 {
    138     atomic_fetch_add(p, 1);
    139 }
    140 
    141 #if defined(_WIN32)
    142 static int cpu_count(void)
    143 {
    144     DWORD_PTR procmask, sysmask;
    145     long count;
    146     int i;
    147 
    148     count = 0;
    149     if (GetProcessAffinityMask(GetCurrentProcess(), &procmask, &sysmask))
    150         for (i = 0; i < 8 * sizeof(procmask); i++)
    151             count += 1 & (procmask >> i);
    152     return count;
    153 }
    154 #elif defined(__linux__)
    155 /* return the number of available physical cores or -1 if not available */
    156 static int get_cpu_info_physical_cores(void)
    157 {
    158     FILE *f;
    159     int nb_cores, physical_id;
    160     char line[1024], *p;
    161     char *field, *value;
    162     int len;
    163     
    164     f = fopen("/proc/cpuinfo", "rb");
    165     if (!f)
    166         return -1;
    167     nb_cores = 0;
    168     physical_id = -1;
    169     for(;;) {
    170         if (fgets(line, sizeof(line), f) == NULL)
    171             break;
    172         len = strlen(line);
    173         while (len > 0 && isspace(line[len - 1]))
    174             len--;
    175         line[len] = '\0';
    176         field = line;
    177         p = line;
    178         if (*p == '#')
    179             continue;
    180         while (*p != ':' && *p != '\0')
    181             p++;
    182         if (*p == '\0')
    183             continue;
    184         *p = '\0';
    185         p++;
    186         while (isspace(*p))
    187             p++;
    188         value = p;
    189         
    190         len = strlen(field);
    191         while (len > 0 && isspace(field[len - 1]))
    192             len--;
    193         field[len] = '\0';
    194 
    195         //        printf("'%s' '%s'\n", field, value);
    196         if (!strcmp(field, "cpu cores")) {
    197             if (nb_cores == 0) {
    198                 nb_cores = strtol(value, NULL, 0);
    199             }
    200         } else if (!strcmp(field, "physical id")) {
    201             physical_id = max_int(physical_id, strtol(value, NULL, 0));
    202         }
    203     }
    204     fclose(f);
    205     //    printf("nb_cores=%d physical_id=%d\n", nb_cores, physical_id);
    206     if (nb_cores <= 0 || physical_id < 0)
    207         return -1;
    208     return nb_cores * (physical_id + 1);
    209 }
    210 
    211 static int cpu_count(void)
    212 {
    213     int n = get_cpu_info_physical_cores();
    214     if (n <= 0)
    215         n = 1;
    216     return n;
    217 }
    218 #else /* __linux__ */
    219 static int cpu_count(void)
    220 {
    221     return sysconf(_SC_NPROCESSORS_ONLN);
    222 }
    223 #endif /* !__linux__ */
    224 
    225 static void init_thread_local_storage(ThreadLocalStorage *tls)
    226 {
    227     memset(tls, 0, sizeof(*tls));
    228     pthread_mutex_init(&tls->agent_mutex, NULL);
    229     pthread_cond_init(&tls->agent_cond, NULL);
    230     init_list_head(&tls->agent_list);
    231 
    232     pthread_mutex_init(&tls->report_mutex, NULL);
    233     init_list_head(&tls->report_list);
    234 }
    235 
    236 void warning(const char *fmt, ...)
    237 {
    238     va_list ap;
    239 
    240     fflush(stdout);
    241     fprintf(stderr, "%s: ", CMD_NAME);
    242     va_start(ap, fmt);
    243     vfprintf(stderr, fmt, ap);
    244     va_end(ap);
    245     fputc('\n', stderr);
    246 }
    247 
    248 void fatal(int errcode, const char *fmt, ...)
    249 {
    250     va_list ap;
    251 
    252     fflush(stdout);
    253     fprintf(stderr, "%s: ", CMD_NAME);
    254     va_start(ap, fmt);
    255     vfprintf(stderr, fmt, ap);
    256     va_end(ap);
    257     fputc('\n', stderr);
    258 
    259     exit(errcode);
    260 }
    261 
    262 void perror_exit(int errcode, const char *s)
    263 {
    264     fflush(stdout);
    265     fprintf(stderr, "%s: ", CMD_NAME);
    266     perror(s);
    267     exit(errcode);
    268 }
    269 
    270 char *strdup_len(const char *str, int len)
    271 {
    272     char *p = malloc(len + 1);
    273     memcpy(p, str, len);
    274     p[len] = '\0';
    275     return p;
    276 }
    277 
    278 static inline int str_equal(const char *a, const char *b) {
    279     return !strcmp(a, b);
    280 }
    281 
    282 char *str_append(char **pp, const char *sep, const char *str) {
    283     char *res, *p;
    284     size_t len = 0;
    285     p = *pp;
    286     if (p) {
    287         len = strlen(p) + strlen(sep);
    288     }
    289     res = malloc(len + strlen(str) + 1);
    290     if (p) {
    291         strcpy(res, p);
    292         strcat(res, sep);
    293     }
    294     strcpy(res + len, str);
    295     free(p);
    296     return *pp = res;
    297 }
    298 
    299 char *str_strip(char *p)
    300 {
    301     size_t len = strlen(p);
    302     while (len > 0 && isspace((unsigned char)p[len - 1]))
    303         p[--len] = '\0';
    304     while (isspace((unsigned char)*p))
    305         p++;
    306     return p;
    307 }
    308 
    309 int has_prefix(const char *str, const char *prefix)
    310 {
    311     return !strncmp(str, prefix, strlen(prefix));
    312 }
    313 
    314 char *skip_prefix(const char *str, const char *prefix)
    315 {
    316     int i;
    317     for (i = 0;; i++) {
    318         if (prefix[i] == '\0') {  /* skip the prefix */
    319             str += i;
    320             break;
    321         }
    322         if (str[i] != prefix[i])
    323             break;
    324     }
    325     return (char *)str;
    326 }
    327 
    328 char *get_basename(const char *filename)
    329 {
    330     char *p;
    331 
    332     p = strrchr(filename, '/');
    333     if (!p)
    334         return NULL;
    335     return strdup_len(filename, p - filename);
    336 }
    337 
    338 char *compose_path(const char *path, const char *name)
    339 {
    340     int path_len, name_len;
    341     char *d, *q;
    342 
    343     if (!path || path[0] == '\0' || *name == '/') {
    344         d = strdup(name);
    345     } else {
    346         path_len = strlen(path);
    347         name_len = strlen(name);
    348         d = malloc(path_len + 1 + name_len + 1);
    349         if (d) {
    350             q = d;
    351             memcpy(q, path, path_len);
    352             q += path_len;
    353             if (path[path_len - 1] != '/')
    354                 *q++ = '/';
    355             memcpy(q, name, name_len + 1);
    356         }
    357     }
    358     return d;
    359 }
    360 
    361 int namelist_cmp(const char *a, const char *b)
    362 {
    363     /* compare strings in modified lexicographical order */
    364     for (;;) {
    365         int ca = (unsigned char)*a++;
    366         int cb = (unsigned char)*b++;
    367         if (isdigit(ca) && isdigit(cb)) {
    368             int na = ca - '0';
    369             int nb = cb - '0';
    370             while (isdigit(ca = (unsigned char)*a++))
    371                 na = na * 10 + ca - '0';
    372             while (isdigit(cb = (unsigned char)*b++))
    373                 nb = nb * 10 + cb - '0';
    374             if (na < nb)
    375                 return -1;
    376             if (na > nb)
    377                 return +1;
    378         }
    379         if (ca < cb)
    380             return -1;
    381         if (ca > cb)
    382             return +1;
    383         if (ca == '\0')
    384             return 0;
    385     }
    386 }
    387 
    388 int namelist_cmp_indirect(const void *a, const void *b)
    389 {
    390     return namelist_cmp(*(const char **)a, *(const char **)b);
    391 }
    392 
    393 void namelist_sort(namelist_t *lp)
    394 {
    395     int i, count;
    396     if (lp->count > 1) {
    397         qsort(lp->array, lp->count, sizeof(*lp->array), namelist_cmp_indirect);
    398         /* remove duplicates */
    399         for (count = i = 1; i < lp->count; i++) {
    400             if (namelist_cmp(lp->array[count - 1], lp->array[i]) == 0) {
    401                 free(lp->array[i]);
    402             } else {
    403                 lp->array[count++] = lp->array[i];
    404             }
    405         }
    406         lp->count = count;
    407     }
    408 }
    409 
    410 /* the list must be sorted */
    411 int namelist_find(const namelist_t *lp, const char *name)
    412 {
    413     int a, b, m, cmp;
    414 
    415     for (a = 0, b = lp->count; a < b;) {
    416         m = a + (b - a) / 2;
    417         cmp = namelist_cmp(lp->array[m], name);
    418         if (cmp < 0)
    419             a = m + 1;
    420         else if (cmp > 0)
    421             b = m;
    422         else
    423             return m;
    424     }
    425     return -1;
    426 }
    427 
    428 void namelist_add(namelist_t *lp, const char *base, const char *name)
    429 {
    430     char *s;
    431 
    432     s = compose_path(base, name);
    433     if (!s)
    434         goto fail;
    435     if (lp->count == lp->size) {
    436         size_t newsize = lp->size + (lp->size >> 1) + 4;
    437         char **a = realloc(lp->array, sizeof(lp->array[0]) * newsize);
    438         if (!a)
    439             goto fail;
    440         lp->array = a;
    441         lp->size = newsize;
    442     }
    443     lp->array[lp->count] = s;
    444     lp->count++;
    445     return;
    446 fail:
    447     fatal(1, "allocation failure\n");
    448 }
    449 
    450 void namelist_load(namelist_t *lp, const char *filename)
    451 {
    452     char buf[1024];
    453     char *base_name;
    454     FILE *f;
    455 
    456     f = fopen(filename, "rb");
    457     if (!f) {
    458         perror_exit(1, filename);
    459     }
    460     base_name = get_basename(filename);
    461 
    462     while (fgets(buf, sizeof(buf), f) != NULL) {
    463         char *p = str_strip(buf);
    464         if (*p == '#' || *p == ';' || *p == '\0')
    465             continue;  /* line comment */
    466 
    467         namelist_add(lp, base_name, p);
    468     }
    469     free(base_name);
    470     fclose(f);
    471 }
    472 
    473 void namelist_add_from_error_file(namelist_t *lp, const char *file)
    474 {
    475     const char *p, *p0;
    476     char *pp;
    477 
    478     for (p = file; (p = strstr(p, ".js:")) != NULL; p++) {
    479         for (p0 = p; p0 > file && p0[-1] != '\n'; p0--)
    480             continue;
    481         pp = strdup_len(p0, p + 3 - p0);
    482         namelist_add(lp, NULL, pp);
    483         free(pp);
    484     }
    485 }
    486 
    487 void namelist_free(namelist_t *lp)
    488 {
    489     while (lp->count > 0) {
    490         free(lp->array[--lp->count]);
    491     }
    492     free(lp->array);
    493     lp->array = NULL;
    494     lp->size = 0;
    495 }
    496 
    497 static int add_test_file(const char *filename, const struct stat *ptr, int flag)
    498 {
    499     namelist_t *lp = &test_list;
    500     if (has_suffix(filename, ".js") && !has_suffix(filename, "_FIXTURE.js"))
    501         namelist_add(lp, NULL, filename);
    502     return 0;
    503 }
    504 
    505 /* find js files from the directory tree and sort the list */
    506 static void enumerate_tests(const char *path)
    507 {
    508     namelist_t *lp = &test_list;
    509     int start = lp->count;
    510     ftw(path, add_test_file, 100);
    511     qsort(lp->array + start, lp->count - start, sizeof(*lp->array),
    512           namelist_cmp_indirect);
    513 }
    514 
    515 static void js_print_value_write(void *opaque, const char *buf, size_t len)
    516 {
    517     FILE *fo = opaque;
    518     fwrite(buf, 1, len, fo);
    519 }
    520 
    521 static JSValue js_print(JSContext *ctx, JSValueConst this_val,
    522                         int argc, JSValueConst *argv)
    523 {
    524     ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
    525     int i;
    526     JSValueConst v;
    527     
    528     for (i = 0; i < argc; i++) {
    529         if (i != 0 && outfile)
    530             fputc(' ', outfile);
    531         v = argv[i];
    532         if (JS_IsString(v)) {
    533             const char *str;
    534             size_t len;
    535             str = JS_ToCStringLen(ctx, &len, v);
    536             if (!str)
    537                 return JS_EXCEPTION;
    538             if (!strcmp(str, "Test262:AsyncTestComplete")) {
    539                 tls->async_done++;
    540             } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) {
    541                 tls->async_done = 2; /* force an error */
    542             }
    543             if (outfile) {
    544                 fwrite(str, 1, len, outfile);
    545             }
    546             JS_FreeCString(ctx, str);
    547         } else {
    548             if (outfile) {
    549                 JS_PrintValue(ctx, js_print_value_write, outfile, v, NULL);
    550             }
    551         }
    552     }
    553     if (outfile)
    554         fputc('\n', outfile);
    555     return JS_UNDEFINED;
    556 }
    557 
    558 static JSValue js_detachArrayBuffer(JSContext *ctx, JSValue this_val,
    559                                     int argc, JSValue *argv)
    560 {
    561     JS_DetachArrayBuffer(ctx, argv[0]);
    562     return JS_UNDEFINED;
    563 }
    564 
    565 static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
    566                              int argc, JSValue *argv)
    567 {
    568     const char *str;
    569     size_t len;
    570     JSValue ret;
    571     str = JS_ToCStringLen(ctx, &len, argv[0]);
    572     if (!str)
    573         return JS_EXCEPTION;
    574     ret = JS_Eval(ctx, str, len, "<evalScript>", JS_EVAL_TYPE_GLOBAL);
    575     JS_FreeCString(ctx, str);
    576     return ret;
    577 }
    578 
    579 static JSValue add_helpers1(JSContext *ctx);
    580 static void add_helpers(JSContext *ctx);
    581 
    582 static void *agent_start(void *arg)
    583 {
    584     Test262Agent *agent = arg;
    585     ThreadLocalStorage *tls = agent->tls;
    586     JSRuntime *rt;
    587     JSContext *ctx;
    588     JSValue ret_val;
    589     int ret;
    590 
    591     rt = JS_NewRuntime();
    592     if (rt == NULL) {
    593         fatal(1, "JS_NewRuntime failure");
    594     }
    595     JS_SetRuntimeOpaque(rt, tls);
    596     ctx = JS_NewContext(rt);
    597     if (ctx == NULL) {
    598         JS_FreeRuntime(rt);
    599         fatal(1, "JS_NewContext failure");
    600     }
    601     JS_SetContextOpaque(ctx, agent);
    602     JS_SetRuntimeInfo(rt, "agent");
    603     JS_SetCanBlock(rt, TRUE);
    604 
    605     add_helpers(ctx);
    606     ret_val = JS_Eval(ctx, agent->script, strlen(agent->script),
    607                       "<evalScript>", JS_EVAL_TYPE_GLOBAL);
    608     free(agent->script);
    609     agent->script = NULL;
    610     if (JS_IsException(ret_val))
    611         js_std_dump_error(ctx);
    612     JS_FreeValue(ctx, ret_val);
    613 
    614     for(;;) {
    615         ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), NULL);
    616         if (ret < 0) {
    617             js_std_dump_error(ctx);
    618             break;
    619         } else if (ret == 0) {
    620             if (JS_IsUndefined(agent->broadcast_func)) {
    621                 break;
    622             } else {
    623                 JSValue args[2];
    624 
    625                 pthread_mutex_lock(&tls->agent_mutex);
    626                 while (!agent->broadcast_pending) {
    627                     pthread_cond_wait(&tls->agent_cond, &tls->agent_mutex);
    628                 }
    629 
    630                 agent->broadcast_pending = FALSE;
    631                 pthread_cond_signal(&tls->agent_cond);
    632 
    633                 pthread_mutex_unlock(&tls->agent_mutex);
    634 
    635                 args[0] = JS_NewArrayBuffer(ctx, agent->broadcast_sab_buf,
    636                                             agent->broadcast_sab_size,
    637                                             NULL, NULL, TRUE);
    638                 args[1] = JS_NewInt32(ctx, agent->broadcast_val);
    639                 ret_val = JS_Call(ctx, agent->broadcast_func, JS_UNDEFINED,
    640                                   2, (JSValueConst *)args);
    641                 JS_FreeValue(ctx, args[0]);
    642                 JS_FreeValue(ctx, args[1]);
    643                 if (JS_IsException(ret_val))
    644                     js_std_dump_error(ctx);
    645                 JS_FreeValue(ctx, ret_val);
    646                 JS_FreeValue(ctx, agent->broadcast_func);
    647                 agent->broadcast_func = JS_UNDEFINED;
    648             }
    649         }
    650     }
    651     JS_FreeValue(ctx, agent->broadcast_func);
    652 
    653     JS_FreeContext(ctx);
    654     JS_FreeRuntime(rt);
    655     return NULL;
    656 }
    657 
    658 static JSValue js_agent_start(JSContext *ctx, JSValue this_val,
    659                               int argc, JSValue *argv)
    660 {
    661     ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
    662     const char *script;
    663     Test262Agent *agent;
    664     pthread_attr_t attr;
    665 
    666     if (JS_GetContextOpaque(ctx) != NULL)
    667         return JS_ThrowTypeError(ctx, "cannot be called inside an agent");
    668 
    669     script = JS_ToCString(ctx, argv[0]);
    670     if (!script)
    671         return JS_EXCEPTION;
    672     agent = malloc(sizeof(*agent));
    673     memset(agent, 0, sizeof(*agent));
    674     agent->tls = tls;
    675     agent->broadcast_func = JS_UNDEFINED;
    676     agent->broadcast_sab = JS_UNDEFINED;
    677     agent->script = strdup(script);
    678     JS_FreeCString(ctx, script);
    679     list_add_tail(&agent->link, &tls->agent_list);
    680     pthread_attr_init(&attr);
    681     // musl libc gives threads 80 kb stacks, much smaller than
    682     // JS_DEFAULT_STACK_SIZE (256 kb)
    683     pthread_attr_setstacksize(&attr, 2 << 20); // 2 MB, glibc default
    684     pthread_create(&agent->tid, &attr, agent_start, agent);
    685     pthread_attr_destroy(&attr);
    686     return JS_UNDEFINED;
    687 }
    688 
    689 static void js_agent_free(JSContext *ctx)
    690 {
    691     ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
    692     struct list_head *el, *el1;
    693     Test262Agent *agent;
    694 
    695     list_for_each_safe(el, el1, &tls->agent_list) {
    696         agent = list_entry(el, Test262Agent, link);
    697         pthread_join(agent->tid, NULL);
    698         JS_FreeValue(ctx, agent->broadcast_sab);
    699         list_del(&agent->link);
    700         free(agent);
    701     }
    702 }
    703 
    704 static JSValue js_agent_leaving(JSContext *ctx, JSValue this_val,
    705                                 int argc, JSValue *argv)
    706 {
    707     Test262Agent *agent = JS_GetContextOpaque(ctx);
    708     if (!agent)
    709         return JS_ThrowTypeError(ctx, "must be called inside an agent");
    710     /* nothing to do */
    711     return JS_UNDEFINED;
    712 }
    713 
    714 static BOOL is_broadcast_pending(ThreadLocalStorage *tls)
    715 {
    716     struct list_head *el;
    717     Test262Agent *agent;
    718     list_for_each(el, &tls->agent_list) {
    719         agent = list_entry(el, Test262Agent, link);
    720         if (agent->broadcast_pending)
    721             return TRUE;
    722     }
    723     return FALSE;
    724 }
    725 
    726 static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val,
    727                                   int argc, JSValue *argv)
    728 {
    729     ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
    730     JSValueConst sab = argv[0];
    731     struct list_head *el;
    732     Test262Agent *agent;
    733     uint8_t *buf;
    734     size_t buf_size;
    735     int32_t val;
    736 
    737     if (JS_GetContextOpaque(ctx) != NULL)
    738         return JS_ThrowTypeError(ctx, "cannot be called inside an agent");
    739 
    740     buf = JS_GetArrayBuffer(ctx, &buf_size, sab);
    741     if (!buf)
    742         return JS_EXCEPTION;
    743     if (JS_ToInt32(ctx, &val, argv[1]))
    744         return JS_EXCEPTION;
    745 
    746     /* broadcast the values and wait until all agents have started
    747        calling their callbacks */
    748     pthread_mutex_lock(&tls->agent_mutex);
    749     list_for_each(el, &tls->agent_list) {
    750         agent = list_entry(el, Test262Agent, link);
    751         agent->broadcast_pending = TRUE;
    752         /* the shared array buffer is used by the thread, so increment
    753            its refcount */
    754         agent->broadcast_sab = JS_DupValue(ctx, sab);
    755         agent->broadcast_sab_buf = buf;
    756         agent->broadcast_sab_size = buf_size;
    757         agent->broadcast_val = val;
    758     }
    759     pthread_cond_broadcast(&tls->agent_cond);
    760 
    761     while (is_broadcast_pending(tls)) {
    762         pthread_cond_wait(&tls->agent_cond, &tls->agent_mutex);
    763     }
    764     pthread_mutex_unlock(&tls->agent_mutex);
    765     return JS_UNDEFINED;
    766 }
    767 
    768 static JSValue js_agent_receiveBroadcast(JSContext *ctx, JSValue this_val,
    769                                          int argc, JSValue *argv)
    770 {
    771     Test262Agent *agent = JS_GetContextOpaque(ctx);
    772     if (!agent)
    773         return JS_ThrowTypeError(ctx, "must be called inside an agent");
    774     if (!JS_IsFunction(ctx, argv[0]))
    775         return JS_ThrowTypeError(ctx, "expecting function");
    776     JS_FreeValue(ctx, agent->broadcast_func);
    777     agent->broadcast_func = JS_DupValue(ctx, argv[0]);
    778     return JS_UNDEFINED;
    779 }
    780 
    781 static JSValue js_agent_sleep(JSContext *ctx, JSValue this_val,
    782                               int argc, JSValue *argv)
    783 {
    784     uint32_t duration;
    785     if (JS_ToUint32(ctx, &duration, argv[0]))
    786         return JS_EXCEPTION;
    787     usleep(duration * 1000);
    788     return JS_UNDEFINED;
    789 }
    790 
    791 static int64_t get_clock_ms(void)
    792 {
    793     struct timespec ts;
    794     clock_gettime(CLOCK_MONOTONIC, &ts);
    795     return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000);
    796 }
    797 
    798 static JSValue js_agent_monotonicNow(JSContext *ctx, JSValue this_val,
    799                                      int argc, JSValue *argv)
    800 {
    801     return JS_NewInt64(ctx, get_clock_ms());
    802 }
    803 
    804 static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val,
    805                                   int argc, JSValue *argv)
    806 {
    807     ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
    808     AgentReport *rep;
    809     JSValue ret;
    810 
    811     pthread_mutex_lock(&tls->report_mutex);
    812     if (list_empty(&tls->report_list)) {
    813         rep = NULL;
    814     } else {
    815         rep = list_entry(tls->report_list.next, AgentReport, link);
    816         list_del(&rep->link);
    817     }
    818     pthread_mutex_unlock(&tls->report_mutex);
    819     if (rep) {
    820         ret = JS_NewString(ctx, rep->str);
    821         free(rep->str);
    822         free(rep);
    823     } else {
    824         ret = JS_NULL;
    825     }
    826     return ret;
    827 }
    828 
    829 static JSValue js_agent_report(JSContext *ctx, JSValue this_val,
    830                                int argc, JSValue *argv)
    831 {
    832     ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
    833     const char *str;
    834     AgentReport *rep;
    835 
    836     str = JS_ToCString(ctx, argv[0]);
    837     if (!str)
    838         return JS_EXCEPTION;
    839     rep = malloc(sizeof(*rep));
    840     rep->str = strdup(str);
    841     JS_FreeCString(ctx, str);
    842 
    843     pthread_mutex_lock(&tls->report_mutex);
    844     list_add_tail(&rep->link, &tls->report_list);
    845     pthread_mutex_unlock(&tls->report_mutex);
    846     return JS_UNDEFINED;
    847 }
    848 
    849 static const JSCFunctionListEntry js_agent_funcs[] = {
    850     /* only in main */
    851     JS_CFUNC_DEF("start", 1, js_agent_start ),
    852     JS_CFUNC_DEF("getReport", 0, js_agent_getReport ),
    853     JS_CFUNC_DEF("broadcast", 2, js_agent_broadcast ),
    854     /* only in agent */
    855     JS_CFUNC_DEF("report", 1, js_agent_report ),
    856     JS_CFUNC_DEF("leaving", 0, js_agent_leaving ),
    857     JS_CFUNC_DEF("receiveBroadcast", 1, js_agent_receiveBroadcast ),
    858     /* in both */
    859     JS_CFUNC_DEF("sleep", 1, js_agent_sleep ),
    860     JS_CFUNC_DEF("monotonicNow", 0, js_agent_monotonicNow ),
    861 };
    862 
    863 static JSValue js_new_agent(JSContext *ctx)
    864 {
    865     JSValue agent;
    866     agent = JS_NewObject(ctx);
    867     JS_SetPropertyFunctionList(ctx, agent, js_agent_funcs,
    868                                countof(js_agent_funcs));
    869     return agent;
    870 }
    871 
    872 static JSValue js_createRealm(JSContext *ctx, JSValue this_val,
    873                               int argc, JSValue *argv)
    874 {
    875     JSContext *ctx1;
    876     JSValue ret;
    877 
    878     ctx1 = JS_NewContext(JS_GetRuntime(ctx));
    879     if (!ctx1)
    880         return JS_ThrowOutOfMemory(ctx);
    881     ret = add_helpers1(ctx1);
    882     /* ctx1 has a refcount so it stays alive */
    883     JS_FreeContext(ctx1);
    884     return ret;
    885 }
    886 
    887 static JSValue js_IsHTMLDDA(JSContext *ctx, JSValue this_val,
    888                             int argc, JSValue *argv)
    889 {
    890     return JS_NULL;
    891 }
    892 
    893 static JSValue js_gc(JSContext *ctx, JSValueConst this_val,
    894                      int argc, JSValueConst *argv)
    895 {
    896     JS_RunGC(JS_GetRuntime(ctx));
    897     return JS_UNDEFINED;
    898 }
    899 
    900 static JSValue add_helpers1(JSContext *ctx)
    901 {
    902     JSValue global_obj;
    903     JSValue obj262, obj;
    904 
    905     global_obj = JS_GetGlobalObject(ctx);
    906 
    907     JS_SetPropertyStr(ctx, global_obj, "print",
    908                       JS_NewCFunction(ctx, js_print, "print", 1));
    909 
    910     /* $262 special object used by the tests */
    911     obj262 = JS_NewObject(ctx);
    912     JS_SetPropertyStr(ctx, obj262, "detachArrayBuffer",
    913                       JS_NewCFunction(ctx, js_detachArrayBuffer,
    914                                       "detachArrayBuffer", 1));
    915     JS_SetPropertyStr(ctx, obj262, "evalScript",
    916                       JS_NewCFunction(ctx, js_evalScript,
    917                                       "evalScript", 1));
    918     JS_SetPropertyStr(ctx, obj262, "codePointRange",
    919                       JS_NewCFunction(ctx, js_string_codePointRange,
    920                                       "codePointRange", 2));
    921     JS_SetPropertyStr(ctx, obj262, "agent", js_new_agent(ctx));
    922 
    923     JS_SetPropertyStr(ctx, obj262, "global",
    924                       JS_DupValue(ctx, global_obj));
    925     JS_SetPropertyStr(ctx, obj262, "createRealm",
    926                       JS_NewCFunction(ctx, js_createRealm,
    927                                       "createRealm", 0));
    928     obj = JS_NewCFunction(ctx, js_IsHTMLDDA, "IsHTMLDDA", 0);
    929     JS_SetIsHTMLDDA(ctx, obj);
    930     JS_SetPropertyStr(ctx, obj262, "IsHTMLDDA", obj);
    931     JS_SetPropertyStr(ctx, obj262, "gc",
    932                       JS_NewCFunction(ctx, js_gc, "gc", 0));
    933 
    934     JS_SetPropertyStr(ctx, global_obj, "$262", JS_DupValue(ctx, obj262));
    935 
    936     JS_FreeValue(ctx, global_obj);
    937     return obj262;
    938 }
    939 
    940 static void add_helpers(JSContext *ctx)
    941 {
    942     JS_FreeValue(ctx, add_helpers1(ctx));
    943 }
    944 
    945 static char *load_file(const char *filename, size_t *lenp)
    946 {
    947     char *buf;
    948     size_t buf_len;
    949     buf = (char *)js_load_file(NULL, &buf_len, filename);
    950     if (!buf)
    951         perror_exit(1, filename);
    952     if (lenp)
    953         *lenp = buf_len;
    954     return buf;
    955 }
    956 
    957 static int json_module_init_test(JSContext *ctx, JSModuleDef *m)
    958 {
    959     JSValue val;
    960     val = JS_GetModulePrivateValue(ctx, m);
    961     JS_SetModuleExport(ctx, m, "default", val);
    962     return 0;
    963 }
    964 
    965 static JSModuleDef *js_module_loader_test(JSContext *ctx,
    966                                           const char *module_name, void *opaque,
    967                                           JSValueConst attributes)
    968 {
    969     size_t buf_len;
    970     uint8_t *buf;
    971     JSModuleDef *m;
    972     char *filename, *slash, path[1024];
    973 
    974     // interpret import("bar.js") from path/to/foo.js as
    975     // import("path/to/bar.js") but leave import("./bar.js") untouched
    976     filename = opaque;
    977     if (!strchr(module_name, '/')) {
    978         slash = strrchr(filename, '/');
    979         if (slash) {
    980             snprintf(path, sizeof(path), "%.*s/%s",
    981                      (int)(slash - filename), filename, module_name);
    982             module_name = path;
    983         }
    984     }
    985 
    986     buf = js_load_file(ctx, &buf_len, module_name);
    987     if (!buf) {
    988         JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
    989                                module_name);
    990         return NULL;
    991     }
    992 
    993     if (js_module_test_json(ctx, attributes) == 1) {
    994         /* compile as JSON */
    995         JSValue val;
    996         val = JS_ParseJSON(ctx, (char *)buf, buf_len, module_name);
    997         js_free(ctx, buf);
    998         if (JS_IsException(val))
    999             return NULL;
   1000         m = JS_NewCModule(ctx, module_name, json_module_init_test);
   1001         if (!m) {
   1002             JS_FreeValue(ctx, val);
   1003             return NULL;
   1004         }
   1005         /* only export the "default" symbol which will contain the JSON object */
   1006         JS_AddModuleExport(ctx, m, "default");
   1007         JS_SetModulePrivateValue(ctx, m, val);
   1008     } else {
   1009         JSValue func_val;
   1010         /* compile the module */
   1011         func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
   1012                            JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
   1013         js_free(ctx, buf);
   1014         if (JS_IsException(func_val))
   1015             return NULL;
   1016         /* the module is already referenced, so we must free it */
   1017         m = JS_VALUE_GET_PTR(func_val);
   1018         JS_FreeValue(ctx, func_val);
   1019     }
   1020     return m;
   1021 }
   1022 
   1023 int is_line_sep(char c)
   1024 {
   1025     return (c == '\0' || c == '\n' || c == '\r');
   1026 }
   1027 
   1028 char *find_line(const char *str, const char *line)
   1029 {
   1030     if (str) {
   1031         const char *p;
   1032         int len = strlen(line);
   1033         for (p = str; (p = strstr(p, line)) != NULL; p += len + 1) {
   1034             if ((p == str || is_line_sep(p[-1])) && is_line_sep(p[len]))
   1035                 return (char *)p;
   1036         }
   1037     }
   1038     return NULL;
   1039 }
   1040 
   1041 int is_word_sep(char c)
   1042 {
   1043     return (c == '\0' || isspace((unsigned char)c) || c == ',');
   1044 }
   1045 
   1046 char *find_word(const char *str, const char *word)
   1047 {
   1048     const char *p;
   1049     int len = strlen(word);
   1050     if (str && len) {
   1051         for (p = str; (p = strstr(p, word)) != NULL; p += len) {
   1052             if ((p == str || is_word_sep(p[-1])) && is_word_sep(p[len]))
   1053                 return (char *)p;
   1054         }
   1055     }
   1056     return NULL;
   1057 }
   1058 
   1059 /* handle exclude directories */
   1060 void update_exclude_dirs(void)
   1061 {
   1062     namelist_t *lp = &test_list;
   1063     namelist_t *ep = &exclude_list;
   1064     namelist_t *dp = &exclude_dir_list;
   1065     char *name;
   1066     int i, j, count;
   1067 
   1068     /* split directories from exclude_list */
   1069     for (count = i = 0; i < ep->count; i++) {
   1070         name = ep->array[i];
   1071         if (has_suffix(name, "/")) {
   1072             namelist_add(dp, NULL, name);
   1073             free(name);
   1074         } else {
   1075             ep->array[count++] = name;
   1076         }
   1077     }
   1078     ep->count = count;
   1079 
   1080     namelist_sort(dp);
   1081 
   1082     /* filter out excluded directories */
   1083     for (count = i = 0; i < lp->count; i++) {
   1084         name = lp->array[i];
   1085         for (j = 0; j < dp->count; j++) {
   1086             if (has_prefix(name, dp->array[j])) {
   1087                 test_excluded++;
   1088                 free(name);
   1089                 name = NULL;
   1090                 break;
   1091             }
   1092         }
   1093         if (name) {
   1094             lp->array[count++] = name;
   1095         }
   1096     }
   1097     lp->count = count;
   1098 }
   1099 
   1100 void load_config(const char *filename, const char *ignore)
   1101 {
   1102     char buf[1024];
   1103     FILE *f;
   1104     char *base_name;
   1105     enum {
   1106         SECTION_NONE = 0,
   1107         SECTION_CONFIG,
   1108         SECTION_EXCLUDE,
   1109         SECTION_FEATURES,
   1110         SECTION_TESTS,
   1111     } section = SECTION_NONE;
   1112     int lineno = 0;
   1113 
   1114     f = fopen(filename, "rb");
   1115     if (!f) {
   1116         perror_exit(1, filename);
   1117     }
   1118     base_name = get_basename(filename);
   1119 
   1120     while (fgets(buf, sizeof(buf), f) != NULL) {
   1121         char *p, *q;
   1122         lineno++;
   1123         p = str_strip(buf);
   1124         if (*p == '#' || *p == ';' || *p == '\0')
   1125             continue;  /* line comment */
   1126 
   1127         if (*p == "[]"[0]) {
   1128             /* new section */
   1129             p++;
   1130             p[strcspn(p, "]")] = '\0';
   1131             if (str_equal(p, "config"))
   1132                 section = SECTION_CONFIG;
   1133             else if (str_equal(p, "exclude"))
   1134                 section = SECTION_EXCLUDE;
   1135             else if (str_equal(p, "features"))
   1136                 section = SECTION_FEATURES;
   1137             else if (str_equal(p, "tests"))
   1138                 section = SECTION_TESTS;
   1139             else
   1140                 section = SECTION_NONE;
   1141             continue;
   1142         }
   1143         q = strchr(p, '=');
   1144         if (q) {
   1145             /* setting: name=value */
   1146             *q++ = '\0';
   1147             q = str_strip(q);
   1148         }
   1149         switch (section) {
   1150         case SECTION_CONFIG:
   1151             if (!q) {
   1152                 printf("%s:%d: syntax error\n", filename, lineno);
   1153                 continue;
   1154             }
   1155             if (strstr(ignore, p)) {
   1156                 printf("%s:%d: ignoring %s=%s\n", filename, lineno, p, q);
   1157                 continue;
   1158             }
   1159             if (str_equal(p, "style")) {
   1160                 new_style = str_equal(q, "new");
   1161                 continue;
   1162             }
   1163             if (str_equal(p, "testdir")) {
   1164                 char *testdir = compose_path(base_name, q);
   1165                 enumerate_tests(testdir);
   1166                 free(testdir);
   1167                 continue;
   1168             }
   1169             if (str_equal(p, "harnessdir")) {
   1170                 harness_dir = compose_path(base_name, q);
   1171                 continue;
   1172             }
   1173             if (str_equal(p, "harnessexclude")) {
   1174                 str_append(&harness_exclude, " ", q);
   1175                 continue;
   1176             }
   1177             if (str_equal(p, "features")) {
   1178                 str_append(&harness_features, " ", q);
   1179                 continue;
   1180             }
   1181             if (str_equal(p, "skip-features")) {
   1182                 str_append(&harness_skip_features, " ", q);
   1183                 continue;
   1184             }
   1185             if (str_equal(p, "mode")) {
   1186                 if (str_equal(q, "default") || str_equal(q, "default-nostrict"))
   1187                     test_mode = TEST_DEFAULT_NOSTRICT;
   1188                 else if (str_equal(q, "default-strict"))
   1189                     test_mode = TEST_DEFAULT_STRICT;
   1190                 else if (str_equal(q, "nostrict"))
   1191                     test_mode = TEST_NOSTRICT;
   1192                 else if (str_equal(q, "strict"))
   1193                     test_mode = TEST_STRICT;
   1194                 else if (str_equal(q, "all") || str_equal(q, "both"))
   1195                     test_mode = TEST_ALL;
   1196                 else
   1197                     fatal(2, "unknown test mode: %s", q);
   1198                 continue;
   1199             }
   1200             if (str_equal(p, "strict")) {
   1201                 if (str_equal(q, "skip") || str_equal(q, "no"))
   1202                     test_mode = TEST_NOSTRICT;
   1203                 continue;
   1204             }
   1205             if (str_equal(p, "nostrict")) {
   1206                 if (str_equal(q, "skip") || str_equal(q, "no"))
   1207                     test_mode = TEST_STRICT;
   1208                 continue;
   1209             }
   1210             if (str_equal(p, "async")) {
   1211                 skip_async = !str_equal(q, "yes");
   1212                 continue;
   1213             }
   1214             if (str_equal(p, "module")) {
   1215                 skip_module = !str_equal(q, "yes");
   1216                 continue;
   1217             }
   1218             if (str_equal(p, "verbose")) {
   1219                 verbose = str_equal(q, "yes");
   1220                 continue;
   1221             }
   1222             if (str_equal(p, "errorfile")) {
   1223                 error_filename = compose_path(base_name, q);
   1224                 continue;
   1225             }
   1226             if (str_equal(p, "excludefile")) {
   1227                 char *path = compose_path(base_name, q);
   1228                 namelist_load(&exclude_list, path);
   1229                 free(path);
   1230                 continue;
   1231             }
   1232             if (str_equal(p, "reportfile")) {
   1233                 report_filename = compose_path(base_name, q);
   1234                 continue;
   1235             }
   1236         case SECTION_EXCLUDE:
   1237             namelist_add(&exclude_list, base_name, p);
   1238             break;
   1239         case SECTION_FEATURES:
   1240             if (!q || str_equal(q, "yes"))
   1241                 str_append(&harness_features, " ", p);
   1242             else
   1243                 str_append(&harness_skip_features, " ", p);
   1244             break;
   1245         case SECTION_TESTS:
   1246             namelist_add(&test_list, base_name, p);
   1247             break;
   1248         default:
   1249             /* ignore settings in other sections */
   1250             break;
   1251         }
   1252     }
   1253     fclose(f);
   1254     free(base_name);
   1255 }
   1256 
   1257 char *find_error(const char *filename, int *pline, int is_strict)
   1258 {
   1259     if (error_file) {
   1260         size_t len = strlen(filename);
   1261         const char *p, *q, *r;
   1262         int line;
   1263 
   1264         for (p = error_file; (p = strstr(p, filename)) != NULL; p += len) {
   1265             if ((p == error_file || p[-1] == '\n' || p[-1] == '(') && p[len] == ':') {
   1266                 q = p + len;
   1267                 line = 1;
   1268                 if (*q == ':') {
   1269                     line = strtol(q + 1, (char**)&q, 10);
   1270                     if (*q == ':')
   1271                         q++;
   1272                 }
   1273                 while (*q == ' ') {
   1274                     q++;
   1275                 }
   1276                 /* check strict mode indicator */
   1277                 if (!strstart(q, "strict mode: ", &q) != !is_strict)
   1278                     continue;
   1279                 r = q = skip_prefix(q, "unexpected error: ");
   1280                 r += strcspn(r, "\n");
   1281                 while (r[0] == '\n' && r[1] && strncmp(r + 1, filename, 8)) {
   1282                     r++;
   1283                     r += strcspn(r, "\n");
   1284                 }
   1285                 if (pline)
   1286                     *pline = line;
   1287                 return strdup_len(q, r - q);
   1288             }
   1289         }
   1290     }
   1291     return NULL;
   1292 }
   1293 
   1294 int skip_comments(const char *str, int line, int *pline)
   1295 {
   1296     const char *p;
   1297     int c;
   1298 
   1299     p = str;
   1300     while ((c = (unsigned char)*p++) != '\0') {
   1301         if (isspace(c)) {
   1302             if (c == '\n')
   1303                 line++;
   1304             continue;
   1305         }
   1306         if (c == '/' && *p == '/') {
   1307             while (*++p && *p != '\n')
   1308                 continue;
   1309             continue;
   1310         }
   1311         if (c == '/' && *p == '*') {
   1312             for (p += 1; *p; p++) {
   1313                 if (*p == '\n') {
   1314                     line++;
   1315                     continue;
   1316                 }
   1317                 if (*p == '*' && p[1] == '/') {
   1318                     p += 2;
   1319                     break;
   1320                 }
   1321             }
   1322             continue;
   1323         }
   1324         break;
   1325     }
   1326     if (pline)
   1327         *pline = line;
   1328 
   1329     return p - str;
   1330 }
   1331 
   1332 int longest_match(const char *str, const char *find, int pos, int *ppos, int line, int *pline)
   1333 {
   1334     int len, maxlen;
   1335 
   1336     maxlen = 0;
   1337 
   1338     if (*find) {
   1339         const char *p;
   1340         for (p = str + pos; *p; p++) {
   1341             if (*p == *find) {
   1342                 for (len = 1; p[len] && p[len] == find[len]; len++)
   1343                     continue;
   1344                 if (len > maxlen) {
   1345                     maxlen = len;
   1346                     if (ppos)
   1347                         *ppos = p - str;
   1348                     if (pline)
   1349                         *pline = line;
   1350                     if (!find[len])
   1351                         break;
   1352                 }
   1353             }
   1354             if (*p == '\n')
   1355                 line++;
   1356         }
   1357     }
   1358     return maxlen;
   1359 }
   1360 
   1361 static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
   1362                     const char *filename, int is_test, int is_negative,
   1363                     const char *error_type, FILE *outfile, int eval_flags,
   1364                     int is_async)
   1365 {
   1366     ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
   1367     JSValue res_val, exception_val;
   1368     int ret, error_line, pos, pos_line;
   1369     BOOL is_error, has_error_line, ret_promise;
   1370     const char *error_name;
   1371 
   1372     pos = skip_comments(buf, 1, &pos_line);
   1373     error_line = pos_line;
   1374     has_error_line = FALSE;
   1375     exception_val = JS_UNDEFINED;
   1376     error_name = NULL;
   1377 
   1378     /* a module evaluation returns a promise */
   1379     ret_promise = ((eval_flags & JS_EVAL_TYPE_MODULE) != 0);
   1380     tls->async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */
   1381 
   1382     res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
   1383 
   1384     if ((is_async || ret_promise) && !JS_IsException(res_val)) {
   1385         JSValue promise = JS_UNDEFINED;
   1386         if (ret_promise) {
   1387             promise = res_val;
   1388         } else {
   1389             JS_FreeValue(ctx, res_val);
   1390         }
   1391         for(;;) {
   1392             ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), NULL);
   1393             if (ret < 0) {
   1394                 res_val = JS_EXCEPTION;
   1395                 break;
   1396             } else if (ret == 0) {
   1397                 if (is_async) {
   1398                     /* test if the test called $DONE() once */
   1399                     if (tls->async_done != 1) {
   1400                         res_val = JS_ThrowTypeError(ctx, "$DONE() not called");
   1401                     } else {
   1402                         res_val = JS_UNDEFINED;
   1403                     }
   1404                 } else {
   1405                     /* check that the returned promise is fulfilled */
   1406                     JSPromiseStateEnum state = JS_PromiseState(ctx, promise);
   1407                     if (state == JS_PROMISE_FULFILLED)
   1408                         res_val = JS_UNDEFINED;
   1409                     else if (state == JS_PROMISE_REJECTED)
   1410                         res_val = JS_Throw(ctx, JS_PromiseResult(ctx, promise));
   1411                     else
   1412                         res_val = JS_ThrowTypeError(ctx, "promise is pending");
   1413                 }
   1414                 break;
   1415             }
   1416         }
   1417         JS_FreeValue(ctx, promise);
   1418     }
   1419 
   1420     if (JS_IsException(res_val)) {
   1421         exception_val = JS_GetException(ctx);
   1422         is_error = JS_IsError(ctx, exception_val);
   1423         /* XXX: should get the filename and line number */
   1424         if (outfile) {
   1425             if (!is_error)
   1426                 fprintf(outfile, "%sThrow: ", (eval_flags & JS_EVAL_FLAG_STRICT) ?
   1427                         "strict mode: " : "");
   1428             js_print(ctx, JS_NULL, 1, &exception_val);
   1429         }
   1430         if (is_error) {
   1431             JSValue name, stack;
   1432             const char *stack_str;
   1433 
   1434             name = JS_GetPropertyStr(ctx, exception_val, "name");
   1435             error_name = JS_ToCString(ctx, name);
   1436             stack = JS_GetPropertyStr(ctx, exception_val, "stack");
   1437             if (!JS_IsUndefined(stack)) {
   1438                 stack_str = JS_ToCString(ctx, stack);
   1439                 if (stack_str) {
   1440                     const char *p;
   1441                     int len;
   1442 
   1443                     if (outfile)
   1444                         fprintf(outfile, "%s", stack_str);
   1445 
   1446                     len = strlen(filename);
   1447                     p = strstr(stack_str, filename);
   1448                     if (p != NULL && p[len] == ':') {
   1449                         error_line = atoi(p + len + 1);
   1450                         has_error_line = TRUE;
   1451                     }
   1452                     JS_FreeCString(ctx, stack_str);
   1453                 }
   1454             }
   1455             JS_FreeValue(ctx, stack);
   1456             JS_FreeValue(ctx, name);
   1457         }
   1458         if (is_negative) {
   1459             ret = 0;
   1460             if (error_type) {
   1461                 char *error_class;
   1462                 const char *msg;
   1463 
   1464                 msg = JS_ToCString(ctx, exception_val);
   1465                 error_class = strdup_len(msg, strcspn(msg, ":"));
   1466                 if (!str_equal(error_class, error_type))
   1467                     ret = -1;
   1468                 free(error_class);
   1469                 JS_FreeCString(ctx, msg);
   1470             }
   1471         } else {
   1472             ret = -1;
   1473         }
   1474     } else {
   1475         if (is_negative)
   1476             ret = -1;
   1477         else
   1478             ret = 0;
   1479     }
   1480 
   1481     if (verbose && is_test) {
   1482         JSValue msg_val = JS_UNDEFINED;
   1483         const char *msg = NULL;
   1484         int s_line;
   1485         char *s = find_error(filename, &s_line, eval_flags & JS_EVAL_FLAG_STRICT);
   1486         const char *strict_mode = (eval_flags & JS_EVAL_FLAG_STRICT) ? "strict mode: " : "";
   1487 
   1488         if (!JS_IsUndefined(exception_val)) {
   1489             msg_val = JS_ToString(ctx, exception_val);
   1490             msg = JS_ToCString(ctx, msg_val);
   1491         }
   1492         if (is_negative) {  // expect error
   1493             if (ret == 0) {
   1494                 if (msg && s &&
   1495                     (str_equal(s, "expected error") ||
   1496                      strstart(s, "unexpected error type:", NULL) ||
   1497                      str_equal(s, msg))) {     // did not have error yet
   1498                     if (!has_error_line) {
   1499                         longest_match(buf, msg, pos, &pos, pos_line, &error_line);
   1500                     }
   1501                     printf("%s:%d: %sOK, now has error %s\n",
   1502                            filename, error_line, strict_mode, msg);
   1503                     fixed_errors++;
   1504                 }
   1505             } else {
   1506                 if (!s) {   // not yet reported
   1507                     if (msg) {
   1508                         fprintf(error_out, "%s:%d: %sunexpected error type: %s\n",
   1509                                 filename, error_line, strict_mode, msg);
   1510                     } else {
   1511                         fprintf(error_out, "%s:%d: %sexpected error\n",
   1512                                 filename, error_line, strict_mode);
   1513                     }
   1514                     new_errors++;
   1515                 }
   1516             }
   1517         } else {            // should not have error
   1518             if (msg) {
   1519                 if (!s || !str_equal(s, msg)) {
   1520                     if (!has_error_line) {
   1521                         char *p = skip_prefix(msg, "Test262 Error: ");
   1522                         if (strstr(p, "Test case returned non-true value!")) {
   1523                             longest_match(buf, "runTestCase", pos, &pos, pos_line, &error_line);
   1524                         } else {
   1525                             longest_match(buf, p, pos, &pos, pos_line, &error_line);
   1526                         }
   1527                     }
   1528                     fprintf(error_out, "%s:%d: %s%s%s\n", filename, error_line, strict_mode,
   1529                             error_file ? "unexpected error: " : "", msg);
   1530 
   1531                     if (s && (!str_equal(s, msg) || error_line != s_line)) {
   1532                         printf("%s:%d: %sprevious error: %s\n", filename, s_line, strict_mode, s);
   1533                         changed_errors++;
   1534                     } else {
   1535                         new_errors++;
   1536                     }
   1537                 }
   1538             } else {
   1539                 if (s) {
   1540                     printf("%s:%d: %sOK, fixed error: %s\n", filename, s_line, strict_mode, s);
   1541                     fixed_errors++;
   1542                 }
   1543             }
   1544         }
   1545         JS_FreeValue(ctx, msg_val);
   1546         JS_FreeCString(ctx, msg);
   1547         free(s);
   1548     }
   1549     JS_FreeCString(ctx, error_name);
   1550     JS_FreeValue(ctx, exception_val);
   1551     JS_FreeValue(ctx, res_val);
   1552     return ret;
   1553 }
   1554 
   1555 static int eval_file(JSContext *ctx, const char *base, const char *p,
   1556                      int eval_flags)
   1557 {
   1558     char *buf;
   1559     size_t buf_len;
   1560     char *filename = compose_path(base, p);
   1561 
   1562     buf = load_file(filename, &buf_len);
   1563     if (!buf) {
   1564         warning("cannot load %s", filename);
   1565         goto fail;
   1566     }
   1567     if (eval_buf(ctx, buf, buf_len, filename, FALSE, FALSE, NULL, stderr,
   1568                  eval_flags, FALSE)) {
   1569         warning("error evaluating %s", filename);
   1570         goto fail;
   1571     }
   1572     free(buf);
   1573     free(filename);
   1574     return 0;
   1575 
   1576 fail:
   1577     free(buf);
   1578     free(filename);
   1579     return 1;
   1580 }
   1581 
   1582 char *extract_desc(const char *buf, char style)
   1583 {
   1584     const char *p, *desc_start;
   1585     char *desc;
   1586     int len;
   1587 
   1588     p = buf;
   1589     while (*p != '\0') {
   1590         if (p[0] == '/' && p[1] == '*' && p[2] == style && p[3] != '/') {
   1591             p += 3;
   1592             desc_start = p;
   1593             while (*p != '\0' && (p[0] != '*' || p[1] != '/'))
   1594                 p++;
   1595             if (*p == '\0') {
   1596                 warning("Expecting end of desc comment");
   1597                 return NULL;
   1598             }
   1599             len = p - desc_start;
   1600             desc = malloc(len + 1);
   1601             memcpy(desc, desc_start, len);
   1602             desc[len] = '\0';
   1603             return desc;
   1604         } else {
   1605             p++;
   1606         }
   1607     }
   1608     return NULL;
   1609 }
   1610 
   1611 static char *find_tag(char *desc, const char *tag, int *state)
   1612 {
   1613     char *p;
   1614     p = strstr(desc, tag);
   1615     if (p) {
   1616         p += strlen(tag);
   1617         *state = 0;
   1618     }
   1619     return p;
   1620 }
   1621 
   1622 static char *get_option(char **pp, int *state)
   1623 {
   1624     char *p, *p0, *option = NULL;
   1625     if (*pp) {
   1626         for (p = *pp;; p++) {
   1627             switch (*p) {
   1628             case '[':
   1629                 *state += 1;
   1630                 continue;
   1631             case ']':
   1632                 *state -= 1;
   1633                 if (*state > 0)
   1634                     continue;
   1635                 p = NULL;
   1636                 break;
   1637             case ' ':
   1638             case '\t':
   1639             case '\r':
   1640             case ',':
   1641             case '-':
   1642                 continue;
   1643             case '\n':
   1644                 if (*state > 0 || p[1] == ' ')
   1645                     continue;
   1646                 p = NULL;
   1647                 break;
   1648             case '\0':
   1649                 p = NULL;
   1650                 break;
   1651             default:
   1652                 p0 = p;
   1653                 p += strcspn(p0, " \t\r\n,]");
   1654                 option = strdup_len(p0, p - p0);
   1655                 break;
   1656             }
   1657             break;
   1658         }
   1659         *pp = p;
   1660     }
   1661     return option;
   1662 }
   1663 
   1664 void update_stats(JSRuntime *rt, const char *filename) {
   1665     JSMemoryUsage stats;
   1666     JS_ComputeMemoryUsage(rt, &stats);
   1667 
   1668     pthread_mutex_lock(&stats_mutex);
   1669     if (stats_count++ == 0) {
   1670         stats_avg = stats_all = stats_min = stats_max = stats;
   1671         stats_min_filename = strdup(filename);
   1672         stats_max_filename = strdup(filename);
   1673     } else {
   1674         if (stats_max.malloc_size < stats.malloc_size) {
   1675             stats_max = stats;
   1676             free(stats_max_filename);
   1677             stats_max_filename = strdup(filename);
   1678         }
   1679         if (stats_min.malloc_size > stats.malloc_size) {
   1680             stats_min = stats;
   1681             free(stats_min_filename);
   1682             stats_min_filename = strdup(filename);
   1683         }
   1684 
   1685 #define update(f)  stats_avg.f = (stats_all.f += stats.f) / stats_count
   1686         update(malloc_count);
   1687         update(malloc_size);
   1688         update(memory_used_count);
   1689         update(memory_used_size);
   1690         update(atom_count);
   1691         update(atom_size);
   1692         update(str_count);
   1693         update(str_size);
   1694         update(obj_count);
   1695         update(obj_size);
   1696         update(prop_count);
   1697         update(prop_size);
   1698         update(shape_count);
   1699         update(shape_size);
   1700         update(js_func_count);
   1701         update(js_func_size);
   1702         update(js_func_code_size);
   1703         update(js_func_pc2line_count);
   1704         update(js_func_pc2line_size);
   1705         update(c_func_count);
   1706         update(array_count);
   1707         update(fast_array_count);
   1708         update(fast_array_elements);
   1709     }
   1710 #undef update
   1711     pthread_mutex_unlock(&stats_mutex);
   1712 }
   1713 
   1714 int run_test_buf(ThreadLocalStorage *tls,
   1715                  const char *filename, const char *harness, namelist_t *ip,
   1716                  char *buf, size_t buf_len, const char* error_type,
   1717                  int eval_flags, BOOL is_negative, BOOL is_async,
   1718                  BOOL can_block)
   1719 {
   1720     JSRuntime *rt;
   1721     JSContext *ctx;
   1722     int i, ret;
   1723 
   1724     rt = JS_NewRuntime();
   1725     if (rt == NULL) {
   1726         fatal(1, "JS_NewRuntime failure");
   1727     }
   1728     JS_SetRuntimeOpaque(rt, tls);
   1729     ctx = JS_NewContext(rt);
   1730     if (ctx == NULL) {
   1731         JS_FreeRuntime(rt);
   1732         fatal(1, "JS_NewContext failure");
   1733     }
   1734     JS_SetRuntimeInfo(rt, filename);
   1735 
   1736     JS_SetCanBlock(rt, can_block);
   1737 
   1738     /* loader for ES6 modules */
   1739     JS_SetModuleLoaderFunc2(rt, NULL, js_module_loader_test, NULL, (void *)filename);
   1740 
   1741     add_helpers(ctx);
   1742 
   1743     for (i = 0; i < ip->count; i++) {
   1744         if (eval_file(ctx, harness, ip->array[i],
   1745                       JS_EVAL_TYPE_GLOBAL)) {
   1746             fatal(1, "error including %s for %s", ip->array[i], filename);
   1747         }
   1748     }
   1749 
   1750     ret = eval_buf(ctx, buf, buf_len, filename, TRUE, is_negative,
   1751                    error_type, outfile, eval_flags, is_async);
   1752     ret = (ret != 0);
   1753 
   1754     if (dump_memory) {
   1755         update_stats(rt, filename);
   1756     }
   1757     js_agent_free(ctx);
   1758     JS_FreeContext(ctx);
   1759     JS_FreeRuntime(rt);
   1760 
   1761     atomic_inc(&test_count);
   1762     if (ret) {
   1763         atomic_inc(&test_failed);
   1764         if (outfile) {
   1765             /* do not output a failure number to minimize diff */
   1766             fprintf(outfile, "  FAILED\n");
   1767         }
   1768     }
   1769     return ret;
   1770 }
   1771 
   1772 int run_test(ThreadLocalStorage *tls, const char *filename, int index)
   1773 {
   1774     char harnessbuf[1024];
   1775     char *harness;
   1776     char *buf;
   1777     size_t buf_len;
   1778     char *desc, *p;
   1779     char *error_type;
   1780     int ret, eval_flags, use_strict, use_nostrict;
   1781     BOOL is_negative, is_nostrict, is_onlystrict, is_async, is_module, skip;
   1782     BOOL can_block;
   1783     namelist_t include_list = { 0 }, *ip = &include_list;
   1784 
   1785     is_nostrict = is_onlystrict = is_negative = is_async = is_module = skip = FALSE;
   1786     can_block = TRUE;
   1787     error_type = NULL;
   1788     buf = load_file(filename, &buf_len);
   1789 
   1790     harness = harness_dir;
   1791 
   1792     if (new_style) {
   1793         if (!harness) {
   1794             p = strstr(filename, "test/");
   1795             if (p) {
   1796                 snprintf(harnessbuf, sizeof(harnessbuf), "%.*s%s",
   1797                          (int)(p - filename), filename, "harness");
   1798             } else {
   1799                 pstrcpy(harnessbuf, sizeof(harnessbuf), "");
   1800             }
   1801             harness = harnessbuf;
   1802         }
   1803         namelist_add(ip, NULL, "sta.js");
   1804         namelist_add(ip, NULL, "assert.js");
   1805         /* extract the YAML frontmatter */
   1806         desc = extract_desc(buf, '-');
   1807         if (desc) {
   1808             char *ifile, *option;
   1809             int state;
   1810             p = find_tag(desc, "includes:", &state);
   1811             if (p) {
   1812                 while ((ifile = get_option(&p, &state)) != NULL) {
   1813                     // skip unsupported harness files
   1814                     if (find_word(harness_exclude, ifile)) {
   1815                         skip |= 1;
   1816                     } else {
   1817                         namelist_add(ip, NULL, ifile);
   1818                     }
   1819                     free(ifile);
   1820                 }
   1821             }
   1822             p = find_tag(desc, "flags:", &state);
   1823             if (p) {
   1824                 while ((option = get_option(&p, &state)) != NULL) {
   1825                     if (str_equal(option, "noStrict") ||
   1826                         str_equal(option, "raw")) {
   1827                         is_nostrict = TRUE;
   1828                         skip |= (test_mode == TEST_STRICT);
   1829                     }
   1830                     else if (str_equal(option, "onlyStrict")) {
   1831                         is_onlystrict = TRUE;
   1832                         skip |= (test_mode == TEST_NOSTRICT);
   1833                     }
   1834                     else if (str_equal(option, "async")) {
   1835                         is_async = TRUE;
   1836                         skip |= skip_async;
   1837                     }
   1838                     else if (str_equal(option, "module")) {
   1839                         is_module = TRUE;
   1840                         skip |= skip_module;
   1841                     }
   1842                     else if (str_equal(option, "CanBlockIsFalse")) {
   1843                         can_block = FALSE;
   1844                     }
   1845                     free(option);
   1846                 }
   1847             }
   1848             p = find_tag(desc, "negative:", &state);
   1849             if (p) {
   1850                 /* XXX: should extract the phase */
   1851                 char *q = find_tag(p, "type:", &state);
   1852                 if (q) {
   1853                     while (isspace((unsigned char)*q))
   1854                         q++;
   1855                     error_type = strdup_len(q, strcspn(q, " \n"));
   1856                 }
   1857                 is_negative = TRUE;
   1858             }
   1859             p = find_tag(desc, "features:", &state);
   1860             if (p) {
   1861                 while ((option = get_option(&p, &state)) != NULL) {
   1862                     char *p1;
   1863                     if (find_word(harness_features, option)) {
   1864                         /* feature is enabled */
   1865                     } else if ((p1 = find_word(harness_skip_features, option)) != NULL) {
   1866                         /* skip disabled feature */
   1867                         if (harness_skip_features_count)
   1868                             harness_skip_features_count[p1 - harness_skip_features]++;
   1869                         skip |= 1;
   1870                     } else {
   1871                         /* feature is not listed: skip and warn */
   1872                         printf("%s:%d: unknown feature: %s\n", filename, 1, option);
   1873                         skip |= 1;
   1874                     }
   1875                     free(option);
   1876                 }
   1877             }
   1878             free(desc);
   1879         }
   1880         if (is_async)
   1881             namelist_add(ip, NULL, "doneprintHandle.js");
   1882     } else {
   1883         char *ifile;
   1884 
   1885         if (!harness) {
   1886             p = strstr(filename, "test/");
   1887             if (p) {
   1888                 snprintf(harnessbuf, sizeof(harnessbuf), "%.*s%s",
   1889                          (int)(p - filename), filename, "test/harness");
   1890             } else {
   1891                 pstrcpy(harnessbuf, sizeof(harnessbuf), "");
   1892             }
   1893             harness = harnessbuf;
   1894         }
   1895 
   1896         namelist_add(ip, NULL, "sta.js");
   1897 
   1898         /* include extra harness files */
   1899         for (p = buf; (p = strstr(p, "$INCLUDE(\"")) != NULL; p++) {
   1900             p += 10;
   1901             ifile = strdup_len(p, strcspn(p, "\""));
   1902             // skip unsupported harness files
   1903             if (find_word(harness_exclude, ifile)) {
   1904                 skip |= 1;
   1905             } else {
   1906                 namelist_add(ip, NULL, ifile);
   1907             }
   1908             free(ifile);
   1909         }
   1910 
   1911         /* locate the old style configuration comment */
   1912         desc = extract_desc(buf, '*');
   1913         if (desc) {
   1914             if (strstr(desc, "@noStrict")) {
   1915                 is_nostrict = TRUE;
   1916                 skip |= (test_mode == TEST_STRICT);
   1917             }
   1918             if (strstr(desc, "@onlyStrict")) {
   1919                 is_onlystrict = TRUE;
   1920                 skip |= (test_mode == TEST_NOSTRICT);
   1921             }
   1922             if (strstr(desc, "@negative")) {
   1923                 /* XXX: should extract the regex to check error type */
   1924                 is_negative = TRUE;
   1925             }
   1926             free(desc);
   1927         }
   1928     }
   1929 
   1930     if (outfile && index >= 0) {
   1931         fprintf(outfile, "%d: %s%s%s%s%s%s%s\n", index, filename,
   1932                 is_nostrict ? "  @noStrict" : "",
   1933                 is_onlystrict ? "  @onlyStrict" : "",
   1934                 is_async ? "  async" : "",
   1935                 is_module ? "  module" : "",
   1936                 is_negative ? "  @negative" : "",
   1937                 skip ? "  SKIPPED" : "");
   1938         fflush(outfile);
   1939     }
   1940 
   1941     use_strict = use_nostrict = 0;
   1942     /* XXX: should remove 'test_mode' or simplify it just to force
   1943        strict or non strict mode for single file tests */
   1944     switch (test_mode) {
   1945     case TEST_DEFAULT_NOSTRICT:
   1946         if (is_onlystrict)
   1947             use_strict = 1;
   1948         else
   1949             use_nostrict = 1;
   1950         break;
   1951     case TEST_DEFAULT_STRICT:
   1952         if (is_nostrict)
   1953             use_nostrict = 1;
   1954         else
   1955             use_strict = 1;
   1956         break;
   1957     case TEST_NOSTRICT:
   1958         if (!is_onlystrict)
   1959             use_nostrict = 1;
   1960         break;
   1961     case TEST_STRICT:
   1962         if (!is_nostrict)
   1963             use_strict = 1;
   1964         break;
   1965     case TEST_ALL:
   1966         if (is_module) {
   1967             use_nostrict = 1;
   1968         } else {
   1969             if (!is_nostrict)
   1970                 use_strict = 1;
   1971             if (!is_onlystrict)
   1972                 use_nostrict = 1;
   1973         }
   1974         break;
   1975     }
   1976 
   1977     if (skip || use_strict + use_nostrict == 0) {
   1978         atomic_inc(&test_skipped);
   1979         ret = -2;
   1980     } else {
   1981         clock_t clocks;
   1982 
   1983         if (is_module) {
   1984             eval_flags = JS_EVAL_TYPE_MODULE;
   1985         } else {
   1986             eval_flags = JS_EVAL_TYPE_GLOBAL;
   1987         }
   1988         clocks = clock();
   1989         ret = 0;
   1990         if (use_nostrict) {
   1991             ret = run_test_buf(tls, filename, harness, ip, buf, buf_len,
   1992                                error_type, eval_flags, is_negative, is_async,
   1993                                can_block);
   1994         }
   1995         if (use_strict) {
   1996             ret |= run_test_buf(tls, filename, harness, ip, buf, buf_len,
   1997                                 error_type, eval_flags | JS_EVAL_FLAG_STRICT,
   1998                                 is_negative, is_async, can_block);
   1999         }
   2000         clocks = clock() - clocks;
   2001         if (outfile && index >= 0 && clocks >= CLOCKS_PER_SEC / 10) {
   2002             /* output timings for tests that take more than 100 ms */
   2003             fprintf(outfile, " time: %d ms\n", (int)(clocks * 1000LL / CLOCKS_PER_SEC));
   2004         }
   2005     }
   2006     namelist_free(&include_list);
   2007     free(error_type);
   2008     free(buf);
   2009 
   2010     return ret;
   2011 }
   2012 
   2013 /* run a test when called by test262-harness+eshost */
   2014 int run_test262_harness_test(ThreadLocalStorage *tls,
   2015                              const char *filename, BOOL is_module, BOOL can_block)
   2016 {
   2017     JSRuntime *rt;
   2018     JSContext *ctx;
   2019     char *buf;
   2020     size_t buf_len;
   2021     int eval_flags, ret_code, ret;
   2022     JSValue res_val;
   2023 
   2024     outfile = stdout; /* for js_print */
   2025 
   2026     rt = JS_NewRuntime();
   2027     if (rt == NULL) {
   2028         fatal(1, "JS_NewRuntime failure");
   2029     }
   2030     JS_SetRuntimeOpaque(rt, tls);
   2031     ctx = JS_NewContext(rt);
   2032     if (ctx == NULL) {
   2033         JS_FreeRuntime(rt);
   2034         fatal(1, "JS_NewContext failure");
   2035     }
   2036     JS_SetRuntimeInfo(rt, filename);
   2037 
   2038     JS_SetCanBlock(rt, can_block);
   2039 
   2040     /* loader for ES6 modules */
   2041     JS_SetModuleLoaderFunc2(rt, NULL, js_module_loader_test, NULL, (void *)filename);
   2042 
   2043     add_helpers(ctx);
   2044 
   2045     buf = load_file(filename, &buf_len);
   2046 
   2047     if (is_module) {
   2048       eval_flags = JS_EVAL_TYPE_MODULE;
   2049     } else {
   2050       eval_flags = JS_EVAL_TYPE_GLOBAL;
   2051     }
   2052     res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
   2053     ret_code = 0;
   2054     if (JS_IsException(res_val)) {
   2055        js_std_dump_error(ctx);
   2056        ret_code = 1;
   2057     } else {
   2058         JSValue promise = JS_UNDEFINED;
   2059         if (is_module) {
   2060             promise = res_val;
   2061         } else {
   2062             JS_FreeValue(ctx, res_val);
   2063         }
   2064         for(;;) {
   2065             ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), NULL);
   2066             if (ret < 0) {
   2067                 js_std_dump_error(ctx);
   2068                 ret_code = 1;
   2069             } else if (ret == 0) {
   2070                 break;
   2071             }
   2072         }
   2073         /* dump the error if the module returned an error. */
   2074         if (is_module) {
   2075             JSPromiseStateEnum state = JS_PromiseState(ctx, promise);
   2076             if (state == JS_PROMISE_REJECTED) {
   2077                 JS_Throw(ctx, JS_PromiseResult(ctx, promise));
   2078                 js_std_dump_error(ctx);
   2079                 ret_code = 1;
   2080             }
   2081         }
   2082         JS_FreeValue(ctx, promise);
   2083     }
   2084     free(buf);
   2085     js_agent_free(ctx);
   2086     JS_FreeContext(ctx);
   2087     JS_FreeRuntime(rt);
   2088     return ret_code;
   2089 }
   2090 
   2091 static int pthread_cond_timedwait2(pthread_cond_t *cond, pthread_mutex_t *mutex, int timeout)
   2092 {
   2093     struct timespec ts;
   2094 
   2095     clock_gettime(CLOCK_REALTIME, &ts);
   2096     ts.tv_sec += timeout / 1000;
   2097     ts.tv_nsec += (timeout % 1000) * 1000000;
   2098     if (ts.tv_nsec >= 1000000000) {
   2099         ts.tv_nsec -= 1000000000;
   2100         ts.tv_sec++;
   2101     }
   2102     return pthread_cond_timedwait(cond, mutex, &ts);
   2103 }
   2104 
   2105 void *show_progress(void *opaque)
   2106 {
   2107     int test_skipped1, test_failed1, test_count1;
   2108 
   2109     pthread_mutex_lock(&progress_mutex);
   2110     for(;;) {
   2111         pthread_cond_timedwait2(&progress_cond, &progress_mutex, 50);
   2112 
   2113         test_failed1 = atomic_load(&test_failed);
   2114         test_count1 = atomic_load(&test_count);
   2115         test_skipped1 = atomic_load(&test_skipped);
   2116 
   2117         if (compact) {
   2118             static int last_test_skipped;
   2119             static int last_test_failed;
   2120             static int dots;
   2121             char c = '.';
   2122             
   2123             if (test_skipped1 > last_test_skipped)
   2124                 c = '-';
   2125             if (test_failed1 > last_test_failed)
   2126                 c = '!';
   2127             last_test_skipped = test_skipped1;
   2128             last_test_failed = test_failed1;
   2129 
   2130             fputc(c, stderr);
   2131             if (progress_exit_request || ++dots % 60 == 0) {
   2132                 fprintf(stderr, " %d/%d/%d\n",
   2133                         test_failed1, test_count1, test_skipped1);
   2134             }
   2135         } else {
   2136             /* output progress indicator: erase end of line and return to col 0 */
   2137             fprintf(stderr, "%d/%d/%d\033[K\r",
   2138                     test_failed1, test_count1, test_skipped1);
   2139         }
   2140         fflush(stderr);
   2141         if (progress_exit_request)
   2142             break;
   2143     }
   2144     pthread_mutex_unlock(&progress_mutex);
   2145     return NULL;
   2146 }
   2147 
   2148 enum { INCLUDE, EXCLUDE, SKIP };
   2149 
   2150 int include_exclude_or_skip(int i) // naming is hard...
   2151 {
   2152     if (namelist_find(&exclude_list, test_list.array[i]) >= 0)
   2153         return EXCLUDE;
   2154     if (i < start_index)
   2155         return SKIP;
   2156     if (stop_index >= 0 && i > stop_index)
   2157         return SKIP;
   2158     return INCLUDE;
   2159 }
   2160 
   2161 typedef struct {
   2162     pthread_t tid;
   2163     int thread_index;
   2164 } RunTestDirThread;
   2165 
   2166 void *run_test_dir_list(void *opaque)
   2167 {
   2168     RunTestDirThread *th = opaque;
   2169     ThreadLocalStorage tls_s, *tls = &tls_s;
   2170     namelist_t *lp = &test_list;
   2171     int i;
   2172     
   2173     init_thread_local_storage(tls);
   2174     
   2175     for (i = th->thread_index; i < lp->count; i += nthreads) {
   2176         const char *p = lp->array[i];
   2177         int ti;
   2178         if (INCLUDE != include_exclude_or_skip(i))
   2179             continue;
   2180         
   2181         if (slow_test_threshold != 0) {
   2182             ti = get_clock_ms();
   2183         } else {
   2184             ti = 0;
   2185         }
   2186         run_test(tls, p, i);
   2187         if (slow_test_threshold != 0) {
   2188             ti = get_clock_ms() - ti;
   2189             if (ti >= slow_test_threshold)
   2190                 fprintf(stderr, "\n%s (%d ms)\n", p, ti);
   2191         }
   2192     }
   2193     return NULL;
   2194 }
   2195 
   2196 void help(void)
   2197 {
   2198     printf("run-test262 version " CONFIG_VERSION "\n"
   2199            "usage: run-test262 [options] {-f file ... | [dir_list] [index range]}\n"
   2200            "-h             help\n"
   2201            "-a             run tests in strict and nostrict modes\n"
   2202            "-m             print memory usage summary\n"
   2203            "-n             use new style harness\n"
   2204            "-N             run test prepared by test262-harness+eshost\n"
   2205            "-s             run tests in strict mode, skip @nostrict tests\n"
   2206            "-E             only run tests from the error file\n"
   2207            "-C             use compact progress indicator\n"
   2208            "-t             show timings\n"
   2209            "-u             update error file\n"
   2210            "-v             verbose: output error messages\n"
   2211            "-D duration    display tests taking more than 'duration' ms\n"
   2212            "-T threads     number of parallel threads\n"
   2213            "-c file        read configuration from 'file'\n"
   2214            "-d dir         run all test files in directory tree 'dir'\n"
   2215            "-e file        load the known errors from 'file'\n"
   2216            "-f file        execute single test from 'file'\n"
   2217            "-r file        set the report file name (default=none)\n"
   2218            "-x file        exclude tests listed in 'file'\n"
   2219            "--no-can-block set [[CanBlock]] to false (Atomics.wait will throw)\n");
   2220     exit(1);
   2221 }
   2222 
   2223 char *get_opt_arg(const char *option, char *arg)
   2224 {
   2225     if (!arg) {
   2226         fatal(2, "missing argument for option %s", option);
   2227     }
   2228     return arg;
   2229 }
   2230 
   2231 int main(int argc, char **argv)
   2232 {
   2233     ThreadLocalStorage tls_s, *tls = &tls_s;
   2234     int optind;
   2235     BOOL is_dir_list;
   2236     BOOL only_check_errors = FALSE;
   2237     const char *filename;
   2238     const char *ignore = "";
   2239     BOOL is_test262_harness = FALSE;
   2240     BOOL is_module = FALSE;
   2241     BOOL can_block = TRUE;
   2242     BOOL count_skipped_features = FALSE;
   2243     clock_t clocks;
   2244     
   2245     init_thread_local_storage(tls);
   2246     pthread_mutex_init(&stats_mutex, NULL);
   2247 
   2248 #if !defined(_WIN32)
   2249     compact = !isatty(STDERR_FILENO);
   2250     /* Date tests assume California local time */
   2251     setenv("TZ", "America/Los_Angeles", 1);
   2252 #endif
   2253 
   2254     optind = 1;
   2255     while (optind < argc) {
   2256         char *arg = argv[optind];
   2257         if (*arg != '-')
   2258             break;
   2259         optind++;
   2260         if (strstr("-c -d -e -x -f -r -E -D -T", arg))
   2261             optind++;
   2262         if (strstr("-d -f", arg))
   2263             ignore = "testdir"; // run only the tests from -d or -f
   2264     }
   2265 
   2266     /* cannot use getopt because we want to pass the command line to
   2267        the script */
   2268     optind = 1;
   2269     is_dir_list = TRUE;
   2270     while (optind < argc) {
   2271         char *arg = argv[optind];
   2272         if (*arg != '-')
   2273             break;
   2274         optind++;
   2275         if (str_equal(arg, "-h")) {
   2276             help();
   2277         } else if (str_equal(arg, "-m")) {
   2278             dump_memory++;
   2279         } else if (str_equal(arg, "-n")) {
   2280             new_style++;
   2281         } else if (str_equal(arg, "-s")) {
   2282             test_mode = TEST_STRICT;
   2283         } else if (str_equal(arg, "-a")) {
   2284             test_mode = TEST_ALL;
   2285         } else if (str_equal(arg, "-t")) {
   2286             show_timings++;
   2287         } else if (str_equal(arg, "-u")) {
   2288             update_errors++;
   2289         } else if (str_equal(arg, "-v")) {
   2290             verbose++;
   2291         } else if (str_equal(arg, "-C")) {
   2292             compact = 1;
   2293         } else if (str_equal(arg, "-c")) {
   2294             load_config(get_opt_arg(arg, argv[optind++]), ignore);
   2295         } else if (str_equal(arg, "-d")) {
   2296             enumerate_tests(get_opt_arg(arg, argv[optind++]));
   2297         } else if (str_equal(arg, "-e")) {
   2298             error_filename = get_opt_arg(arg, argv[optind++]);
   2299         } else if (str_equal(arg, "-x")) {
   2300             namelist_load(&exclude_list, get_opt_arg(arg, argv[optind++]));
   2301         } else if (str_equal(arg, "-f")) {
   2302             is_dir_list = FALSE;
   2303         } else if (str_equal(arg, "-r")) {
   2304             report_filename = get_opt_arg(arg, argv[optind++]);
   2305         } else if (str_equal(arg, "-E")) {
   2306             only_check_errors = TRUE;
   2307         } else if (str_equal(arg, "-D")) {
   2308             slow_test_threshold = atoi(get_opt_arg(arg, argv[optind++]));
   2309         } else if (str_equal(arg, "-T")) {
   2310             nthreads = atoi(get_opt_arg(arg, argv[optind++]));
   2311         } else if (str_equal(arg, "-N")) {
   2312             is_test262_harness = TRUE;
   2313         } else if (str_equal(arg, "--module")) {
   2314             is_module = TRUE;
   2315         } else if (str_equal(arg, "--no-can-block")) {
   2316             can_block = FALSE;
   2317         } else if (str_equal(arg, "--count_skipped_features")) {
   2318             count_skipped_features = TRUE;
   2319         } else {
   2320             fatal(1, "unknown option: %s", arg);
   2321             break;
   2322         }
   2323     }
   2324 
   2325     if (optind >= argc && !test_list.count)
   2326         help();
   2327 
   2328     if (is_test262_harness) {
   2329         return run_test262_harness_test(tls, argv[optind], is_module, can_block);
   2330     }
   2331 
   2332     if (nthreads == 0) {
   2333         nthreads = cpu_count();
   2334         if (nthreads >= 8) {
   2335             // minus one to not (over)commit the system completely
   2336             nthreads--;
   2337         }
   2338     }
   2339     nthreads = max_int(nthreads, 1);
   2340 
   2341     error_out = stdout;
   2342     if (error_filename) {
   2343         error_file = load_file(error_filename, NULL);
   2344         if (only_check_errors && error_file) {
   2345             namelist_free(&test_list);
   2346             namelist_add_from_error_file(&test_list, error_file);
   2347         }
   2348         if (update_errors) {
   2349             free(error_file);
   2350             error_file = NULL;
   2351             error_out = fopen(error_filename, "w");
   2352             if (!error_out) {
   2353                 perror_exit(1, error_filename);
   2354             }
   2355         }
   2356     }
   2357 
   2358     update_exclude_dirs();
   2359 
   2360     clocks = clock();
   2361 
   2362     if (count_skipped_features) {
   2363         /* not storage efficient but it is simple */
   2364         size_t size;
   2365         size = sizeof(harness_skip_features_count[0]) * strlen(harness_skip_features);
   2366         harness_skip_features_count = malloc(size);
   2367         memset(harness_skip_features_count, 0, size);
   2368     }
   2369     
   2370     if (is_dir_list) {
   2371         RunTestDirThread *threads;
   2372         int i;
   2373         
   2374         if (optind < argc && !isdigit((unsigned char)argv[optind][0])) {
   2375             filename = argv[optind++];
   2376             namelist_load(&test_list, filename);
   2377         }
   2378 
   2379         start_index = 0;
   2380         stop_index = -1;
   2381         if (optind < argc) {
   2382             start_index = atoi(argv[optind++]);
   2383             if (optind < argc) {
   2384                 stop_index = atoi(argv[optind++]);
   2385             }
   2386         }
   2387         /* XXX: could reorder the report and the errors when nthreads > 1 */
   2388         if (!report_filename || str_equal(report_filename, "none") || nthreads > 1) {
   2389             outfile = NULL;
   2390         } else if (str_equal(report_filename, "-")) {
   2391             outfile = stdout;
   2392         } else {
   2393             outfile = fopen(report_filename, "wb");
   2394             if (!outfile) {
   2395                 perror_exit(1, report_filename);
   2396             }
   2397         }
   2398 
   2399         // exclude_dir_list has already been sorted by update_exclude_dirs()
   2400         namelist_sort(&test_list);
   2401         namelist_sort(&exclude_list);
   2402         
   2403         for (i = 0; i < test_list.count; i++) {
   2404             switch (include_exclude_or_skip(i)) {
   2405             case EXCLUDE:
   2406                 test_excluded++;
   2407                 break;
   2408             case SKIP:
   2409                 test_skipped++;
   2410                 break;
   2411             }
   2412         }
   2413 
   2414         pthread_cond_init(&progress_cond, NULL);
   2415         pthread_mutex_init(&progress_mutex, NULL);
   2416         pthread_create(&progress_thread, NULL, show_progress, NULL);
   2417 
   2418         threads = malloc(sizeof(threads[0]) * nthreads);
   2419         for (i = 0; i < nthreads; i++) {
   2420             RunTestDirThread *th = &threads[i];
   2421             pthread_attr_t attr;
   2422 
   2423             th->thread_index = i;
   2424             
   2425             pthread_attr_init(&attr);
   2426             pthread_attr_setstacksize(&attr, 2 << 20); // 2 MB, glibc default
   2427             pthread_create(&th->tid, &attr, run_test_dir_list, th);
   2428             pthread_attr_destroy(&attr);
   2429         }
   2430         for (i = 0; i < nthreads; i++)
   2431             pthread_join(threads[i].tid, NULL);
   2432         free(threads);
   2433 
   2434         pthread_mutex_lock(&progress_mutex);
   2435         progress_exit_request = TRUE;
   2436         pthread_cond_signal(&progress_cond);
   2437         pthread_mutex_unlock(&progress_mutex);
   2438         pthread_join(progress_thread, NULL);
   2439 
   2440         pthread_mutex_destroy(&progress_mutex);
   2441         pthread_cond_destroy(&progress_cond);
   2442 
   2443         if (outfile && outfile != stdout) {
   2444             fclose(outfile);
   2445             outfile = NULL;
   2446         }
   2447     } else {
   2448         outfile = stdout;
   2449         while (optind < argc) {
   2450             run_test(tls, argv[optind++], -1);
   2451         }
   2452     }
   2453 
   2454     clocks = clock() - clocks;
   2455 
   2456     if (dump_memory) {
   2457         if (dump_memory > 1 && stats_count > 1) {
   2458             printf("\nMininum memory statistics for %s:\n\n", stats_min_filename);
   2459             JS_DumpMemoryUsage(stdout, &stats_min, NULL);
   2460             printf("\nMaximum memory statistics for %s:\n\n", stats_max_filename);
   2461             JS_DumpMemoryUsage(stdout, &stats_max, NULL);
   2462         }
   2463         printf("\nAverage memory statistics for %d tests:\n\n", stats_count);
   2464         JS_DumpMemoryUsage(stdout, &stats_avg, NULL);
   2465         printf("\n");
   2466     }
   2467 
   2468     if (count_skipped_features) {
   2469         size_t i, n, len = strlen(harness_skip_features);
   2470         BOOL disp = FALSE;
   2471         int c;
   2472         for(i = 0; i < len; i++) {
   2473             if (harness_skip_features_count[i] != 0) {
   2474                 if (!disp) {
   2475                     disp = TRUE;
   2476                     printf("%-30s %7s\n", "SKIPPED FEATURE", "COUNT");
   2477                 }
   2478                 for(n = 0; n < 30; n++) {
   2479                     c = harness_skip_features[i + n];
   2480                     if (is_word_sep(c))
   2481                         break;
   2482                     putchar(c);
   2483                 }
   2484                 for(; n < 30; n++)
   2485                     putchar(' ');
   2486                 printf(" %7d\n", harness_skip_features_count[i]);
   2487             }
   2488         }
   2489         printf("\n");
   2490     }
   2491     
   2492     if (is_dir_list) {
   2493         fprintf(stderr, "Result: %d/%d error%s",
   2494                 test_failed, test_count, test_count != 1 ? "s" : "");
   2495         if (test_excluded)
   2496             fprintf(stderr, ", %d excluded", test_excluded);
   2497         if (test_skipped)
   2498             fprintf(stderr, ", %d skipped", test_skipped);
   2499         if (error_file) {
   2500             if (new_errors)
   2501                 fprintf(stderr, ", %d new", new_errors);
   2502             if (changed_errors)
   2503                 fprintf(stderr, ", %d changed", changed_errors);
   2504             if (fixed_errors)
   2505                 fprintf(stderr, ", %d fixed", fixed_errors);
   2506         }
   2507         fprintf(stderr, "\n");
   2508         if (show_timings)
   2509             fprintf(stderr, "Total user time: %.3fs (nthreads=%d)\n", (double)clocks / CLOCKS_PER_SEC, nthreads);
   2510     }
   2511 
   2512     if (error_out && error_out != stdout) {
   2513         fclose(error_out);
   2514         error_out = NULL;
   2515     }
   2516 
   2517     namelist_free(&test_list);
   2518     namelist_free(&exclude_list);
   2519     namelist_free(&exclude_dir_list);
   2520     free(harness_dir);
   2521     free(harness_skip_features);
   2522     free(harness_skip_features_count);
   2523     free(harness_features);
   2524     free(harness_exclude);
   2525     free(error_file);
   2526 
   2527     /* Signal that the error file is out of date. */
   2528     return new_errors || changed_errors || fixed_errors;
   2529 }