#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"
#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,
TEST_DEFAULT_STRICT,
TEST_NOSTRICT,
TEST_STRICT,
TEST_ALL,
} 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'
) {
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)
{
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);
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
;
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;
}
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;
}
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;
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;
static
struct
list_head agent_list = LIST_HEAD_INIT(agent_list);
static
pthread_mutex_t report_mutex = PTHREAD_MUTEX_INITIALIZER;
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;
pthread_cond_signal(&agent_cond);
pthread_mutex_unlock(&agent_mutex);
args[0] = JS_NewArrayBuffer(ctx, agent->broadcast_sab_buf,
agent->broadcast_sab_size,
NULL, NULL, TRUE);
args[1] = JS_NewInt32(ctx, agent->broadcast_val);
ret_val = JS_Call(ctx, agent->broadcast_func, JS_UNDEFINED,
2, (JSValueConst *)args);
JS_FreeValue(ctx, args[0]);
JS_FreeValue(ctx, args[1]);
if
(JS_IsException(ret_val))
js_std_dump_error(ctx);
JS_FreeValue(ctx, ret_val);
JS_FreeValue(ctx, agent->broadcast_func);
agent->broadcast_func = JS_UNDEFINED;
}
}
}
JS_FreeValue(ctx, agent->broadcast_func);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return
NULL;
}
static
JSValue js_agent_start(JSContext *ctx, JSValue this_val,
int
argc, JSValue *argv)
{
const
char
*script;
Test262Agent *agent;
if
(JS_GetContextOpaque(ctx) != NULL)
return
JS_ThrowTypeError(ctx,
"cannot be called inside an agent"
);
script = JS_ToCString(ctx, argv[0]);
if
(!script)
return
JS_EXCEPTION;
agent =
malloc
(
sizeof
(*agent));
memset
(agent, 0,
sizeof
(*agent));
agent->broadcast_func = JS_UNDEFINED;
agent->broadcast_sab = JS_UNDEFINED;
agent->script = strdup(script);
JS_FreeCString(ctx, script);
list_add_tail(&agent->link, &agent_list);
pthread_create(&agent->tid, NULL, agent_start, agent);
return
JS_UNDEFINED;
}
static
void
js_agent_free(JSContext *ctx)
{
struct
list_head *el, *el1;
Test262Agent *agent;
list_for_each_safe(el, el1, &agent_list) {
agent = list_entry(el, Test262Agent, link);
pthread_join(agent->tid, NULL);
JS_FreeValue(ctx, agent->broadcast_sab);
list_del(&agent->link);
free
(agent);
}
}
static
JSValue js_agent_leaving(JSContext *ctx, JSValue this_val,
int
argc, JSValue *argv)
{
Test262Agent *agent = JS_GetContextOpaque(ctx);
if
(!agent)
return
JS_ThrowTypeError(ctx,
"must be called inside an agent"
);
return
JS_UNDEFINED;
}
static
BOOL
is_broadcast_pending(
void
)
{
struct
list_head *el;
Test262Agent *agent;
list_for_each(el, &agent_list) {
agent = list_entry(el, Test262Agent, link);
if
(agent->broadcast_pending)
return
TRUE;
}
return
FALSE;
}
static
JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val,
int
argc, JSValue *argv)
{
JSValueConst sab = argv[0];
struct
list_head *el;
Test262Agent *agent;
uint8_t *buf;
size_t
buf_size;
int32_t val;
if
(JS_GetContextOpaque(ctx) != NULL)
return
JS_ThrowTypeError(ctx,
"cannot be called inside an agent"
);
buf = JS_GetArrayBuffer(ctx, &buf_size, sab);
if
(!buf)
return
JS_EXCEPTION;
if
(JS_ToInt32(ctx, &val, argv[1]))
return
JS_EXCEPTION;
pthread_mutex_lock(&agent_mutex);
list_for_each(el, &agent_list) {
agent = list_entry(el, Test262Agent, link);
agent->broadcast_pending = TRUE;
agent->broadcast_sab = JS_DupValue(ctx, sab);
agent->broadcast_sab_buf = buf;
agent->broadcast_sab_size = buf_size;
agent->broadcast_val = val;
}
pthread_cond_broadcast(&agent_cond);
while
(is_broadcast_pending()) {
pthread_cond_wait(&agent_cond, &agent_mutex);
}
pthread_mutex_unlock(&agent_mutex);
return
JS_UNDEFINED;
}
static
JSValue js_agent_receiveBroadcast(JSContext *ctx, JSValue this_val,
int
argc, JSValue *argv)
{
Test262Agent *agent = JS_GetContextOpaque(ctx);
if
(!agent)
return
JS_ThrowTypeError(ctx,
"must be called inside an agent"
);
if
(!JS_IsFunction(ctx, argv[0]))
return
JS_ThrowTypeError(ctx,
"expecting function"
);
JS_FreeValue(ctx, agent->broadcast_func);
agent->broadcast_func = JS_DupValue(ctx, argv[0]);
return
JS_UNDEFINED;
}
static
JSValue js_agent_sleep(JSContext *ctx, JSValue this_val,
int
argc, JSValue *argv)
{
uint32_t duration;
if
(JS_ToUint32(ctx, &duration, argv[0]))
return
JS_EXCEPTION;
usleep(duration * 1000);
return
JS_UNDEFINED;
}
static
int64_t get_clock_ms(
void
)
{
struct
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return
(uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000);
}
static
JSValue js_agent_monotonicNow(JSContext *ctx, JSValue this_val,
int
argc, JSValue *argv)
{
return
JS_NewInt64(ctx, get_clock_ms());
}
static
JSValue js_agent_getReport(JSContext *ctx, JSValue this_val,
int
argc, JSValue *argv)
{
AgentReport *rep;
JSValue ret;
pthread_mutex_lock(&report_mutex);
if
(list_empty(&report_list)) {
rep = NULL;
}
else
{
rep = list_entry(report_list.next, AgentReport, link);
list_del(&rep->link);
}
pthread_mutex_unlock(&report_mutex);
if
(rep) {
ret = JS_NewString(ctx, rep->str);
free
(rep->str);
free
(rep);
}
else
{
ret = JS_NULL;
}
return
ret;
}
static
JSValue js_agent_report(JSContext *ctx, JSValue this_val,
int
argc, JSValue *argv)
{
const
char
*str;
AgentReport *rep;
str = JS_ToCString(ctx, argv[0]);
if
(!str)
return
JS_EXCEPTION;
rep =
malloc
(
sizeof
(*rep));
rep->str = strdup(str);
JS_FreeCString(ctx, str);
pthread_mutex_lock(&report_mutex);
list_add_tail(&rep->link, &report_list);
pthread_mutex_unlock(&report_mutex);
return
JS_UNDEFINED;
}
static
const
JSCFunctionListEntry js_agent_funcs[] = {
JS_CFUNC_DEF(
"start"
, 1, js_agent_start ),
JS_CFUNC_DEF(
"getReport"
, 0, js_agent_getReport ),
JS_CFUNC_DEF(
"broadcast"
, 2, js_agent_broadcast ),
JS_CFUNC_DEF(
"report"
, 1, js_agent_report ),
JS_CFUNC_DEF(
"leaving"
, 0, js_agent_leaving ),
JS_CFUNC_DEF(
"receiveBroadcast"
, 1, js_agent_receiveBroadcast ),
JS_CFUNC_DEF(
"sleep"
, 1, js_agent_sleep ),
JS_CFUNC_DEF(
"monotonicNow"
, 0, js_agent_monotonicNow ),
};
static
JSValue js_new_agent(JSContext *ctx)
{
JSValue agent;
agent = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, agent, js_agent_funcs,
countof(js_agent_funcs));
return
agent;
}
#endif
static
JSValue js_createRealm(JSContext *ctx, JSValue this_val,
int
argc, JSValue *argv)
{
JSContext *ctx1;
JSValue ret;
ctx1 = JS_NewContext(JS_GetRuntime(ctx));
if
(!ctx1)
return
JS_ThrowOutOfMemory(ctx);
ret = add_helpers1(ctx1);
JS_FreeContext(ctx1);
return
ret;
}
static
JSValue js_IsHTMLDDA(JSContext *ctx, JSValue this_val,
int
argc, JSValue *argv)
{
return
JS_NULL;
}
static
JSValue add_helpers1(JSContext *ctx)
{
JSValue global_obj;
JSValue obj262, obj;
global_obj = JS_GetGlobalObject(ctx);
JS_SetPropertyStr(ctx, global_obj,
"print"
,
JS_NewCFunction(ctx, js_print,
"print"
, 1));
obj262 = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, obj262,
"detachArrayBuffer"
,
JS_NewCFunction(ctx, js_detachArrayBuffer,
"detachArrayBuffer"
, 1));
JS_SetPropertyStr(ctx, obj262,
"evalScript"
,
JS_NewCFunction(ctx, js_evalScript,
"evalScript"
, 1));
JS_SetPropertyStr(ctx, obj262,
"codePointRange"
,
JS_NewCFunction(ctx, js_string_codePointRange,
"codePointRange"
, 2));
#ifdef CONFIG_AGENT
JS_SetPropertyStr(ctx, obj262,
"agent"
, js_new_agent(ctx));
#endif
JS_SetPropertyStr(ctx, obj262,
"global"
,
JS_DupValue(ctx, global_obj));
JS_SetPropertyStr(ctx, obj262,
"createRealm"
,
JS_NewCFunction(ctx, js_createRealm,
"createRealm"
, 0));
obj = JS_NewCFunction(ctx, js_IsHTMLDDA,
"IsHTMLDDA"
, 0);
JS_SetIsHTMLDDA(ctx, obj);
JS_SetPropertyStr(ctx, obj262,
"IsHTMLDDA"
, obj);
JS_SetPropertyStr(ctx, global_obj,
"$262"
, JS_DupValue(ctx, obj262));
JS_FreeValue(ctx, global_obj);
return
obj262;
}
static
void
add_helpers(JSContext *ctx)
{
JS_FreeValue(ctx, add_helpers1(ctx));
}
static
char
*load_file(
const
char
*filename,
size_t
*lenp)
{
char
*buf;
size_t
buf_len;
buf = (
char
*)js_load_file(NULL, &buf_len, filename);
if
(!buf)
perror_exit(1, filename);
if
(lenp)
*lenp = buf_len;
return
buf;
}
static
JSModuleDef *js_module_loader_test(JSContext *ctx,
const
char
*module_name,
void
*opaque)
{
size_t
buf_len;
uint8_t *buf;
JSModuleDef *m;
JSValue func_val;
buf = js_load_file(ctx, &buf_len, module_name);
if
(!buf) {
JS_ThrowReferenceError(ctx,
"could not load module filename '%s'"
,
module_name);
return
NULL;
}
func_val = JS_Eval(ctx, (
char
*)buf, buf_len, module_name,
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
js_free(ctx, buf);
if
(JS_IsException(func_val))
return
NULL;
m = JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val);
return
m;
}
int
is_line_sep(
char
c)
{
return
(c ==
'\0'
|| c ==
'\n'
|| c ==
'\r'
);
}
char
*find_line(
const
char
*str,
const
char
*line)
{
if
(str) {
const
char
*p;
int
len =
strlen
(line);
for
(p = str; (p =
strstr
(p, line)) != NULL; p += len + 1) {
if
((p == str || is_line_sep(p[-1])) && is_line_sep(p[len]))
return
(
char
*)p;
}
}
return
NULL;
}
int
is_word_sep(
char
c)
{
return
(c ==
'\0'
||
isspace
((unsigned
char
)c) || c ==
','
);
}
char
*find_word(
const
char
*str,
const
char
*word)
{
const
char
*p;
int
len =
strlen
(word);
if
(str && len) {
for
(p = str; (p =
strstr
(p, word)) != NULL; p += len) {
if
((p == str || is_word_sep(p[-1])) && is_word_sep(p[len]))
return
(
char
*)p;
}
}
return
NULL;
}
void
update_exclude_dirs(
void
)
{
namelist_t *lp = &test_list;
namelist_t *ep = &exclude_list;
namelist_t *dp = &exclude_dir_list;
char
*name;
int
i, j, count;
for
(count = i = 0; i < ep->count; i++) {
name = ep->array[i];
if
(has_suffix(name,
"/"
)) {
namelist_add(dp, NULL, name);
free
(name);
}
else
{
ep->array[count++] = name;
}
}
ep->count = count;
namelist_sort(dp);
for
(count = i = 0; i < lp->count; i++) {
name = lp->array[i];
for
(j = 0; j < dp->count; j++) {
if
(has_prefix(name, dp->array[j])) {
test_excluded++;
free
(name);
name = NULL;
break
;
}
}
if
(name) {
lp->array[count++] = name;
}
}
lp->count = count;
}
void
load_config(
const
char
*filename)
{
char
buf[1024];
FILE
*f;
char
*base_name;
enum
{
SECTION_NONE = 0,
SECTION_CONFIG,
SECTION_EXCLUDE,
SECTION_FEATURES,
SECTION_TESTS,
} section = SECTION_NONE;
int
lineno = 0;
f =
fopen
(filename,
"rb"
);
if
(!f) {
perror_exit(1, filename);
}
base_name = get_basename(filename);
while
(
fgets
(buf,
sizeof
(buf), f) != NULL) {
char
*p, *q;
lineno++;
p = str_strip(buf);
if
(*p ==
'#'
|| *p ==
';'
|| *p ==
'\0'
)
continue
;
if
(*p ==
"[]"
[0]) {
p++;
p[
strcspn
(p,
"]"
)] =
'\0'
;
if
(str_equal(p,
"config"
))
section = SECTION_CONFIG;
else
if
(str_equal(p,
"exclude"
))
section = SECTION_EXCLUDE;
else
if
(str_equal(p,
"features"
))
section = SECTION_FEATURES;
else
if
(str_equal(p,
"tests"
))
section = SECTION_TESTS;
else
section = SECTION_NONE;
continue
;
}
q =
strchr
(p,
'='
);
if
(q) {
*q++ =
'\0'
;
q = str_strip(q);
}
switch
(section) {
case
SECTION_CONFIG:
if
(!q) {
printf
(
"%s:%d: syntax error\n"
, filename, lineno);
continue
;
}
if
(str_equal(p,
"style"
)) {
new_style = str_equal(q,
"new"
);
continue
;
}
if
(str_equal(p,
"testdir"
)) {
char
*testdir = compose_path(base_name, q);
enumerate_tests(testdir);
free
(testdir);
continue
;
}
if
(str_equal(p,
"harnessdir"
)) {
harness_dir = compose_path(base_name, q);
continue
;
}
if
(str_equal(p,
"harnessexclude"
)) {
str_append(&harness_exclude,
" "
, q);
continue
;
}
if
(str_equal(p,
"features"
)) {
str_append(&harness_features,
" "
, q);
continue
;
}
if
(str_equal(p,
"skip-features"
)) {
str_append(&harness_skip_features,
" "
, q);
continue
;
}
if
(str_equal(p,
"mode"
)) {
if
(str_equal(q,
"default"
) || str_equal(q,
"default-nostrict"
))
test_mode = TEST_DEFAULT_NOSTRICT;
else
if
(str_equal(q,
"default-strict"
))
test_mode = TEST_DEFAULT_STRICT;
else
if
(str_equal(q,
"nostrict"
))
test_mode = TEST_NOSTRICT;
else
if
(str_equal(q,
"strict"
))
test_mode = TEST_STRICT;
else
if
(str_equal(q,
"all"
) || str_equal(q,
"both"
))
test_mode = TEST_ALL;
else
fatal(2,
"unknown test mode: %s"
, q);
continue
;
}
if
(str_equal(p,
"strict"
)) {
if
(str_equal(q,
"skip"
) || str_equal(q,
"no"
))
test_mode = TEST_NOSTRICT;
continue
;
}
if
(str_equal(p,
"nostrict"
)) {
if
(str_equal(q,
"skip"
) || str_equal(q,
"no"
))
test_mode = TEST_STRICT;
continue
;
}
if
(str_equal(p,
"async"
)) {
skip_async = !str_equal(q,
"yes"
);
continue
;
}
if
(str_equal(p,
"module"
)) {
skip_module = !str_equal(q,
"yes"
);
continue
;
}
if
(str_equal(p,
"verbose"
)) {
verbose = str_equal(q,
"yes"
);
continue
;
}
if
(str_equal(p,
"errorfile"
)) {
error_filename = compose_path(base_name, q);
continue
;
}
if
(str_equal(p,
"excludefile"
)) {
char
*path = compose_path(base_name, q);
namelist_load(&exclude_list, path);
free
(path);
continue
;
}
if
(str_equal(p,
"reportfile"
)) {
report_filename = compose_path(base_name, q);
continue
;
}
case
SECTION_EXCLUDE:
namelist_add(&exclude_list, base_name, p);
break
;
case
SECTION_FEATURES:
if
(!q || str_equal(q,
"yes"
))
str_append(&harness_features,
" "
, p);
else
str_append(&harness_skip_features,
" "
, p);
break
;
case
SECTION_TESTS:
namelist_add(&test_list, base_name, p);
break
;
default
:
break
;
}
}
fclose
(f);
free
(base_name);
}
char
*find_error(
const
char
*filename,
int
*pline,
int
is_strict)
{
if
(error_file) {
size_t
len =
strlen
(filename);
const
char
*p, *q, *r;
int
line;
for
(p = error_file; (p =
strstr
(p, filename)) != NULL; p += len) {
if
((p == error_file || p[-1] ==
'\n'
|| p[-1] ==
'('
) && p[len] ==
':'
) {
q = p + len;
line = 1;
if
(*q ==
':'
) {
line =
strtol
(q + 1, (
char
**)&q, 10);
if
(*q ==
':'
)
q++;
}
while
(*q ==
' '
) {
q++;
}
if
(!strstart(q,
"strict mode: "
, &q) != !is_strict)
continue
;
r = q = skip_prefix(q,
"unexpected error: "
);
r +=
strcspn
(r,
"\n"
);
while
(r[0] ==
'\n'
&& r[1] &&
strncmp
(r + 1, filename, 8)) {
r++;
r +=
strcspn
(r,
"\n"
);
}
if
(pline)
*pline = line;
return
strdup_len(q, r - q);
}
}
}
return
NULL;
}
int
skip_comments(
const
char
*str,
int
line,
int
*pline)
{
const
char
*p;
int
c;
p = str;
while
((c = (unsigned
char
)*p++) !=
'\0'
) {
if
(
isspace
(c)) {
if
(c ==
'\n'
)
line++;
continue
;
}
if
(c ==
'/'
&& *p ==
'/'
) {
while
(*++p && *p !=
'\n'
)
continue
;
continue
;
}
if
(c ==
'/'
&& *p ==
'*'
) {
for
(p += 1; *p; p++) {
if
(*p ==
'\n'
) {
line++;
continue
;
}
if
(*p ==
'*'
&& p[1] ==
'/'
) {
p += 2;
break
;
}
}
continue
;
}
break
;
}
if
(pline)
*pline = line;
return
p - str;
}
int
longest_match(
const
char
*str,
const
char
*find,
int
pos,
int
*ppos,
int
line,
int
*pline)
{
int
len, maxlen;
maxlen = 0;
if
(*find) {
const
char
*p;
for
(p = str + pos; *p; p++) {
if
(*p == *find) {
for
(len = 1; p[len] && p[len] == find[len]; len++)
continue
;
if
(len > maxlen) {
maxlen = len;
if
(ppos)
*ppos = p - str;
if
(pline)
*pline = line;
if
(!find[len])
break
;
}
}
if
(*p ==
'\n'
)
line++;
}
}
return
maxlen;
}
static
int
eval_buf(JSContext *ctx,
const
char
*buf,
size_t
buf_len,
const
char
*filename,
int
is_test,
int
is_negative,
const
char
*error_type,
FILE
*outfile,
int
eval_flags,
int
is_async)
{
JSValue res_val, exception_val;
int
ret, error_line, pos, pos_line;
BOOL
is_error, has_error_line, ret_promise;
const
char
*error_name;
pos = skip_comments(buf, 1, &pos_line);
error_line = pos_line;
has_error_line = FALSE;
exception_val = JS_UNDEFINED;
error_name = NULL;
ret_promise = ((eval_flags & JS_EVAL_TYPE_MODULE) != 0);
async_done = 0;
res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
if
((is_async || ret_promise) && !JS_IsException(res_val)) {
JSValue promise = JS_UNDEFINED;
if
(ret_promise) {
promise = res_val;
}
else
{
JS_FreeValue(ctx, res_val);
}
for
(;;) {
JSContext *ctx1;
ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
if
(ret < 0) {
res_val = JS_EXCEPTION;
break
;
}
else
if
(ret == 0) {
if
(is_async) {
if
(async_done != 1) {
res_val = JS_ThrowTypeError(ctx,
"$DONE() not called"
);
}
else
{
res_val = JS_UNDEFINED;
}
}
else
{
JSPromiseStateEnum state = JS_PromiseState(ctx, promise);
if
(state == JS_PROMISE_FULFILLED)
res_val = JS_UNDEFINED;
else
if
(state == JS_PROMISE_REJECTED)
res_val = JS_Throw(ctx, JS_PromiseResult(ctx, promise));
else
res_val = JS_ThrowTypeError(ctx,
"promise is pending"
);
}
break
;
}
}
JS_FreeValue(ctx, promise);
}
if
(JS_IsException(res_val)) {
exception_val = JS_GetException(ctx);
is_error = JS_IsError(ctx, exception_val);
if
(outfile) {
if
(!is_error)
fprintf
(outfile,
"%sThrow: "
, (eval_flags & JS_EVAL_FLAG_STRICT) ?
"strict mode: "
:
""
);
js_print(ctx, JS_NULL, 1, &exception_val);
}
if
(is_error) {
JSValue name, stack;
const
char
*stack_str;
name = JS_GetPropertyStr(ctx, exception_val,
"name"
);
error_name = JS_ToCString(ctx, name);
stack = JS_GetPropertyStr(ctx, exception_val,
"stack"
);
if
(!JS_IsUndefined(stack)) {
stack_str = JS_ToCString(ctx, stack);
if
(stack_str) {
const
char
*p;
int
len;
if
(outfile)
fprintf
(outfile,
"%s"
, stack_str);
len =
strlen
(filename);
p =
strstr
(stack_str, filename);
if
(p != NULL && p[len] ==
':'
) {
error_line =
atoi
(p + len + 1);
has_error_line = TRUE;
}
JS_FreeCString(ctx, stack_str);
}
}
JS_FreeValue(ctx, stack);
JS_FreeValue(ctx, name);
}
if
(is_negative) {
ret = 0;
if
(error_type) {
char
*error_class;
const
char
*msg;
msg = JS_ToCString(ctx, exception_val);
error_class = strdup_len(msg,
strcspn
(msg,
":"
));
if
(!str_equal(error_class, error_type))
ret = -1;
free
(error_class);
JS_FreeCString(ctx, msg);
}
}
else
{
ret = -1;
}
}
else
{
if
(is_negative)
ret = -1;
else
ret = 0;
}
if
(verbose && is_test) {
JSValue msg_val = JS_UNDEFINED;
const
char
*msg = NULL;
int
s_line;
char
*s = find_error(filename, &s_line, eval_flags & JS_EVAL_FLAG_STRICT);
const
char
*strict_mode = (eval_flags & JS_EVAL_FLAG_STRICT) ?
"strict mode: "
:
""
;
if
(!JS_IsUndefined(exception_val)) {
msg_val = JS_ToString(ctx, exception_val);
msg = JS_ToCString(ctx, msg_val);
}
if
(is_negative) {
if
(ret == 0) {
if
(msg && s &&
(str_equal(s,
"expected error"
) ||
strstart(s,
"unexpected error type:"
, NULL) ||
str_equal(s, msg))) {
if
(!has_error_line) {
longest_match(buf, msg, pos, &pos, pos_line, &error_line);
}
printf
(
"%s:%d: %sOK, now has error %s\n"
,
filename, error_line, strict_mode, msg);
fixed_errors++;
}
}
else
{
if
(!s) {
if
(msg) {
fprintf
(error_out,
"%s:%d: %sunexpected error type: %s\n"
,
filename, error_line, strict_mode, msg);
}
else
{
fprintf
(error_out,
"%s:%d: %sexpected error\n"
,
filename, error_line, strict_mode);
}
new_errors++;
}
}
}
else
{
if
(msg) {
if
(!s || !str_equal(s, msg)) {
if
(!has_error_line) {
char
*p = skip_prefix(msg,
"Test262 Error: "
);
if
(
strstr
(p,
"Test case returned non-true value!"
)) {
longest_match(buf,
"runTestCase"
, pos, &pos, pos_line, &error_line);
}
else
{
longest_match(buf, p, pos, &pos, pos_line, &error_line);
}
}
fprintf
(error_out,
"%s:%d: %s%s%s\n"
, filename, error_line, strict_mode,
error_file ?
"unexpected error: "
:
""
, msg);
if
(s && (!str_equal(s, msg) || error_line != s_line)) {
printf
(
"%s:%d: %sprevious error: %s\n"
, filename, s_line, strict_mode, s);
changed_errors++;
}
else
{
new_errors++;
}
}
}
else
{
if
(s) {
printf
(
"%s:%d: %sOK, fixed error: %s\n"
, filename, s_line, strict_mode, s);
fixed_errors++;
}
}
}
JS_FreeValue(ctx, msg_val);
JS_FreeCString(ctx, msg);
free
(s);
}
JS_FreeCString(ctx, error_name);
JS_FreeValue(ctx, exception_val);
JS_FreeValue(ctx, res_val);
return
ret;
}
static
int
eval_file(JSContext *ctx,
const
char
*base,
const
char
*p,
int
eval_flags)
{
char
*buf;
size_t
buf_len;
char
*filename = compose_path(base, p);
buf = load_file(filename, &buf_len);
if
(!buf) {
warning(
"cannot load %s"
, filename);
goto
fail;
}
if
(eval_buf(ctx, buf, buf_len, filename, FALSE, FALSE, NULL, stderr,
eval_flags, FALSE)) {
warning(
"error evaluating %s"
, filename);
goto
fail;
}
free
(buf);
free
(filename);
return
0;
fail:
free
(buf);
free
(filename);
return
1;
}
char
*extract_desc(
const
char
*buf,
char
style)
{
const
char
*p, *desc_start;
char
*desc;
int
len;
p = buf;
while
(*p !=
'\0'
) {
if
(p[0] ==
'/'
&& p[1] ==
'*'
&& p[2] == style && p[3] !=
'/'
) {
p += 3;
desc_start = p;
while
(*p !=
'\0'
&& (p[0] !=
'*'
|| p[1] !=
'/'
))
p++;
if
(*p ==
'\0'
) {
warning(
"Expecting end of desc comment"
);
return
NULL;
}
len = p - desc_start;
desc =
malloc
(len + 1);
memcpy
(desc, desc_start, len);
desc[len] =
'\0'
;
return
desc;
}
else
{
p++;
}
}
return
NULL;
}
static
char
*find_tag(
char
*desc,
const
char
*tag,
int
*state)
{
char
*p;
p =
strstr
(desc, tag);
if
(p) {
p +=
strlen
(tag);
*state = 0;
}
return
p;
}
static
char
*get_option(
char
**pp,
int
*state)
{
char
*p, *p0, *option = NULL;
if
(*pp) {
for
(p = *pp;; p++) {
switch
(*p) {
case
'['
:
*state += 1;
continue
;
case
']'
:
*state -= 1;
if
(*state > 0)
continue
;
p = NULL;
break
;
case
' '
:
case
'\t'
:
case
'\r'
:
case
','
:
case
'-'
:
continue
;
case
'\n'
:
if
(*state > 0 || p[1] ==
' '
)
continue
;
p = NULL;
break
;
case
'\0'
:
p = NULL;
break
;
default
:
p0 = p;
p +=
strcspn
(p0,
" \t\r\n,]"
);
option = strdup_len(p0, p - p0);
break
;
}
break
;
}
*pp = p;
}
return
option;
}
void
update_stats(JSRuntime *rt,
const
char
*filename) {
JSMemoryUsage stats;
JS_ComputeMemoryUsage(rt, &stats);
if
(stats_count++ == 0) {
stats_avg = stats_all = stats_min = stats_max = stats;
stats_min_filename = strdup(filename);
stats_max_filename = strdup(filename);
}
else
{
if
(stats_max.malloc_size < stats.malloc_size) {
stats_max = stats;
free
(stats_max_filename);
stats_max_filename = strdup(filename);
}
if
(stats_min.malloc_size > stats.malloc_size) {
stats_min = stats;
free
(stats_min_filename);
stats_min_filename = strdup(filename);
}
#define update(f) stats_avg.f = (stats_all.f += stats.f) / stats_count
update(malloc_count);
update(malloc_size);
update(memory_used_count);
update(memory_used_size);
update(atom_count);
update(atom_size);
update(str_count);
update(str_size);
update(obj_count);
update(obj_size);
update(prop_count);
update(prop_size);
update(shape_count);
update(shape_size);
update(js_func_count);
update(js_func_size);
update(js_func_code_size);
update(js_func_pc2line_count);
update(js_func_pc2line_size);
update(c_func_count);
update(array_count);
update(fast_array_count);
update(fast_array_elements);
}
#undef update
}
int
run_test_buf(
const
char
*filename,
const
char
*harness, namelist_t *ip,
char
*buf,
size_t
buf_len,
const
char
* error_type,
int
eval_flags,
BOOL
is_negative,
BOOL
is_async,
BOOL
can_block)
{
JSRuntime *rt;
JSContext *ctx;
int
i, 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_SetRuntimeInfo(rt, filename);
JS_SetCanBlock(rt, can_block);
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL);
add_helpers(ctx);
for
(i = 0; i < ip->count; i++) {
if
(eval_file(ctx, harness, ip->array[i],
JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRIP)) {
fatal(1,
"error including %s for %s"
, ip->array[i], filename);
}
}
ret = eval_buf(ctx, buf, buf_len, filename, TRUE, is_negative,
error_type, outfile, eval_flags, is_async);
ret = (ret != 0);
if
(dump_memory) {
update_stats(rt, filename);
}
#ifdef CONFIG_AGENT
js_agent_free(ctx);
#endif
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
test_count++;
if
(ret) {
test_failed++;
if
(outfile) {
fprintf
(outfile,
" FAILED\n"
);
}
}
return
ret;
}
int
run_test(
const
char
*filename,
int
index)
{
char
harnessbuf[1024];
char
*harness;
char
*buf;
size_t
buf_len;
char
*desc, *p;
char
*error_type;
int
ret, eval_flags, use_strict, use_nostrict;
BOOL
is_negative, is_nostrict, is_onlystrict, is_async, is_module, skip;
BOOL
can_block;
namelist_t include_list = { 0 }, *ip = &include_list;
is_nostrict = is_onlystrict = is_negative = is_async = is_module = skip = FALSE;
can_block = TRUE;
error_type = NULL;
buf = load_file(filename, &buf_len);
harness = harness_dir;
if
(new_style) {
if
(!harness) {
p =
strstr
(filename,
"test/"
);
if
(p) {
snprintf(harnessbuf,
sizeof
(harnessbuf),
"%.*s%s"
,
(
int
)(p - filename), filename,
"harness"
);
}
else
{
pstrcpy(harnessbuf,
sizeof
(harnessbuf),
""
);
}
harness = harnessbuf;
}
namelist_add(ip, NULL,
"sta.js"
);
namelist_add(ip, NULL,
"assert.js"
);
desc = extract_desc(buf,
'-'
);
if
(desc) {
char
*ifile, *option;
int
state;
p = find_tag(desc,
"includes:"
, &state);
if
(p) {
while
((ifile = get_option(&p, &state)) != NULL) {
if
(find_word(harness_exclude, ifile)) {
skip |= 1;
}
else
{
namelist_add(ip, NULL, ifile);
}
free
(ifile);
}
}
p = find_tag(desc,
"flags:"
, &state);
if
(p) {
while
((option = get_option(&p, &state)) != NULL) {
if
(str_equal(option,
"noStrict"
) ||
str_equal(option,
"raw"
)) {
is_nostrict = TRUE;
skip |= (test_mode == TEST_STRICT);
}
else
if
(str_equal(option,
"onlyStrict"
)) {
is_onlystrict = TRUE;
skip |= (test_mode == TEST_NOSTRICT);
}
else
if
(str_equal(option,
"async"
)) {
is_async = TRUE;
skip |= skip_async;
}
else
if
(str_equal(option,
"module"
)) {
is_module = TRUE;
skip |= skip_module;
}
else
if
(str_equal(option,
"CanBlockIsFalse"
)) {
can_block = FALSE;
}
free
(option);
}
}
p = find_tag(desc,
"negative:"
, &state);
if
(p) {
char
*q = find_tag(p,
"type:"
, &state);
if
(q) {
while
(
isspace
(*q))
q++;
error_type = strdup_len(q,
strcspn
(q,
" \n"
));
}
is_negative = TRUE;
}
p = find_tag(desc,
"features:"
, &state);
if
(p) {
while
((option = get_option(&p, &state)) != NULL) {
if
(find_word(harness_features, option)) {
}
else
if
(find_word(harness_skip_features, option)) {
skip |= 1;
}
else
{
printf
(
"%s:%d: unknown feature: %s\n"
, filename, 1, option);
skip |= 1;
}
free
(option);
}
}
free
(desc);
}
if
(is_async)
namelist_add(ip, NULL,
"doneprintHandle.js"
);
}
else
{
char
*ifile;
if
(!harness) {
p =
strstr
(filename,
"test/"
);
if
(p) {
snprintf(harnessbuf,
sizeof
(harnessbuf),
"%.*s%s"
,
(
int
)(p - filename), filename,
"test/harness"
);
}
else
{
pstrcpy(harnessbuf,
sizeof
(harnessbuf),
""
);
}
harness = harnessbuf;
}
namelist_add(ip, NULL,
"sta.js"
);
for
(p = buf; (p =
strstr
(p,
"$INCLUDE(\""
)) != NULL; p++) {
p += 10;
ifile = strdup_len(p,
strcspn
(p,
"\""
));
if
(find_word(harness_exclude, ifile)) {
skip |= 1;
}
else
{
namelist_add(ip, NULL, ifile);
}
free
(ifile);
}
desc = extract_desc(buf,
'*'
);
if
(desc) {
if
(
strstr
(desc,
"@noStrict"
)) {
is_nostrict = TRUE;
skip |= (test_mode == TEST_STRICT);
}
if
(
strstr
(desc,
"@onlyStrict"
)) {
is_onlystrict = TRUE;
skip |= (test_mode == TEST_NOSTRICT);
}
if
(
strstr
(desc,
"@negative"
)) {
is_negative = TRUE;
}
free
(desc);
}
}
if
(outfile && index >= 0) {
fprintf
(outfile,
"%d: %s%s%s%s%s%s%s\n"
, index, filename,
is_nostrict ?
" @noStrict"
:
""
,
is_onlystrict ?
" @onlyStrict"
:
""
,
is_async ?
" async"
:
""
,
is_module ?
" module"
:
""
,
is_negative ?
" @negative"
:
""
,
skip ?
" SKIPPED"
:
""
);
fflush
(outfile);
}
use_strict = use_nostrict = 0;
switch
(test_mode) {
case
TEST_DEFAULT_NOSTRICT:
if
(is_onlystrict)
use_strict = 1;
else
use_nostrict = 1;
break
;
case
TEST_DEFAULT_STRICT:
if
(is_nostrict)
use_nostrict = 1;
else
use_strict = 1;
break
;
case
TEST_NOSTRICT:
if
(!is_onlystrict)
use_nostrict = 1;
break
;
case
TEST_STRICT:
if
(!is_nostrict)
use_strict = 1;
break
;
case
TEST_ALL:
if
(is_module) {
use_nostrict = 1;
}
else
{
if
(!is_nostrict)
use_strict = 1;
if
(!is_onlystrict)
use_nostrict = 1;
}
break
;
}
if
(skip || use_strict + use_nostrict == 0) {
test_skipped++;
ret = -2;
}
else
{
clock_t
clocks;
if
(is_module) {
eval_flags = JS_EVAL_TYPE_MODULE;
}
else
{
eval_flags = JS_EVAL_TYPE_GLOBAL;
}
clocks =
clock
();
ret = 0;
if
(use_nostrict) {
ret = run_test_buf(filename, harness, ip, buf, buf_len,
error_type, eval_flags, is_negative, is_async,
can_block);
}
if
(use_strict) {
ret |= run_test_buf(filename, harness, ip, buf, buf_len,
error_type, eval_flags | JS_EVAL_FLAG_STRICT,
is_negative, is_async, can_block);
}
clocks =
clock
() - clocks;
if
(outfile && index >= 0 && clocks >= CLOCKS_PER_SEC / 10) {
fprintf
(outfile,
" time: %d ms\n"
, (
int
)(clocks * 1000LL / CLOCKS_PER_SEC));
}
}
namelist_free(&include_list);
free
(error_type);
free
(buf);
return
ret;
}
int
run_test262_harness_test(
const
char
*filename,
BOOL
is_module)
{
JSRuntime *rt;
JSContext *ctx;
char
*buf;
size_t
buf_len;
int
eval_flags, ret_code, ret;
JSValue res_val;
BOOL
can_block;
outfile = stdout;
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_SetRuntimeInfo(rt, filename);
can_block = TRUE;
JS_SetCanBlock(rt, can_block);
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL);
add_helpers(ctx);
buf = load_file(filename, &buf_len);
if
(is_module) {
eval_flags = JS_EVAL_TYPE_MODULE;
}
else
{
eval_flags = JS_EVAL_TYPE_GLOBAL;
}
res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
ret_code = 0;
if
(JS_IsException(res_val)) {
js_std_dump_error(ctx);
ret_code = 1;
}
else
{
JSValue promise = JS_UNDEFINED;
if
(is_module) {
promise = res_val;
}
else
{
JS_FreeValue(ctx, res_val);
}
for
(;;) {
JSContext *ctx1;
ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
if
(ret < 0) {
js_std_dump_error(ctx1);
ret_code = 1;
}
else
if
(ret == 0) {
break
;
}
}
if
(is_module) {
JSPromiseStateEnum state = JS_PromiseState(ctx, promise);
if
(state == JS_PROMISE_REJECTED) {
JS_Throw(ctx, JS_PromiseResult(ctx, promise));
js_std_dump_error(ctx);
ret_code = 1;
}
}
JS_FreeValue(ctx, promise);
}
free
(buf);
#ifdef CONFIG_AGENT
js_agent_free(ctx);
#endif
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return
ret_code;
}
clock_t
last_clock;
void
show_progress(
int
force) {
clock_t
t =
clock
();
if
(force || !last_clock || (t - last_clock) > CLOCKS_PER_SEC / 20) {
last_clock = t;
fprintf
(stderr,
"%d/%d/%d\033[K\r"
,
test_failed, test_count, test_skipped);
fflush
(stderr);
}
}
static
int
slow_test_threshold;
void
run_test_dir_list(namelist_t *lp,
int
start_index,
int
stop_index)
{
int
i;
namelist_sort(lp);
for
(i = 0; i < lp->count; i++) {
const
char
*p = lp->array[i];
if
(namelist_find(&exclude_list, p) >= 0) {
test_excluded++;
}
else
if
(test_index < start_index) {
test_skipped++;
}
else
if
(stop_index >= 0 && test_index > stop_index) {
test_skipped++;
}
else
{
int
ti;
if
(slow_test_threshold != 0) {
ti = get_clock_ms();
}
else
{
ti = 0;
}
run_test(p, test_index);
if
(slow_test_threshold != 0) {
ti = get_clock_ms() - ti;
if
(ti >= slow_test_threshold)
fprintf
(stderr,
"\n%s (%d ms)\n"
, p, ti);
}
show_progress(FALSE);
}
test_index++;
}
show_progress(TRUE);
}
void
help(
void
)
{
printf
(
"run-test262 version "
CONFIG_VERSION
"\n"
"usage: run-test262 [options] {-f file ... | [dir_list] [index range]}\n"
"-h help\n"
"-a run tests in strict and nostrict modes\n"
"-m print memory usage summary\n"
"-n use new style harness\n"
"-N run test prepared by test262-harness+eshost\n"
"-s run tests in strict mode, skip @nostrict tests\n"
"-E only run tests from the error file\n"
"-u update error file\n"
"-v verbose: output error messages\n"
"-T duration display tests taking more than 'duration' ms\n"
"-c file read configuration from 'file'\n"
"-d dir run all test files in directory tree 'dir'\n"
"-e file load the known errors from 'file'\n"
"-f file execute single test from 'file'\n"
"-r file set the report file name (default=none)\n"
"-x file exclude tests listed in 'file'\n"
);
exit
(1);
}
char
*get_opt_arg(
const
char
*option,
char
*arg)
{
if
(!arg) {
fatal(2,
"missing argument for option %s"
, option);
}
return
arg;
}
int
main(
int
argc,
char
**argv)
{
int
optind, start_index, stop_index;
BOOL
is_dir_list;
BOOL
only_check_errors = FALSE;
const
char
*filename;
BOOL
is_test262_harness = FALSE;
BOOL
is_module = FALSE;
#if !defined(_WIN32)
setenv(
"TZ"
,
"America/Los_Angeles"
, 1);
#endif
optind = 1;
is_dir_list = TRUE;
while
(optind < argc) {
char
*arg = argv[optind];
if
(*arg !=
'-'
)
break
;
optind++;
if
(str_equal(arg,
"-h"
)) {
help();
}
else
if
(str_equal(arg,
"-m"
)) {
dump_memory++;
}
else
if
(str_equal(arg,
"-n"
)) {
new_style++;
}
else
if
(str_equal(arg,
"-s"
)) {
test_mode = TEST_STRICT;
}
else
if
(str_equal(arg,
"-a"
)) {
test_mode = TEST_ALL;
}
else
if
(str_equal(arg,
"-u"
)) {
update_errors++;
}
else
if
(str_equal(arg,
"-v"
)) {
verbose++;
}
else
if
(str_equal(arg,
"-c"
)) {
load_config(get_opt_arg(arg, argv[optind++]));
}
else
if
(str_equal(arg,
"-d"
)) {
enumerate_tests(get_opt_arg(arg, argv[optind++]));
}
else
if
(str_equal(arg,
"-e"
)) {
error_filename = get_opt_arg(arg, argv[optind++]);
}
else
if
(str_equal(arg,
"-x"
)) {
namelist_load(&exclude_list, get_opt_arg(arg, argv[optind++]));
}
else
if
(str_equal(arg,
"-f"
)) {
is_dir_list = FALSE;
}
else
if
(str_equal(arg,
"-r"
)) {
report_filename = get_opt_arg(arg, argv[optind++]);
}
else
if
(str_equal(arg,
"-E"
)) {
only_check_errors = TRUE;
}
else
if
(str_equal(arg,
"-T"
)) {
slow_test_threshold =
atoi
(get_opt_arg(arg, argv[optind++]));
}
else
if
(str_equal(arg,
"-N"
)) {
is_test262_harness = TRUE;
}
else
if
(str_equal(arg,
"--module"
)) {
is_module = TRUE;
}
else
{
fatal(1,
"unknown option: %s"
, arg);
break
;
}
}
if
(optind >= argc && !test_list.count)
help();
if
(is_test262_harness) {
return
run_test262_harness_test(argv[optind], is_module);
}
error_out = stdout;
if
(error_filename) {
error_file = load_file(error_filename, NULL);
if
(only_check_errors && error_file) {
namelist_free(&test_list);
namelist_add_from_error_file(&test_list, error_file);
}
if
(update_errors) {
free
(error_file);
error_file = NULL;
error_out =
fopen
(error_filename,
"w"
);
if
(!error_out) {
perror_exit(1, error_filename);
}
}
}
update_exclude_dirs();
if
(is_dir_list) {
if
(optind < argc && !
isdigit
(argv[optind][0])) {
filename = argv[optind++];
namelist_load(&test_list, filename);
}
start_index = 0;
stop_index = -1;
if
(optind < argc) {
start_index =
atoi
(argv[optind++]);
if
(optind < argc) {
stop_index =
atoi
(argv[optind++]);
}
}
if
(!report_filename || str_equal(report_filename,
"none"
)) {
outfile = NULL;
}
else
if
(str_equal(report_filename,
"-"
)) {
outfile = stdout;
}
else
{
outfile =
fopen
(report_filename,
"wb"
);
if
(!outfile) {
perror_exit(1, report_filename);
}
}
run_test_dir_list(&test_list, start_index, stop_index);
if
(outfile && outfile != stdout) {
fclose
(outfile);
outfile = NULL;
}
}
else
{
outfile = stdout;
while
(optind < argc) {
run_test(argv[optind++], -1);
}
}
if
(dump_memory) {
if
(dump_memory > 1 && stats_count > 1) {
printf
(
"\nMininum memory statistics for %s:\n\n"
, stats_min_filename);
JS_DumpMemoryUsage(stdout, &stats_min, NULL);
printf
(
"\nMaximum memory statistics for %s:\n\n"
, stats_max_filename);
JS_DumpMemoryUsage(stdout, &stats_max, NULL);
}
printf
(
"\nAverage memory statistics for %d tests:\n\n"
, stats_count);
JS_DumpMemoryUsage(stdout, &stats_avg, NULL);
printf
(
"\n"
);
}
if
(is_dir_list) {
fprintf
(stderr,
"Result: %d/%d error%s"
,
test_failed, test_count, test_count != 1 ?
"s"
:
""
);
if
(test_excluded)
fprintf
(stderr,
", %d excluded"
, test_excluded);
if
(test_skipped)
fprintf
(stderr,
", %d skipped"
, test_skipped);
if
(error_file) {
if
(new_errors)
fprintf
(stderr,
", %d new"
, new_errors);
if
(changed_errors)
fprintf
(stderr,
", %d changed"
, changed_errors);
if
(fixed_errors)
fprintf
(stderr,
", %d fixed"
, fixed_errors);
}
fprintf
(stderr,
"\n"
);
}
if
(error_out && error_out != stdout) {
fclose
(error_out);
error_out = NULL;
}
namelist_free(&test_list);
namelist_free(&exclude_list);
namelist_free(&exclude_dir_list);
free
(harness_dir);
free
(harness_features);
free
(harness_exclude);
free
(error_file);
return
0;
}