From 5e5b00c48cbfe1cceff0c4a201e56a3482467655 Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Fri, 22 Mar 2024 11:19:36 +0100 Subject: [PATCH] Improve string parsing and JSON parsing (#316) * Improve string parsing and JSON parsing - fix JSON parsing of non ASCII string contents - more precise string parsing errors - more precise JSON parsing errors - add `JS_ParseState::buf_start` to compute line/column - fix HTML comment detection at start of source code - improve v8 Failure messages (pulled and modified `formatFailureText` from **mjsunit.js**) - ignore more v8 tests --- quickjs.c | 174 +++++++++++++++++++++++++++++++-------------------- v8-tweak.js | 16 ++++- v8.js | 17 ++++- v8.txt | 176 +++++++++++++++++++++++++++++----------------------- 4 files changed, 237 insertions(+), 146 deletions(-) diff --git a/quickjs.c b/quickjs.c index 6acf54c..8e8f856 100644 --- a/quickjs.c +++ b/quickjs.c @@ -18392,6 +18392,7 @@ typedef struct JSParseState { JSToken token; BOOL got_lf; /* true if got line feed before the current token */ const uint8_t *last_ptr; + const uint8_t *buf_start; const uint8_t *buf_ptr; const uint8_t *buf_end; const uint8_t *eol; // most recently seen end-of-line character @@ -18671,13 +18672,17 @@ static __exception int js_parse_string(JSParseState *s, int sep, break; } if (c == '\\') { + const uint8_t *p0 = p; c = *p; - /* XXX: need a specific JSON case to avoid - accepting invalid escapes */ switch(c) { case '\0': - if (p >= s->buf_end) - goto invalid_char; + if (p >= s->buf_end) { + if (sep != '`') + goto invalid_char; + if (do_throw) + js_parse_error(s, "Unexpected end of input"); + goto fail; + } p++; break; case '\'': @@ -18700,22 +18705,19 @@ static __exception int js_parse_string(JSParseState *s, int sep, } continue; default: - if (c >= '0' && c <= '9') { - if (s->cur_func && !(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') - goto parse_escape; - if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { - p++; - c = '\0'; - } else { - if (c >= '8' || sep == '`') { - /* Note: according to ES2021, \8 and \9 are not - accepted in strict mode or in templates. */ - goto invalid_escape; - } - if (do_throw) - js_parse_error(s, "octal escape sequences are not allowed in strict mode"); - goto fail; + if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { + /* accept isolated \0 */ + p++; + c = '\0'; + } else + if ((c >= '0' && c <= '9') + && ((s->cur_func->js_mode & JS_MODE_STRICT) || sep == '`')) { + if (do_throw) { + js_parse_error(s, "%s are not allowed in %s", + (c >= '8') ? "\\8 and \\9" : "Octal escape sequences", + (sep == '`') ? "template strings" : "strict mode"); } + goto fail; } else if (c >= 0x80) { const uint8_t *p_next; c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); @@ -18727,10 +18729,13 @@ static __exception int js_parse_string(JSParseState *s, int sep, if (c == CP_LS || c == CP_PS) continue; } else { - parse_escape: ret = lre_parse_escape(&p, TRUE); if (ret == -1) { - goto invalid_escape; + if (do_throw) { + js_parse_error(s, "Invalid %s escape sequence", + c == 'u' ? "Unicode" : "hexadecimal"); + } + goto fail; } else if (ret < 0) { /* ignore the '\' (could output a warning) */ p++; @@ -18760,10 +18765,6 @@ static __exception int js_parse_string(JSParseState *s, int sep, if (do_throw) js_parse_error(s, "invalid UTF-8 sequence"); goto fail; - invalid_escape: - if (do_throw) - js_parse_error(s, "malformed escape sequence in string literal"); - goto fail; invalid_char: if (do_throw) js_parse_error(s, "unexpected end of string"); @@ -19258,8 +19259,8 @@ static __exception int next_token(JSParseState *s) p += 2; s->token.val = TOK_MINUS_ASSIGN; } else if (p[1] == '-') { - if (s->allow_html_comments && - p[2] == '>' && s->last_line_num != s->line_num) { + if (s->allow_html_comments && p[2] == '>' && + (s->got_lf || s->last_ptr == s->buf_start)) { /* Annex B: `-->` at beginning of line is an html comment end. It extends to the end of the line. */ @@ -19439,6 +19440,23 @@ static __exception int next_token(JSParseState *s) return -1; } +static int json_parse_error(JSParseState *s, const uint8_t *curp, const char *msg) +{ + const uint8_t *p, *line_start; + int position = curp - s->buf_start; + int line = 1; + for (line_start = p = s->buf_start; p < curp; p++) { + /* column count does not account for TABs nor wide characters */ + if (*p == '\r' || *p == '\n') { + p += 1 + (p[0] == '\r' && p[1] == '\n'); + line++; + line_start = p; + } + } + return js_parse_error(s, "%s in JSON at position %d (line %d column %d)", + msg, position, line, (int)(p - line_start) + 1); +} + static int json_parse_string(JSParseState *s, const uint8_t **pp) { const uint8_t *p = *pp; @@ -19451,14 +19469,13 @@ static int json_parse_string(JSParseState *s, const uint8_t **pp) for(;;) { if (p >= s->buf_end) { - js_parse_error(s, "Unexpected end of JSON input"); - goto fail; + goto end_of_input; } c = *p++; if (c == '"') break; if (c < 0x20) { - js_parse_error(s, "Bad control character in string literal in JSON"); + json_parse_error(s, p - 1, "Bad control character in string literal"); goto fail; } if (c == '\\') { @@ -19476,16 +19493,28 @@ static int json_parse_string(JSParseState *s, const uint8_t **pp) c = 0; for(i = 0; i < 4; i++) { int h = from_hex(*p++); - if (h < 0) - goto invalid_escape; + if (h < 0) { + json_parse_error(s, p - 1, "Bad Unicode escape"); + goto fail; + } c = (c << 4) | h; } break; default: - invalid_escape: - js_parse_error(s, "Bad escaped character in JSON"); + if (p > s->buf_end) + goto end_of_input; + json_parse_error(s, p - 1, "Bad escaped character"); goto fail; } + } else + if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p - 1, s->buf_end - p, &p_next); + if (c > 0x10FFFF) { + json_parse_error(s, p - 1, "Bad UTF-8 sequence"); + goto fail; + } + p = p_next; } if (string_buffer_putc(b, c)) goto fail; @@ -19496,6 +19525,8 @@ static int json_parse_string(JSParseState *s, const uint8_t **pp) *pp = p; return 0; + end_of_input: + js_parse_error(s, "Unexpected end of JSON input"); fail: string_buffer_free(b); return -1; @@ -19513,7 +19544,7 @@ static int json_parse_number(JSParseState *s, const uint8_t **pp) return js_parse_error(s, "Unexpected token '%c'", *p_start); if (p[0] == '0' && is_digit(p[1])) - return js_parse_error(s, "Unexpected number in JSON"); + return json_parse_error(s, p, "Unexpected number"); while (is_digit(*p)) p++; @@ -19521,7 +19552,7 @@ static int json_parse_number(JSParseState *s, const uint8_t **pp) if (*p == '.') { p++; if (!is_digit(*p)) - return js_parse_error(s, "Unterminated fractional number in JSON"); + return json_parse_error(s, p, "Unterminated fractional number"); while (is_digit(*p)) p++; } @@ -19530,7 +19561,7 @@ static int json_parse_number(JSParseState *s, const uint8_t **pp) if (*p == '+' || *p == '-') p++; if (!is_digit(*p)) - return js_parse_error(s, "Exponent part is missing a number in JSON"); + return json_parse_error(s, p, "Exponent part is missing a number"); while (is_digit(*p)) p++; } @@ -19659,17 +19690,17 @@ static __exception int json_next_token(JSParseState *s) s->token.u.ident.is_reserved = FALSE; s->token.val = TOK_IDENT; break; - case '+': - if (!is_digit(p[1])) - goto def_token; + case '-': + if (!is_digit(p[1])) { + json_parse_error(s, p, "No number after minus sign"); + goto fail; + } goto parse_number; case '0': - if (is_digit(p[1])) - goto def_token; - goto parse_number; - case '-': - if (!is_digit(p[1])) - goto def_token; + if (is_digit(p[1])) { + json_parse_error(s, p, "Unexpected number"); + goto fail; + } goto parse_number; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': @@ -19681,7 +19712,17 @@ static __exception int json_next_token(JSParseState *s) break; default: if (c >= 128) { - js_parse_error(s, "unexpected character"); + const uint8_t *p_next; + c = unicode_from_utf8(p, s->buf_end - p, &p_next); + if (c == -1) { + js_parse_error(s, "Unexpected token '\\x%02x' in JSON", *p); + } else { + if (c > 0xFFFF) { + /* get high surrogate */ + c = (c >> 10) - (0x10000 >> 10) + 0xD800; + } + js_parse_error(s, "Unexpected token '\\u%04x' in JSON", c); + } goto fail; } def_token: @@ -32017,7 +32058,7 @@ static void js_parse_init(JSContext *ctx, JSParseState *s, s->filename = filename; s->line_num = 1; s->col_num = 1; - s->buf_ptr = (const uint8_t *)input; + s->buf_start = s->buf_ptr = (const uint8_t *)input; s->buf_end = s->buf_ptr + input_len; s->mark = s->buf_ptr + min_int(1, input_len); s->eol = s->buf_ptr; @@ -42476,15 +42517,6 @@ void JS_AddIntrinsicRegExp(JSContext *ctx) /* JSON */ -static int json_parse_expect(JSParseState *s, int tok) -{ - if (s->token.val != tok) { - /* XXX: dump token correctly in all cases */ - return js_parse_error(s, "expecting '%c'", tok); - } - return json_next_token(s); -} - static JSValue json_parse_value(JSParseState *s) { JSContext *ctx = s->ctx; @@ -42509,12 +42541,16 @@ static JSValue json_parse_value(JSParseState *s) if (prop_name == JS_ATOM_NULL) goto fail; } else { - js_parse_error(s, "expecting property name"); + json_parse_error(s, s->token.ptr, "Expected property name or '}'"); goto fail; } if (json_next_token(s)) goto fail1; - if (json_parse_expect(s, ':')) + if (s->token.val != ':') { + json_parse_error(s, s->token.ptr, "Expected ':' after property name"); + goto fail1; + } + if (json_next_token(s)) goto fail1; prop_val = json_parse_value(s); if (JS_IsException(prop_val)) { @@ -42528,13 +42564,17 @@ static JSValue json_parse_value(JSParseState *s) if (ret < 0) goto fail; - if (s->token.val != ',') + if (s->token.val == '}') break; + if (s->token.val != ',') { + json_parse_error(s, s->token.ptr, "Expected ',' or '}' after property value"); + goto fail; + } if (json_next_token(s)) goto fail; } } - if (json_parse_expect(s, '}')) + if (json_next_token(s)) goto fail; } break; @@ -42549,22 +42589,24 @@ static JSValue json_parse_value(JSParseState *s) if (JS_IsException(val)) goto fail; if (s->token.val != ']') { - idx = 0; - for(;;) { + for(idx = 0;; idx++) { el = json_parse_value(s); if (JS_IsException(el)) goto fail; ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E); if (ret < 0) goto fail; - if (s->token.val != ',') + if (s->token.val == ']') break; + if (s->token.val != ',') { + json_parse_error(s, s->token.ptr, "Expected ',' or ']' after array element"); + goto fail; + } if (json_next_token(s)) goto fail; - idx++; } } - if (json_parse_expect(s, ']')) + if (json_next_token(s)) goto fail; } break; diff --git a/v8-tweak.js b/v8-tweak.js index 021ddef..de0b509 100644 --- a/v8-tweak.js +++ b/v8-tweak.js @@ -6,6 +6,20 @@ globalThis.failWithMessage = function(message) { if (count > 99) return if (++count > 99) return print("") - print(String(message).slice(0, 128)) + print(String(message).slice(0, 300)) + } + globalThis.formatFailureText = function(expectedText, found, name_opt) { + var message = "Fail" + "ure" + if (name_opt) { + // Fix this when we ditch the old test runner. + message += " (" + name_opt + ")" + } + var foundText = prettyPrinted(found) + if (expectedText.length <= 60 && foundText.length <= 60) { + message += ": expected <" + expectedText + "> found <" + foundText + ">" + } else { + message += ":\nexpected:\n" + expectedText + "\nfound:\n" + foundText + } + return message } })() diff --git a/v8.js b/v8.js index f2b354e..3da78ea 100644 --- a/v8.js +++ b/v8.js @@ -20,13 +20,17 @@ const exclude = [ "regexp-indexof.js", // deprecated RegExp.lastMatch etc. "regexp-static.js", // deprecated RegExp static properties. "string-replace.js", // unstable output + "omit-default-ctors-array-iterator.js", - "mjsunit-assertion-error.js", "mjsunit.js", + "mjsunit-assertion-error.js", + "mjsunit-assert-equals.js", "mjsunit_suppressions.js", "verify-assert-false.js", // self check "verify-check-false.js", // self check + + "clone-ic-regression-crbug-1466315.js", // spurious output ] let files = scriptArgs.slice(1) // run only these tests @@ -37,7 +41,7 @@ for (const file of files) { if (exclude.includes(file)) continue const source = std.loadFile(dir + "/" + file) if (/^(im|ex)port/m.test(source)) continue // TODO support modules - if (source.includes('Realm.create()')) continue // TODO support Realm object + if (source.includes('Realm.create')) continue // TODO support Realm object if (source.includes('// MODULE')) continue // TODO support modules if (source.includes('// Files:')) continue // TODO support includes @@ -59,6 +63,15 @@ for (const file of files) { } // exclude tests that use V8 intrinsics like %OptimizeFunctionOnNextCall if (flags["--allow-natives-syntax"]) continue + if (flags["--allow-natives-for-differential-fuzzing"]) continue + if (flags["--dump-counters"]) continue + if (flags["--expose-cputracemark"]) continue + if (flags["--harmony-rab-gsab"]) continue + if (flags["--harmony-class-fields"]) continue + if (flags["--enable-experimental-regexp-engine"]) continue + if (flags["--experimental-stack-trace-frames"]) continue + if (flags["--js-float16array"]) continue + if (flags["--expose-cputracemark-as"]) continue // exclude tests that use V8 extensions if (flags["--expose-externalize-string"]) continue // parse environment variables diff --git a/v8.txt b/v8.txt index def407e..6da607e 100644 --- a/v8.txt +++ b/v8.txt @@ -66,7 +66,10 @@ async function two(x) { try { x = await x; throw new Error(); - } + } catch (e) { + return e.stack; + } +} === async-stack-traces-prepare-stacktrace-3.js === async-stack-traces-prepare-stacktrace-4.js === big-array-literal.js @@ -324,11 +327,7 @@ Failure: expected found Failure: expected found === function-length-accessor.js === function-name-eval-shadowed.js -Failure: -expected: -200 -found: -function f() { eval("var f = 100"); f = 200; return f } +Failure: expected <200> found === function-names.js === function-property.js === function-prototype.js @@ -336,14 +335,10 @@ function f() { eval("var f = 100"); f = 200; return f } === function-without-prototype.js === function.js Failure: expected <"undefined"> found <"function"> -Failure: -expected: -42 -found: -function anonymous( +Failure: expected <42> found === fuzz-accessors.js === get-own-property-descriptor-non-objects.js === get-own-property-descriptor.js @@ -368,9 +363,6 @@ return anonymous; === hex-parsing.js === holy-double-no-arg-array.js === html-comments.js -SyntaxError: unexpected token in expression: '>' - at html-comments.js:1:1 - === html-string-funcs.js === icu-date-lord-howe.js TZ=Australia/Lord_Howe LC_ALL=en Failure: @@ -443,12 +435,12 @@ Failure: expected: "Unexpected token \n in JSON at position 3" found: -"Bad control character in string literal in JSON" +"Bad control character in string literal in JSON at position 3 (line 1 column 4)" Failure: expected: "Unexpected token \n in JSON at position 3" found: -"Bad control character in string literal in JSON" +"Bad control character in string literal in JSON at position 3 (line 1 column 4)" === json-parser-recursive.js === json-replacer-number-wrapper-tostring.js === json-replacer-order.js @@ -639,43 +631,17 @@ SyntaxError: invalid assignment left-hand side === spread-large-set.js === spread-large-string.js === stack-traces-2.js -=== stack-traces-class-fields.js -Failure (during class construction doesn't contain expected[1] stack = at thrower (stack-traces-class-fields.js:35:3) - at -Failure (during class construction contains unexpected[0]): expected <59> found <-1> -Failure (during class instantiation doesn't contain expected[1] stack = at thrower (stack-traces-class-fields.js:35:3) - a -Failure (during class instantiation doesn't contain expected[2] stack = at thrower (stack-traces-class-fields.js:35:3) - a -Failure (during class instantiation contains unexpected[0]): expected <59> found <-1> -Failure (during class instantiation with super doesn't contain expected[1] stack = at thrower (stack-traces-class-fields.js: -Failure (during class instantiation with super doesn't contain expected[2] stack = at thrower (stack-traces-class-fields.js: -Failure (during class instantiation with super contains unexpected[1]): expected <59> found <-1> -Failure (during class instantiation with super2 doesn't contain expected[1] stack = at thrower (stack-traces-class-fields.js -Failure (during class instantiation with super2 doesn't contain expected[2] stack = at thrower (stack-traces-class-fields.js -Failure (during class instantiation with super2 contains unexpected[1]): expected <59> found <-1> -Failure (during class instantiation with super3 doesn't contain expected[1] stack = at thrower (stack-traces-class-fields.js -Failure (during class instantiation with super3 doesn't contain expected[2] stack = at thrower (stack-traces-class-fields.js -Failure (during class instantiation with super3 doesn't contain expected[3] stack = at thrower (stack-traces-class-fields.js -Failure (during class instantiation with super3 contains unexpected[0]): expected <59> found <-1> -Failure (during class field call doesn't contain expected[0] stack = at thrower (stack-traces-class-fields.js:35:3) - at t -Failure (during static class field call doesn't contain expected[0] stack = at thrower (stack-traces-class-fields.js:35:3) - -Failure (during class field call with FNI doesn't contain expected[0] stack = at x (stack-traces-class-fields.js:208:7) - -Failure (during static class field call with FNI doesn't contain expected[0] stack = at x (stack-traces-class-fields.js:230: === stack-traces-custom-lazy.js Failure: expected: "bar" found: -" at (stack-traces-custom-lazy.js:47:46)\n at testPrepareStackTrace (stack-tra +" at (stack-traces-custom-lazy.js:47:46)\n at testPrepareStackTrace (stack-traces-custom-lazy.js:31:5)\n at (stack-traces-custom-lazy.js:47:60)\n" Failure: expected: "bar" found: -" at f (stack-traces-custom-lazy.js:48:38)\n at f (stack-traces-custom-lazy.js:48:38)\n +" at f (stack-traces-custom-lazy.js:48:38)\n at f (stack-traces-custom-lazy.js:48:38)\n at f (stack-traces-custom-lazy.js:48:38)\n at f (stack-traces-custom-lazy.js:48:38)\n at f (stack-traces-custom-lazy.js:48:38)\n at f (stack-traces-custom-lazy.js === stack-traces-custom.js TypeError: not a function at (stack-traces-custom.js:20:21) @@ -687,58 +653,118 @@ Failure: expected found <""> === stack-traces.js Failure (testArrayNative doesn't contain expected[0] stack = at (stack-traces.js:48:31) at map (native) - + at testArrayNative (stack-traces.js:48:37) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:267:47) +): expected found Failure (testMethodNameInference doesn't contain expected[0] stack = at (stack-traces.js:30:37) - at testMetho + at testMethodNameInference (stack-traces.js:31:8) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:269:63) +): expected found Failure (testImplicitConversion doesn't contain expected[0] stack = at (stack-traces.js:53:42) - at testImplic + at testImplicitConversion (stack-traces.js:54:19) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:270:61) +): expected found Failure (testEval doesn't contain expected[0] stack = at Doo (:1:17) at (:1:26) - at testEval (st + at testEval (stack-traces.js:58:3) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:271:33) +): expected found Failure (testNestedEval doesn't contain expected[0] stack = at (:1:1) at Inner (:1:19) - at Outer + at Outer (:1:58) + at (:1:70) + at testNestedEval (stack-traces.js:63:3) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:272:45) +): Failure (testEvalWithSourceURL doesn't contain expected[0] stack = at Doo (:1:17) at (:1:26) - at + at testEvalWithSourceURL (stack-traces.js:67:3) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:274:34) +): expected found Failure (testNestedEvalWithSourceURL doesn't contain expected[0] stack = at (:1:1) at Inner (:1:19) + at Outer (:1:36) + at (:1:48) + at testNestedEvalWithSourceURL (stack-traces.js:73:3) + at testTrace (stack-traces.js:162:5) + at ( Failure (testNestedEvalWithSourceURL doesn't contain expected[1] stack = at (:1:1) at Inner (:1:19) + at Outer (:1:36) + at (:1:48) + at testNestedEvalWithSourceURL (stack-traces.js:73:3) + at testTrace (stack-traces.js:162:5) + at ( Failure (testValue doesn't contain expected[0] stack = at (stack-traces.js:77:47) - at testValue (stack-traces + at testValue (stack-traces.js:78:3) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:278:35) +): expected found Failure (testConstructor doesn't contain expected[0] stack = at Plonk (stack-traces.js:82:22) - at testConstructor (stack- + at testConstructor (stack-traces.js:83:7) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:279:47) +): expected found Failure (testRenamedMethod doesn't contain expected[0] stack = at a$b$c$d (stack-traces.js:87:31) - at testRenamedMethod ( + at testRenamedMethod (stack-traces.js:90:8) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:280:51) +): expected found Failure (testAnonymousMethod doesn't contain expected[0] stack = at (stack-traces.js:94:18) at call (native) + at testAnonymousMethod (stack-traces.js:94:38) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:281:55) +): expected found Failure (testFunctionName doesn't contain expected[2] stack = at foo_0 (stack-traces.js:101:9) - at foo_1 (stack-traces.js + at foo_1 (stack-traces.js:103:27) + at (stack-traces.js:103:27) + at boo_3 (stack-traces.js:103:27) + at (stack-traces.js:103:27) + at testFunctionName (stack-traces Failure (testFunctionName doesn't contain expected[4] stack = at foo_0 (stack-traces.js:101:9) - at foo_1 (stack-traces.js + at foo_1 (stack-traces.js:103:27) + at (stack-traces.js:103:27) + at boo_3 (stack-traces.js:103:27) + at (stack-traces.js:103:27) + at testFunctionName (stack-traces Failure (testDefaultCustomError doesn't contain expected[0] stack = at CustomError (stack-traces.js:130:33) - at testDefau + at testDefaultCustomError (stack-traces.js:138:36) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:287:5) +): expected found Failure (testDefaultCustomError doesn't contain expected[1] stack = at CustomError (stack-traces.js:130:33) - at testDefau + at testDefaultCustomError (stack-traces.js:138:36) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:287:5) +): expected found Failure (testStrippedCustomError doesn't contain expected[0] stack = at CustomError (stack-traces.js:130:33) - at testStri + at testStrippedCustomError (stack-traces.js:142:36) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:289:25) +): expected found Failure (testClassNames doesn't contain expected[0] stack = at MyObj (stack-traces.js:145:22) - at (stack-trac + at (stack-traces.js:150:14) + at testClassNames (stack-traces.js:154:8) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:291:49) +): expected found Failure (testClassNames doesn't contain expected[1] stack = at MyObj (stack-traces.js:145:22) - at (stack-trac + at (stack-traces.js:150:14) + at testClassNames (stack-traces.js:154:8) + at testTrace (stack-traces.js:162:5) + at (stack-traces.js:291:49) +): expected found Failure (UnintendedCallerCensorship didn't contain new ReferenceError): expected found Failure: expected <"abc"> found -Failure: -expected: -"abc" -found: -" at (stack-traces.js:371:13)\n" -Failure: -expected: -undefined -found: -" at (stack-traces.js:375:13)\n" +Failure: expected <"abc"> found <" at (stack-traces.js:371:13)\n"> +Failure: expected found <" at (stack-traces.js:375:13)\n"> TypeError: not a function at (stack-traces.js:381:1) @@ -809,13 +835,9 @@ Did not throw exception Did not throw exception Did not throw exception Failure: expected found -Failure: -expected: -undefined -found: -function non_strict() { +Failure: expected found Failure: expected found Failure: expected found Failure: expected found