This is an automated email from the ASF dual-hosted git repository. vatamane pushed a commit to branch update-quickjs in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 84800246ea5195241efc4beb3cf1fc23adf37771 Author: Nick Vatamaniuc <[email protected]> AuthorDate: Thu Sep 18 11:45:01 2025 -0400 Update QuickJS - New official release: 2025-09-13 - Fix crash on bytecode read https://github.com/bellard/quickjs/commit/391cd3feff17ebdf572230bd22d1986f18c9f81f) - Improve pretty printing of strings https://github.com/bellard/quickjs/commit/8a0a6e92d2fc01331b4a7c80f7d7569625af352a - More efficient handling of strings in `JSON.stringify()` https://github.com/bellard/quickjs/commit/9f6c1907316caea933ae3ed9dbfa3f2c4897525c - Fix array memory leak: https://github.com/bellard/quickjs/commit/de4d3927b8edff5fbfee1f69cfeef840844259e9 --- .../patches/01-spidermonkey-185-mode.patch | 6 +- .../patches/02-test262-makefile.patch | 4 +- src/couch_quickjs/patches/03-test262-yield.patch | 4 +- src/couch_quickjs/patches/04-test262-errors.patch | 4 +- src/couch_quickjs/quickjs/Changelog | 3 + src/couch_quickjs/quickjs/VERSION | 2 +- src/couch_quickjs/quickjs/qjsc.c | 3 + src/couch_quickjs/quickjs/quickjs.c | 257 ++++++++++++--------- src/couch_quickjs/quickjs/quickjs.h | 4 +- src/couch_quickjs/quickjs/run-test262.c | 43 +++- 10 files changed, 206 insertions(+), 124 deletions(-) diff --git a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch index 20ac3172e..15443fe4d 100644 --- a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch +++ b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch @@ -1,6 +1,6 @@ ---- quickjs-master/quickjs.c 2025-08-25 12:20:58 -+++ quickjs/quickjs.c 2025-08-25 15:10:50 -@@ -30776,10 +30776,24 @@ +--- quickjs-master/quickjs.c 2025-09-18 04:42:13 ++++ quickjs/quickjs.c 2025-09-18 11:43:37 +@@ -30751,10 +30751,24 @@ if (s->token.val == TOK_FUNCTION || (token_is_pseudo_keyword(s, JS_ATOM_async) && peek_token(s, TRUE) == TOK_FUNCTION)) { diff --git a/src/couch_quickjs/patches/02-test262-makefile.patch b/src/couch_quickjs/patches/02-test262-makefile.patch index a4fb9b4b1..dd362b8ab 100644 --- a/src/couch_quickjs/patches/02-test262-makefile.patch +++ b/src/couch_quickjs/patches/02-test262-makefile.patch @@ -1,5 +1,5 @@ ---- quickjs-master/Makefile 2025-08-25 12:20:58 -+++ quickjs/Makefile 2025-08-25 15:27:47 +--- quickjs-master/Makefile 2025-09-18 04:42:13 ++++ quickjs/Makefile 2025-09-18 11:43:37 @@ -53,6 +53,10 @@ #CONFIG_MSAN=y # use UB sanitizer diff --git a/src/couch_quickjs/patches/03-test262-yield.patch b/src/couch_quickjs/patches/03-test262-yield.patch index 419e609c3..b4ef63866 100644 --- a/src/couch_quickjs/patches/03-test262-yield.patch +++ b/src/couch_quickjs/patches/03-test262-yield.patch @@ -1,5 +1,5 @@ ---- quickjs-master/tests/test262.patch 2025-08-25 12:20:58 -+++ quickjs/tests/test262.patch 2025-08-25 15:10:50 +--- quickjs-master/tests/test262.patch 2025-09-18 04:42:13 ++++ quickjs/tests/test262.patch 2025-09-18 11:43:37 @@ -14,9 +14,9 @@ +// small: 200, +// long: 1000, diff --git a/src/couch_quickjs/patches/04-test262-errors.patch b/src/couch_quickjs/patches/04-test262-errors.patch index d37155640..a393222d8 100644 --- a/src/couch_quickjs/patches/04-test262-errors.patch +++ b/src/couch_quickjs/patches/04-test262-errors.patch @@ -1,5 +1,5 @@ ---- quickjs-master/test262_errors.txt 2025-08-25 12:20:58 -+++ quickjs/test262_errors.txt 2025-08-25 15:13:35 +--- quickjs-master/test262_errors.txt 2025-09-18 04:42:13 ++++ quickjs/test262_errors.txt 2025-09-18 11:43:37 @@ -7,6 +7,8 @@ test262/test/annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js:20: SyntaxError: invalid assignment left-hand side test262/test/built-ins/Atomics/notify/retrieve-length-before-index-coercion-non-shared-detached.js:34: TypeError: ArrayBuffer is detached diff --git a/src/couch_quickjs/quickjs/Changelog b/src/couch_quickjs/quickjs/Changelog index b1443f5ad..8a32a92c1 100644 --- a/src/couch_quickjs/quickjs/Changelog +++ b/src/couch_quickjs/quickjs/Changelog @@ -1,3 +1,5 @@ +2025-09-13: + - added JSON modules and import attributes - added JS_PrintValue() API - qjs: pretty print objects in print() and console.log() @@ -12,6 +14,7 @@ accept JSON5 modules - added JS_FreePropertyEnum() and JS_AtomToCStringLen() API - added Error.isError() +- misc bug fixes 2025-04-26: diff --git a/src/couch_quickjs/quickjs/VERSION b/src/couch_quickjs/quickjs/VERSION index c76e76d1f..433b8f85b 100644 --- a/src/couch_quickjs/quickjs/VERSION +++ b/src/couch_quickjs/quickjs/VERSION @@ -1 +1 @@ -2025-04-26 +2025-09-13 diff --git a/src/couch_quickjs/quickjs/qjsc.c b/src/couch_quickjs/quickjs/qjsc.c index 49aa44985..e55ca61ce 100644 --- a/src/couch_quickjs/quickjs/qjsc.c +++ b/src/couch_quickjs/quickjs/qjsc.c @@ -362,6 +362,9 @@ static void compile_file(JSContext *ctx, FILE *fo, pstrcpy(c_name, sizeof(c_name), c_name1); } else { get_c_name(c_name, sizeof(c_name), filename); + if (namelist_find(&cname_list, c_name)) { + find_unique_cname(c_name, sizeof(c_name)); + } } output_object_code(ctx, fo, obj, c_name, CNAME_TYPE_SCRIPT); JS_FreeValue(ctx, obj); diff --git a/src/couch_quickjs/quickjs/quickjs.c b/src/couch_quickjs/quickjs/quickjs.c index 2f1291702..b0f9d03ad 100644 --- a/src/couch_quickjs/quickjs/quickjs.c +++ b/src/couch_quickjs/quickjs/quickjs.c @@ -13002,73 +13002,6 @@ static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) return JS_ToString(ctx, val); } -static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) -{ - JSValue val; - JSString *p; - int i; - uint32_t c; - StringBuffer b_s, *b = &b_s; - char buf[16]; - - val = JS_ToStringCheckObject(ctx, val1); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - - if (string_buffer_init(ctx, b, p->len + 2)) - goto fail; - - if (string_buffer_putc8(b, '\"')) - goto fail; - for(i = 0; i < p->len; ) { - c = string_getc(p, &i); - switch(c) { - case '\t': - c = 't'; - goto quote; - case '\r': - c = 'r'; - goto quote; - case '\n': - c = 'n'; - goto quote; - case '\b': - c = 'b'; - goto quote; - case '\f': - c = 'f'; - goto quote; - case '\"': - case '\\': - quote: - if (string_buffer_putc8(b, '\\')) - goto fail; - if (string_buffer_putc8(b, c)) - goto fail; - break; - default: - if (c < 32 || is_surrogate(c)) { - snprintf(buf, sizeof(buf), "\\u%04x", c); - if (string_buffer_puts8(b, buf)) - goto fail; - } else { - if (string_buffer_putc(b, c)) - goto fail; - } - break; - } - } - if (string_buffer_putc8(b, '\"')) - goto fail; - JS_FreeValue(ctx, val); - return string_buffer_end(b); - fail: - JS_FreeValue(ctx, val); - string_buffer_free(b); - return JS_EXCEPTION; -} - #define JS_PRINT_MAX_DEPTH 8 typedef struct { @@ -13126,18 +13059,61 @@ static uint32_t js_string_get_length(JSValueConst val) } } -static void js_dump_char(JSPrintValueState *s, int c, int sep) +/* pretty print the first 'len' characters of 'p' */ +static void js_print_string1(JSPrintValueState *s, JSString *p, int len, int sep) { - if (c == sep || c == '\\') { - js_putc(s, '\\'); - js_putc(s, c); - } else if (c >= ' ' && c <= 126) { - js_putc(s, c); - } else if (c == '\n') { - js_putc(s, '\\'); - js_putc(s, 'n'); - } else { - js_printf(s, "\\u%04x", c); + uint8_t buf[UTF8_CHAR_LEN_MAX]; + int l, i, c, c1; + + for(i = 0; i < len; i++) { + c = string_get(p, i); + switch(c) { + case '\t': + c = 't'; + goto quote; + case '\r': + c = 'r'; + goto quote; + case '\n': + c = 'n'; + goto quote; + case '\b': + c = 'b'; + goto quote; + case '\f': + c = 'f'; + goto quote; + case '\\': + quote: + js_putc(s, '\\'); + js_putc(s, c); + break; + default: + if (c == sep) + goto quote; + if (c >= 32 && c <= 126) { + js_putc(s, c); + } else if (c < 32 || + (c >= 0x7f && c <= 0x9f)) { + escape: + js_printf(s, "\\u%04x", c); + } else { + if (is_hi_surrogate(c)) { + if ((i + 1) >= len) + goto escape; + c1 = string_get(p, i + 1); + if (!is_lo_surrogate(c1)) + goto escape; + i++; + c = from_surrogate(c, c1); + } else if (is_lo_surrogate(c)) { + goto escape; + } + l = unicode_to_utf8(buf, c); + s->write_func(s->write_opaque, (char *)buf, l); + } + break; + } } } @@ -13146,12 +13122,10 @@ static void js_print_string_rec(JSPrintValueState *s, JSValueConst val, { if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { JSString *p = JS_VALUE_GET_STRING(val); - uint32_t i, len; + uint32_t len; if (pos < s->options.max_string_length) { len = min_uint32(p->len, s->options.max_string_length - pos); - for(i = 0; i < len; i++) { - js_dump_char(s, string_get(p, i), sep); - } + js_print_string1(s, p, len, sep); } } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) { JSStringRope *r = JS_VALUE_GET_PTR(val); @@ -13224,9 +13198,7 @@ static void js_print_atom(JSPrintValueState *s, JSAtom atom) } } else { js_putc(s, '"'); - for(i = 0; i < p->len; i++) { - js_dump_char(s, string_get(p, i), '\"'); - } + js_print_string1(s, p, p->len, '\"'); js_putc(s, '"'); } } @@ -16199,6 +16171,7 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) int is_array_iterator; JSValue *arrp; uint32_t i, count32, pos; + JSCFunctionType ft; if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { JS_ThrowInternalError(ctx, "invalid index for append"); @@ -16216,8 +16189,8 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); if (JS_IsException(iterator)) return -1; - is_array_iterator = JS_IsCFunction(ctx, iterator, - (JSCFunction *)js_create_array_iterator, + ft.generic_magic = js_create_array_iterator; + is_array_iterator = JS_IsCFunction(ctx, iterator, ft.generic, JS_ITERATOR_KIND_VALUE); JS_FreeValue(ctx, iterator); @@ -16229,8 +16202,10 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) JS_FreeValue(ctx, enumobj); return -1; } + + ft.iterator_next = js_array_iterator_next; if (is_array_iterator - && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0) + && JS_IsCFunction(ctx, method, ft.generic, 0) && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { uint32_t len; if (js_get_length32(ctx, &len, sp[-1])) @@ -34963,7 +34938,8 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); } #endif - free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE); + if (b->byte_code_buf) + free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE); if (b->vardefs) { for(i = 0; i < b->arg_count + b->var_count; i++) { @@ -40840,8 +40816,10 @@ done: goto exception; args[0] = ret; res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args); - if (check_exception_free(ctx, res)) + if (check_exception_free(ctx, res)) { + JS_FreeValue(ctx, arr); goto exception; + } JS_FreeValue(ctx, ret); ret = arr; } @@ -43870,12 +43848,6 @@ static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val, return ret; } -static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToQuotedString(ctx, this_val); -} - /* return 0 if before the first char */ static int string_prevc(JSString *p, int *pidx) { @@ -44363,7 +44335,6 @@ static const JSCFunctionListEntry js_string_proto_funcs[] = { JS_ALIAS_DEF("trimLeft", "trimStart" ), JS_CFUNC_DEF("toString", 0, js_string_toString ), JS_CFUNC_DEF("valueOf", 0, js_string_toString ), - JS_CFUNC_DEF("__quote", 1, js_string___quote ), JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ), JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ), JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ), @@ -46678,10 +46649,72 @@ typedef struct JSONStringifyContext { StringBuffer *b; } JSONStringifyContext; -static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) { - JSValue r = JS_ToQuotedString(ctx, val); +static int JS_ToQuotedString(JSContext *ctx, StringBuffer *b, JSValueConst val1) +{ + JSValue val; + JSString *p; + int i; + uint32_t c; + char buf[16]; + + val = JS_ToStringCheckObject(ctx, val1); + if (JS_IsException(val)) + return -1; + p = JS_VALUE_GET_STRING(val); + + if (string_buffer_putc8(b, '\"')) + goto fail; + for(i = 0; i < p->len; ) { + c = string_getc(p, &i); + switch(c) { + case '\t': + c = 't'; + goto quote; + case '\r': + c = 'r'; + goto quote; + case '\n': + c = 'n'; + goto quote; + case '\b': + c = 'b'; + goto quote; + case '\f': + c = 'f'; + goto quote; + case '\"': + case '\\': + quote: + if (string_buffer_putc8(b, '\\')) + goto fail; + if (string_buffer_putc8(b, c)) + goto fail; + break; + default: + if (c < 32 || is_surrogate(c)) { + snprintf(buf, sizeof(buf), "\\u%04x", c); + if (string_buffer_puts8(b, buf)) + goto fail; + } else { + if (string_buffer_putc(b, c)) + goto fail; + } + break; + } + } + if (string_buffer_putc8(b, '\"')) + goto fail; JS_FreeValue(ctx, val); - return r; + return 0; + fail: + JS_FreeValue(ctx, val); + return -1; +} + +static int JS_ToQuotedStringFree(JSContext *ctx, StringBuffer *b, JSValue val) { + int ret = JS_ToQuotedString(ctx, b, val); + JS_FreeValue(ctx, val); + return ret; } static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, @@ -46864,13 +46897,11 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, if (!JS_IsUndefined(v)) { if (has_content) string_buffer_putc8(jsc->b, ','); - prop = JS_ToQuotedStringFree(ctx, prop); - if (JS_IsException(prop)) { + string_buffer_concat_value(jsc->b, sep); + if (JS_ToQuotedString(ctx, jsc->b, prop)) { JS_FreeValue(ctx, v); goto exception; } - string_buffer_concat_value(jsc->b, sep); - string_buffer_concat_value(jsc->b, prop); string_buffer_putc8(jsc->b, ':'); string_buffer_concat_value(jsc->b, sep1); if (js_json_to_str(ctx, jsc, val, v, indent1)) @@ -46898,10 +46929,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, switch (JS_VALUE_GET_NORM_TAG(val)) { case JS_TAG_STRING: case JS_TAG_STRING_ROPE: - val = JS_ToQuotedStringFree(ctx, val); - if (JS_IsException(val)) - goto exception; - goto concat_value; + return JS_ToQuotedStringFree(ctx, jsc->b, val); case JS_TAG_FLOAT64: if (!isfinite(JS_VALUE_GET_FLOAT64(val))) { val = JS_NULL; @@ -50653,6 +50681,7 @@ void JS_AddIntrinsicPromise(JSContext *ctx) { JSRuntime *rt = ctx->rt; JSValue obj1; + JSCFunctionType ft; if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) { init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE, @@ -50681,7 +50710,8 @@ void JS_AddIntrinsicPromise(JSContext *ctx) /* AsyncFunction */ ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); - obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, + ft.generic_magic = js_function_constructor; + obj1 = JS_NewCFunction3(ctx, ft.generic, "AsyncFunction", 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC, ctx->function_ctor); @@ -50717,7 +50747,8 @@ void JS_AddIntrinsicPromise(JSContext *ctx) /* AsyncGeneratorFunction */ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); - obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, + ft.generic_magic = js_function_constructor; + obj1 = JS_NewCFunction3(ctx, ft.generic, "AsyncGeneratorFunction", 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC_GENERATOR, @@ -52441,6 +52472,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) int i; JSValueConst obj, number_obj; JSValue obj1; + JSCFunctionType ft; ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0); @@ -52481,7 +52513,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) JS_SetPropertyFunctionList(ctx, obj1, js_error_funcs, countof(js_error_funcs)); /* Used to squelch a -Wcast-function-type warning. */ - JSCFunctionType ft = { .generic_magic = js_error_constructor }; + ft.generic_magic = js_error_constructor; for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { JSValue func_obj; int n_args; @@ -52624,7 +52656,8 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) countof(js_generator_proto_funcs)); ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); - obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, + ft.generic_magic = js_function_constructor; + obj1 = JS_NewCFunction3(ctx, ft.generic, "GeneratorFunction", 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR, ctx->function_ctor); diff --git a/src/couch_quickjs/quickjs/quickjs.h b/src/couch_quickjs/quickjs/quickjs.h index 4570c6d3e..c8cd14944 100644 --- a/src/couch_quickjs/quickjs/quickjs.h +++ b/src/couch_quickjs/quickjs/quickjs.h @@ -1049,7 +1049,9 @@ static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *fun const char *name, int length, JSCFunctionEnum cproto, int magic) { - return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic); + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = func }; + return JS_NewCFunction2(ctx, ft.generic, name, length, cproto, magic); } void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, JSValueConst proto); diff --git a/src/couch_quickjs/quickjs/run-test262.c b/src/couch_quickjs/quickjs/run-test262.c index ef16025ae..100ed134a 100644 --- a/src/couch_quickjs/quickjs/run-test262.c +++ b/src/couch_quickjs/quickjs/run-test262.c @@ -78,6 +78,7 @@ char *harness_dir; char *harness_exclude; char *harness_features; char *harness_skip_features; +int *harness_skip_features_count; char *error_filename; char *error_file; FILE *error_out; @@ -1736,10 +1737,13 @@ int run_test(const char *filename, int index) p = find_tag(desc, "features:", &state); if (p) { while ((option = get_option(&p, &state)) != NULL) { + char *p1; if (find_word(harness_features, option)) { /* feature is enabled */ - } else if (find_word(harness_skip_features, option)) { + } else if ((p1 = find_word(harness_skip_features, option)) != NULL) { /* skip disabled feature */ + if (harness_skip_features_count) + harness_skip_features_count[p1 - harness_skip_features]++; skip |= 1; } else { /* feature is not listed: skip and warn */ @@ -2072,6 +2076,7 @@ int main(int argc, char **argv) const char *ignore = ""; BOOL is_test262_harness = FALSE; BOOL is_module = FALSE; + BOOL count_skipped_features = FALSE; clock_t clocks; #if !defined(_WIN32) @@ -2139,6 +2144,8 @@ int main(int argc, char **argv) is_test262_harness = TRUE; } else if (str_equal(arg, "--module")) { is_module = TRUE; + } else if (str_equal(arg, "--count_skipped_features")) { + count_skipped_features = TRUE; } else { fatal(1, "unknown option: %s", arg); break; @@ -2173,6 +2180,14 @@ int main(int argc, char **argv) clocks = clock(); + if (count_skipped_features) { + /* not storage efficient but it is simple */ + size_t size; + size = sizeof(harness_skip_features_count[0]) * strlen(harness_skip_features); + harness_skip_features_count = malloc(size); + memset(harness_skip_features_count, 0, size); + } + if (is_dir_list) { if (optind < argc && !isdigit((unsigned char)argv[optind][0])) { filename = argv[optind++]; @@ -2223,6 +2238,30 @@ int main(int argc, char **argv) printf("\n"); } + if (count_skipped_features) { + size_t i, n, len = strlen(harness_skip_features); + BOOL disp = FALSE; + int c; + for(i = 0; i < len; i++) { + if (harness_skip_features_count[i] != 0) { + if (!disp) { + disp = TRUE; + printf("%-30s %7s\n", "SKIPPED FEATURE", "COUNT"); + } + for(n = 0; n < 30; n++) { + c = harness_skip_features[i + n]; + if (is_word_sep(c)) + break; + putchar(c); + } + for(; n < 30; n++) + putchar(' '); + printf(" %7d\n", harness_skip_features_count[i]); + } + } + printf("\n"); + } + if (is_dir_list) { fprintf(stderr, "Result: %d/%d error%s", test_failed, test_count, test_count != 1 ? "s" : ""); @@ -2252,6 +2291,8 @@ int main(int argc, char **argv) namelist_free(&exclude_list); namelist_free(&exclude_dir_list); free(harness_dir); + free(harness_skip_features); + free(harness_skip_features_count); free(harness_features); free(harness_exclude); free(error_file);
