quickjs-tart

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

run-test262.c (66546B)


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