/* * ECMA Test 262 Runner for QuickJS * * Copyright (c) 2017-2020 Fabrice Bellard * Copyright (c) 2017-2020 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */#include <stdlib.h>#include <stdio.h>#include <stdarg.h>#include <inttypes.h>#include <string.h>#include <assert.h>#include <ctype.h>#include <unistd.h>#include <errno.h>#include <time.h>#include <dirent.h>#include <ftw.h> #include "cutils.h"#include "list.h"#include "quickjs-libc.h" /* enable test262 thread support to test SharedArrayBuffer and Atomics */#define CONFIG_AGENT #define CMD_NAME "run-test262" typedef struct namelist_t { char **array; int count; int size; unsigned int sorted : 1;} namelist_t; namelist_t test_list;namelist_t exclude_list;namelist_t exclude_dir_list; FILE *outfile;enum test_mode_t { TEST_DEFAULT_NOSTRICT, /* run tests as nostrict unless test is flagged as strictonly */ TEST_DEFAULT_STRICT, /* run tests as strict unless test is flagged as nostrict */ TEST_NOSTRICT, /* run tests as nostrict, skip strictonly tests */ TEST_STRICT, /* run tests as strict, skip nostrict tests */ TEST_ALL, /* run tests in both strict and nostrict, unless restricted by spec */} test_mode = TEST_DEFAULT_NOSTRICT;int skip_async;int skip_module;int new_style;int dump_memory;int stats_count;JSMemoryUsage stats_all, stats_avg, stats_min, stats_max;char *stats_min_filename;char *stats_max_filename;int verbose;char *harness_dir;char *harness_exclude;char *harness_features;char *harness_skip_features;char *error_filename;char *error_file;FILE *error_out;char *report_filename;int update_errors;int test_count, test_failed, test_index, test_skipped, test_excluded;int new_errors, changed_errors, fixed_errors;int async_done; void warning(const char *, ...) __attribute__((__format__(__printf__, 1, 2)));void fatal(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); void warning(const char *fmt, ...){ va_list ap; fflush(stdout); fprintf(stderr, "%s: ", CMD_NAME); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fputc('\n', stderr);} void fatal(int errcode, const char *fmt, ...){ va_list ap; fflush(stdout); fprintf(stderr, "%s: ", CMD_NAME); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fputc('\n', stderr); exit(errcode);} void perror_exit(int errcode, const char *s){ fflush(stdout); fprintf(stderr, "%s: ", CMD_NAME); perror(s); exit(errcode);} char *strdup_len(const char *str, int len){ char *p = malloc(len + 1); memcpy(p, str, len); p[len] = '\0'; return p;} static inline int str_equal(const char *a, const char *b) { return !strcmp(a, b);} char *str_append(char **pp, const char *sep, const char *str) { char *res, *p; size_t len = 0; p = *pp; if (p) { len = strlen(p) + strlen(sep); } res = malloc(len + strlen(str) + 1); if (p) { strcpy(res, p); strcat(res, sep); } strcpy(res + len, str); free(p); return *pp = res;} char *str_strip(char *p){ size_t len = strlen(p); while (len > 0 && isspace((unsigned char)p[len - 1])) p[--len] = '\0'; while (isspace((unsigned char)*p)) p++; return p;} int has_prefix(const char *str, const char *prefix){ return !strncmp(str, prefix, strlen(prefix));} char *skip_prefix(const char *str, const char *prefix){ int i; for (i = 0;; i++) { if (prefix[i] == '\0') { /* skip the prefix */ str += i; break; } if (str[i] != prefix[i]) break; } return (char *)str;} char *get_basename(const char *filename){ char *p; p = strrchr(filename, '/'); if (!p) return NULL; return strdup_len(filename, p - filename);} char *compose_path(const char *path, const char *name){ int path_len, name_len; char *d, *q; if (!path || path[0] == '\0' || *name == '/') { d = strdup(name); } else { path_len = strlen(path); name_len = strlen(name); d = malloc(path_len + 1 + name_len + 1); if (d) { q = d; memcpy(q, path, path_len); q += path_len; if (path[path_len - 1] != '/') *q++ = '/'; memcpy(q, name, name_len + 1); } } return d;} int namelist_cmp(const char *a, const char *b){ /* compare strings in modified lexicographical order */ for (;;) { int ca = (unsigned char)*a++; int cb = (unsigned char)*b++; if (isdigit(ca) && isdigit(cb)) { int na = ca - '0'; int nb = cb - '0'; while (isdigit(ca = (unsigned char)*a++)) na = na * 10 + ca - '0'; while (isdigit(cb = (unsigned char)*b++)) nb = nb * 10 + cb - '0'; if (na < nb) return -1; if (na > nb) return +1; } if (ca < cb) return -1; if (ca > cb) return +1; if (ca == '\0') return 0; }} int namelist_cmp_indirect(const void *a, const void *b){ return namelist_cmp(*(const char **)a, *(const char **)b);} void namelist_sort(namelist_t *lp){ int i, count; if (lp->count > 1) { qsort(lp->array, lp->count, sizeof(*lp->array), namelist_cmp_indirect); /* remove duplicates */ for (count = i = 1; i < lp->count; i++) { if (namelist_cmp(lp->array[count - 1], lp->array[i]) == 0) { free(lp->array[i]); } else { lp->array[count++] = lp->array[i]; } } lp->count = count; } lp->sorted = 1;} int namelist_find(namelist_t *lp, const char *name){ int a, b, m, cmp; if (!lp->sorted) { namelist_sort(lp); } for (a = 0, b = lp->count; a < b;) { m = a + (b - a) / 2; cmp = namelist_cmp(lp->array[m], name); if (cmp < 0) a = m + 1; else if (cmp > 0) b = m; else return m; } return -1;} void namelist_add(namelist_t *lp, const char *base, const char *name){ char *s; s = compose_path(base, name); if (!s) goto fail; if (lp->count == lp->size) { size_t newsize = lp->size + (lp->size >> 1) + 4; char **a = realloc(lp->array, sizeof(lp->array[0]) * newsize); if (!a) goto fail; lp->array = a; lp->size = newsize; } lp->array[lp->count] = s; lp->count++; return;fail: fatal(1, "allocation failure\n");} void namelist_load(namelist_t *lp, const char *filename){ char buf[1024]; char *base_name; FILE *f; f = fopen(filename, "rb"); if (!f) { perror_exit(1, filename); } base_name = get_basename(filename); while (fgets(buf, sizeof(buf), f) != NULL) { char *p = str_strip(buf); if (*p == '#' || *p == ';' || *p == '\0') continue; /* line comment */ namelist_add(lp, base_name, p); } free(base_name); fclose(f);} void namelist_add_from_error_file(namelist_t *lp, const char *file){ const char *p, *p0; char *pp; for (p = file; (p = strstr(p, ".js:")) != NULL; p++) { for (p0 = p; p0 > file && p0[-1] != '\n'; p0--) continue; pp = strdup_len(p0, p + 3 - p0); namelist_add(lp, NULL, pp); free(pp); }} void namelist_free(namelist_t *lp){ while (lp->count > 0) { free(lp->array[--lp->count]); } free(lp->array); lp->array = NULL; lp->size = 0;} static int add_test_file(const char *filename, const struct stat *ptr, int flag){ namelist_t *lp = &test_list; if (has_suffix(filename, ".js") && !has_suffix(filename, "_FIXTURE.js")) namelist_add(lp, NULL, filename); return 0;} /* find js files from the directory tree and sort the list */static void enumerate_tests(const char *path){ namelist_t *lp = &test_list; int start = lp->count; ftw(path, add_test_file, 100); qsort(lp->array + start, lp->count - start, sizeof(*lp->array), namelist_cmp_indirect);} static JSValue js_print(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv){ int i; const char *str; if (outfile) { for (i = 0; i < argc; i++) { if (i != 0) fputc(' ', outfile); str = JS_ToCString(ctx, argv[i]); if (!str) return JS_EXCEPTION; if (!strcmp(str, "Test262:AsyncTestComplete")) { async_done++; } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) { async_done = 2; /* force an error */ } fputs(str, outfile); JS_FreeCString(ctx, str); } fputc('\n', outfile); } return JS_UNDEFINED;} static JSValue js_detachArrayBuffer(JSContext *ctx, JSValue this_val, int argc, JSValue *argv){ JS_DetachArrayBuffer(ctx, argv[0]); return JS_UNDEFINED;} static JSValue js_evalScript(JSContext *ctx, JSValue this_val, int argc, JSValue *argv){ const char *str; size_t len; JSValue ret; str = JS_ToCStringLen(ctx, &len, argv[0]); if (!str) return JS_EXCEPTION; ret = JS_Eval(ctx, str, len, "<evalScript>", JS_EVAL_TYPE_GLOBAL); JS_FreeCString(ctx, str); return ret;} #ifdef CONFIG_AGENT #include <pthread.h> typedef struct { struct list_head link; pthread_t tid; char *script; JSValue broadcast_func; BOOL broadcast_pending; JSValue broadcast_sab; /* in the main context */ uint8_t *broadcast_sab_buf; size_t broadcast_sab_size; int32_t broadcast_val;} Test262Agent; typedef struct { struct list_head link; char *str;} AgentReport; static JSValue add_helpers1(JSContext *ctx);static void add_helpers(JSContext *ctx); static pthread_mutex_t agent_mutex = PTHREAD_MUTEX_INITIALIZER;static pthread_cond_t agent_cond = PTHREAD_COND_INITIALIZER;/* list of Test262Agent.link */static struct list_head agent_list = LIST_HEAD_INIT(agent_list); static pthread_mutex_t report_mutex = PTHREAD_MUTEX_INITIALIZER;/* list of AgentReport.link */static struct list_head report_list = LIST_HEAD_INIT(report_list); static void *agent_start(void *arg){ Test262Agent *agent = arg; JSRuntime *rt; JSContext *ctx; JSValue ret_val; int ret; rt = JS_NewRuntime(); if (rt == NULL) { fatal(1, "JS_NewRuntime failure"); } ctx = JS_NewContext(rt); if (ctx == NULL) { JS_FreeRuntime(rt); fatal(1, "JS_NewContext failure"); } JS_SetContextOpaque(ctx, agent); JS_SetRuntimeInfo(rt, "agent"); JS_SetCanBlock(rt, TRUE); add_helpers(ctx); ret_val = JS_Eval(ctx, agent->script, strlen(agent->script), "<evalScript>", JS_EVAL_TYPE_GLOBAL); free(agent->script); agent->script = NULL; if (JS_IsException(ret_val)) js_std_dump_error(ctx); JS_FreeValue(ctx, ret_val); for(;;) { JSContext *ctx1; ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); if (ret < 0) { js_std_dump_error(ctx); break; } else if (ret == 0) { if (JS_IsUndefined(agent->broadcast_func)) { break; } else { JSValue args[2]; pthread_mutex_lock(&agent_mutex); while (!agent->broadcast_pending) { pthread_cond_wait(&agent_cond, &agent_mutex); } agent->broadcast_pending = FALSE;Lines 1–500 of 2107. Load more
/* * ECMA Test 262 Runner for QuickJS * * Copyright (c) 2017-2020 Fabrice Bellard * Copyright (c) 2017-2020 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include <stdlib.h> #include <stdio.h> #include <stdarg.h> #include <inttypes.h> #include <string.h> #include <assert.h> #include <ctype.h> #include <unistd.h> #include <errno.h> #include <time.h> #include <dirent.h> #include <ftw.h> #include "cutils.h" #include "list.h" #include "quickjs-libc.h" /* enable test262 thread support to test SharedArrayBuffer and Atomics */ #define CONFIG_AGENT #define CMD_NAME "run-test262" typedef struct namelist_t { char **array; int count; int size; unsigned int sorted : 1; } namelist_t; namelist_t test_list; namelist_t exclude_list; namelist_t exclude_dir_list; FILE *outfile; enum test_mode_t { TEST_DEFAULT_NOSTRICT, /* run tests as nostrict unless test is flagged as strictonly */ TEST_DEFAULT_STRICT, /* run tests as strict unless test is flagged as nostrict */ TEST_NOSTRICT, /* run tests as nostrict, skip strictonly tests */ TEST_STRICT, /* run tests as strict, skip nostrict tests */ TEST_ALL, /* run tests in both strict and nostrict, unless restricted by spec */ } test_mode = TEST_DEFAULT_NOSTRICT; int skip_async; int skip_module; int new_style; int dump_memory; int stats_count; JSMemoryUsage stats_all, stats_avg, stats_min, stats_max; char *stats_min_filename; char *stats_max_filename; int verbose; char *harness_dir; char *harness_exclude; char *harness_features; char *harness_skip_features; char *error_filename; char *error_file; FILE *error_out; char *report_filename; int update_errors; int test_count, test_failed, test_index, test_skipped, test_excluded; int new_errors, changed_errors, fixed_errors; int async_done; void warning(const char *, ...) __attribute__((__format__(__printf__, 1, 2))); void fatal(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); void warning(const char *fmt, ...) { va_list ap; fflush(stdout); fprintf(stderr, "%s: ", CMD_NAME); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fputc('\n', stderr); } void fatal(int errcode, const char *fmt, ...) { va_list ap; fflush(stdout); fprintf(stderr, "%s: ", CMD_NAME); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fputc('\n', stderr); exit(errcode); } void perror_exit(int errcode, const char *s) { fflush(stdout); fprintf(stderr, "%s: ", CMD_NAME); perror(s); exit(errcode); } char *strdup_len(const char *str, int len) { char *p = malloc(len + 1); memcpy(p, str, len); p[len] = '\0'; return p; } static inline int str_equal(const char *a, const char *b) { return !strcmp(a, b); } char *str_append(char **pp, const char *sep, const char *str) { char *res, *p; size_t len = 0; p = *pp; if (p) { len = strlen(p) + strlen(sep); } res = malloc(len + strlen(str) + 1); if (p) { strcpy(res, p); strcat(res, sep); } strcpy(res + len, str); free(p); return *pp = res; } char *str_strip(char *p) { size_t len = strlen(p); while (len > 0 && isspace((unsigned char)p[len - 1])) p[--len] = '\0'; while (isspace((unsigned char)*p)) p++; return p; } int has_prefix(const char *str, const char *prefix) { return !strncmp(str, prefix, strlen(prefix)); } char *skip_prefix(const char *str, const char *prefix) { int i; for (i = 0;; i++) { if (prefix[i] == '\0') { /* skip the prefix */ str += i; break; } if (str[i] != prefix[i]) break; } return (char *)str; } char *get_basename(const char *filename) { char *p; p = strrchr(filename, '/'); if (!p) return NULL; return strdup_len(filename, p - filename); } char *compose_path(const char *path, const char *name) { int path_len, name_len; char *d, *q; if (!path || path[0] == '\0' || *name == '/') { d = strdup(name); } else { path_len = strlen(path); name_len = strlen(name); d = malloc(path_len + 1 + name_len + 1); if (d) { q = d; memcpy(q, path, path_len); q += path_len; if (path[path_len - 1] != '/') *q++ = '/'; memcpy(q, name, name_len + 1); } } return d; } int namelist_cmp(const char *a, const char *b) { /* compare strings in modified lexicographical order */ for (;;) { int ca = (unsigned char)*a++; int cb = (unsigned char)*b++; if (isdigit(ca) && isdigit(cb)) { int na = ca - '0'; int nb = cb - '0'; while (isdigit(ca = (unsigned char)*a++)) na = na * 10 + ca - '0'; while (isdigit(cb = (unsigned char)*b++)) nb = nb * 10 + cb - '0'; if (na < nb) return -1; if (na > nb) return +1; } if (ca < cb) return -1; if (ca > cb) return +1; if (ca == '\0') return 0; } } int namelist_cmp_indirect(const void *a, const void *b) { return namelist_cmp(*(const char **)a, *(const char **)b); } void namelist_sort(namelist_t *lp) { int i, count; if (lp->count > 1) { qsort(lp->array, lp->count, sizeof(*lp->array), namelist_cmp_indirect); /* remove duplicates */ for (count = i = 1; i < lp->count; i++) { if (namelist_cmp(lp->array[count - 1], lp->array[i]) == 0) { free(lp->array[i]); } else { lp->array[count++] = lp->array[i]; } } lp->count = count; } lp->sorted = 1; } int namelist_find(namelist_t *lp, const char *name) { int a, b, m, cmp; if (!lp->sorted) { namelist_sort(lp); } for (a = 0, b = lp->count; a < b;) { m = a + (b - a) / 2; cmp = namelist_cmp(lp->array[m], name); if (cmp < 0) a = m + 1; else if (cmp > 0) b = m; else return m; } return -1; } void namelist_add(namelist_t *lp, const char *base, const char *name) { char *s; s = compose_path(base, name); if (!s) goto fail; if (lp->count == lp->size) { size_t newsize = lp->size + (lp->size >> 1) + 4; char **a = realloc(lp->array, sizeof(lp->array[0]) * newsize); if (!a) goto fail; lp->array = a; lp->size = newsize; } lp->array[lp->count] = s; lp->count++; return; fail: fatal(1, "allocation failure\n"); } void namelist_load(namelist_t *lp, const char *filename) { char buf[1024]; char *base_name; FILE *f; f = fopen(filename, "rb"); if (!f) { perror_exit(1, filename); } base_name = get_basename(filename); while (fgets(buf, sizeof(buf), f) != NULL) { char *p = str_strip(buf); if (*p == '#' || *p == ';' || *p == '\0') continue; /* line comment */ namelist_add(lp, base_name, p); } free(base_name); fclose(f); } void namelist_add_from_error_file(namelist_t *lp, const char *file) { const char *p, *p0; char *pp; for (p = file; (p = strstr(p, ".js:")) != NULL; p++) { for (p0 = p; p0 > file && p0[-1] != '\n'; p0--) continue; pp = strdup_len(p0, p + 3 - p0); namelist_add(lp, NULL, pp); free(pp); } } void namelist_free(namelist_t *lp) { while (lp->count > 0) { free(lp->array[--lp->count]); } free(lp->array); lp->array = NULL; lp->size = 0; } static int add_test_file(const char *filename, const struct stat *ptr, int flag) { namelist_t *lp = &test_list; if (has_suffix(filename, ".js") && !has_suffix(filename, "_FIXTURE.js")) namelist_add(lp, NULL, filename); return 0; } /* find js files from the directory tree and sort the list */ static void enumerate_tests(const char *path) { namelist_t *lp = &test_list; int start = lp->count; ftw(path, add_test_file, 100); qsort(lp->array + start, lp->count - start, sizeof(*lp->array), namelist_cmp_indirect); } static JSValue js_print(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int i; const char *str; if (outfile) { for (i = 0; i < argc; i++) { if (i != 0) fputc(' ', outfile); str = JS_ToCString(ctx, argv[i]); if (!str) return JS_EXCEPTION; if (!strcmp(str, "Test262:AsyncTestComplete")) { async_done++; } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) { async_done = 2; /* force an error */ } fputs(str, outfile); JS_FreeCString(ctx, str); } fputc('\n', outfile); } return JS_UNDEFINED; } static JSValue js_detachArrayBuffer(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { JS_DetachArrayBuffer(ctx, argv[0]); return JS_UNDEFINED; } static JSValue js_evalScript(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { const char *str; size_t len; JSValue ret; str = JS_ToCStringLen(ctx, &len, argv[0]); if (!str) return JS_EXCEPTION; ret = JS_Eval(ctx, str, len, "<evalScript>", JS_EVAL_TYPE_GLOBAL); JS_FreeCString(ctx, str); return ret; } #ifdef CONFIG_AGENT #include <pthread.h> typedef struct { struct list_head link; pthread_t tid; char *script; JSValue broadcast_func; BOOL broadcast_pending; JSValue broadcast_sab; /* in the main context */ uint8_t *broadcast_sab_buf; size_t broadcast_sab_size; int32_t broadcast_val; } Test262Agent; typedef struct { struct list_head link; char *str; } AgentReport; static JSValue add_helpers1(JSContext *ctx); static void add_helpers(JSContext *ctx); static pthread_mutex_t agent_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t agent_cond = PTHREAD_COND_INITIALIZER; /* list of Test262Agent.link */ static struct list_head agent_list = LIST_HEAD_INIT(agent_list); static pthread_mutex_t report_mutex = PTHREAD_MUTEX_INITIALIZER; /* list of AgentReport.link */ static struct list_head report_list = LIST_HEAD_INIT(report_list); static void *agent_start(void *arg) { Test262Agent *agent = arg; JSRuntime *rt; JSContext *ctx; JSValue ret_val; int ret; rt = JS_NewRuntime(); if (rt == NULL) { fatal(1, "JS_NewRuntime failure"); } ctx = JS_NewContext(rt); if (ctx == NULL) { JS_FreeRuntime(rt); fatal(1, "JS_NewContext failure"); } JS_SetContextOpaque(ctx, agent); JS_SetRuntimeInfo(rt, "agent"); JS_SetCanBlock(rt, TRUE); add_helpers(ctx); ret_val = JS_Eval(ctx, agent->script, strlen(agent->script), "<evalScript>", JS_EVAL_TYPE_GLOBAL); free(agent->script); agent->script = NULL; if (JS_IsException(ret_val)) js_std_dump_error(ctx); JS_FreeValue(ctx, ret_val); for(;;) { JSContext *ctx1; ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); if (ret < 0) { js_std_dump_error(ctx); break; } else if (ret == 0) { if (JS_IsUndefined(agent->broadcast_func)) { break; } else { JSValue args[2]; pthread_mutex_lock(&agent_mutex); while (!agent->broadcast_pending) { pthread_cond_wait(&agent_cond, &agent_mutex); } agent->broadcast_pending = FALSE;