v8/test/mjsunit/stack-traces-class-fields.js
Benedikt Meurer ad21d212fc Preserve "proper method names" as-is in error.stack.
This changes the logic for generating method names in `error.stack` to
prepend an inferred type name only when the function name is a valid
ECMAScript identifiers and does not equal the inferred type name, to

(1) give developers more control over the exact name shown in
    `error.stack`, as well as
(2) avoid confusion in the presence of renaming of local variables.

Previously we'd leave the function name as-is if it was prefixed by the
inferred type name, but that condition is unnecessarily strict, and led
to a bunch of inconsistencies around special names like
`<instance_member_initializer>` where this dynamic approached often
prefixed it with the correct type name, but also sometimes got it wrong
and prepended `Object.`, which is very unfortunate and misleading.
Specifically for these special names, we'll add logic later in the
parser to infer a useful (complete) name.

The design doc (https://bit.ly/devtools-method-names-in-stack-traces)
contains more background and examples of why we do this change.

Doc: https://bit.ly/devtools-method-names-in-stack-traces
Fixed: chromium:1294619
Bug: chromium:1283435
Change-Id: Ib8b528ba25255dcd07e9d11044c562c11d699bcb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3565724
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79748}
2022-04-04 14:08:56 +00:00

238 lines
4.8 KiB
JavaScript

// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-class-fields
// Utility function for testing that the expected strings occur
// in the stack trace produced when running the given function.
function testTrace(name, fun, expected, unexpected) {
var threw = false;
try {
fun();
} catch (e) {
for (var i = 0; i < expected.length; i++) {
assertTrue(
e.stack.indexOf(expected[i]) != -1,
name + " doesn't contain expected[" + i + "] stack = " + e.stack
);
}
if (unexpected) {
for (var i = 0; i < unexpected.length; i++) {
assertEquals(
e.stack.indexOf(unexpected[i]),
-1,
name + " contains unexpected[" + i + "]"
);
}
}
threw = true;
}
assertTrue(threw, name + " didn't throw");
}
function thrower() {
FAIL;
}
function testClassConstruction() {
class X {
static x = thrower();
}
}
// ReferenceError: FAIL is not defined
// at thrower
// at <static_initializer>
// at testClassConstruction
// at testTrace
testTrace(
"during class construction",
testClassConstruction,
["thrower", "<static_initializer>"],
["anonymous"]
);
function testClassConstruction2() {
class X {
[thrower()];
}
}
// ReferenceError: FAIL is not defined
// at thrower
// at testClassConstruction2
// at testTrace
testTrace("during class construction2", testClassConstruction2, ["thrower"]);
function testClassInstantiation() {
class X {
x = thrower();
}
new X();
}
// ReferenceError: FAIL is not defined
// at thrower
// at <instance_members_initializer>
// at new X
// at testClassInstantiation
// at testTrace
testTrace(
'during class instantiation', testClassInstantiation,
['thrower', '<instance_members_initializer>', 'new X'], ['anonymous']);
function testClassInstantiationWithSuper() {
class Base {}
class X extends Base {
x = thrower();
}
new X();
}
// ReferenceError: FAIL is not defined
// at thrower
// at <instance_members_initializer>
// at new X
// at testClassInstantiation
// at testTrace
testTrace(
'during class instantiation with super', testClassInstantiationWithSuper,
['thrower', '<instance_members_initializer>', 'new X'],
['Base', 'anonymous']);
function testClassInstantiationWithSuper2() {
class Base {}
class X extends Base {
constructor() {
super();
}
x = thrower();
}
new X();
}
// ReferenceError: FAIL is not defined
// at thrower
// at <instance_members_initializer>
// at new X
// at testClassInstantiation
// at testTrace
testTrace(
'during class instantiation with super2', testClassInstantiationWithSuper2,
['thrower', '<instance_members_initializer>', 'new X'],
['Base', 'anonymous']);
function testClassInstantiationWithSuper3() {
class Base {
x = thrower();
}
class X extends Base {
constructor() {
super();
}
}
new X();
}
// ReferenceError: FAIL is not defined
// at thrower
// at <instance_members_initializer>
// at new Base
// at new X
// at testClassInstantiationWithSuper3
// at testTrace
testTrace(
'during class instantiation with super3', testClassInstantiationWithSuper3,
['thrower', '<instance_members_initializer>', 'new Base', 'new X'],
['anonymous']);
function testClassFieldCall() {
class X {
x = thrower;
}
let x = new X();
x.x();
}
// ReferenceError: FAIL is not defined
// at X.thrower [as x]
// at testClassFieldCall
// at testTrace
testTrace(
"during class field call",
testClassFieldCall,
["X.thrower"],
["anonymous"]
);
function testStaticClassFieldCall() {
class X {
static x = thrower;
}
X.x();
}
// ReferenceError: FAIL is not defined
// at Function.thrower [as x]
// at testStaticClassFieldCall
// at testTrace
testTrace(
"during static class field call",
testStaticClassFieldCall,
["Function.thrower"],
["anonymous"]
);
function testClassFieldCallWithFNI() {
class X {
x = function() {
FAIL;
};
}
let x = new X();
x.x();
}
// ReferenceError: FAIL is not defined
// at X.x
// at testClassFieldCallWithFNI
// at testTrace
testTrace(
"during class field call with FNI",
testClassFieldCallWithFNI,
["X.x"],
["anonymous"]
);
function testStaticClassFieldCallWithFNI() {
class X {
static x = function() {
FAIL;
};
}
X.x();
}
// ReferenceError: FAIL is not defined
// at Function.x
// at testStaticClassFieldCallWithFNI
// at testTrace
testTrace(
"during static class field call with FNI",
testStaticClassFieldCallWithFNI,
["Function.x"],
["anonymous"]
);