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 }