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 }