Skip to content

Commit dabbab2

Browse files
committed
[JSC] Improve error message for calling constructor without new
https://bugs.webkit.org/show_bug.cgi?id=298927 Reviewed by Yusuke Suzuki. Calling constructors without `new` is invalid: class Foo {} Foo(); Current JSC throws a TypeError with the message "Cannot call a class constructor without |new|" for such code. This patch changes this error message to include the constructor name for debugging experience. Test: JSTests/stress/constructor-call-without-new.js Test: JSTests/stress/constructor-call-without-new.js * JSTests/stress/calling-non-callable-constructors.js: (shouldThrow): * JSTests/stress/constructor-call-without-new.js: Added. (shouldThrow): (throw.new.Error): * JSTests/stress/constructor-kind-naked-should-not-be-applied-to-inner-functions.js: (shouldThrow.Promise): * JSTests/stress/promise-cannot-be-called.js: (shouldThrow): * LayoutTests/js/Promise-types-expected.txt: * LayoutTests/js/class-syntax-call-expected.txt: * LayoutTests/js/class-syntax-default-constructor-expected.txt: * LayoutTests/js/script-tests/Promise-types.js: * LayoutTests/js/script-tests/class-syntax-call.js: * LayoutTests/js/script-tests/class-syntax-default-constructor.js: * LayoutTests/performance-api/performance-observer-exception-expected.txt: * Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::BytecodeGenerator): Canonical link: https://commits.webkit.org/301023@main
1 parent bd3009d commit dabbab2

12 files changed

+82
-31
lines changed

JSTests/stress/calling-non-callable-constructors.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ shouldThrow(() => {
1919
}
2020

2121
C();
22-
}, `TypeError: Cannot call a class constructor without |new|`);
22+
}, `TypeError: Cannot call a class constructor C without |new|`);
2323

2424
shouldThrow(() => {
2525
Promise();
26-
}, `TypeError: Cannot call a constructor without |new|`);
26+
}, `TypeError: Cannot call a constructor Promise without |new|`);
2727

2828
shouldThrow(() => {
2929
class DerivedPromise extends Promise { }
3030
DerivedPromise();
31-
}, `TypeError: Cannot call a class constructor without |new|`);
31+
}, `TypeError: Cannot call a class constructor DerivedPromise without |new|`);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
function shouldThrow(testFunction, expectedError) {
2+
let actualError = null;
3+
try {
4+
testFunction();
5+
} catch (e) {
6+
actualError = e;
7+
}
8+
9+
if (!actualError) {
10+
throw new Error(
11+
`Expected to throw "${expectedError}" but no error was thrown`,
12+
);
13+
}
14+
15+
const actualMessage = String(actualError);
16+
if (actualMessage !== expectedError) {
17+
throw new Error(
18+
`Expected error: "${expectedError}"\nActual error: "${actualMessage}"`,
19+
);
20+
}
21+
}
22+
23+
{
24+
class Foo { }
25+
shouldThrow(() => {
26+
Foo();
27+
}, "TypeError: Cannot call a class constructor Foo without |new|");
28+
}
29+
30+
{
31+
shouldThrow(() => {
32+
(class { })()
33+
}, "TypeError: Cannot call a class constructor without |new|");
34+
}

JSTests/stress/constructor-kind-naked-should-not-be-applied-to-inner-functions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ new Promise((resolve) => {
1717
resolve(42);
1818
});
1919
drainMicrotasks();
20-
shouldThrow(() => Promise(function () { }), `TypeError: Cannot call a constructor without |new|`)
20+
shouldThrow(() => Promise(function () { }), `TypeError: Cannot call a constructor Promise without |new|`)

JSTests/stress/promise-cannot-be-called.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function shouldThrow(func, errorMessage) {
2121
var executorCalled = false;
2222
shouldThrow(() => {
2323
Promise(function (resolve, reject) { executorCalled = true; });
24-
}, `TypeError: Cannot call a constructor without |new|`);
24+
}, `TypeError: Cannot call a constructor Promise without |new|`);
2525
shouldBe(executorCalled, false);
2626

2727
// But should accept inheriting Promise.

LayoutTests/js/Promise-types-expected.txt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,25 @@ PASS aPromise.catch.length is 1
1818
PASS aPromise.finally is an instance of Function
1919
PASS aPromise.finally.length is 1
2020
aPromise2 = Promise(...)
21-
PASS Promise(function(resolve, reject) { resolve(1); }) threw exception TypeError: Cannot call a constructor without |new|.
21+
PASS Promise(function(resolve, reject) { resolve(1); }) threw exception TypeError: Cannot call a constructor Promise without |new|.
2222

2323
Promise constructor
2424

2525
PASS Promise.length is 1
2626
PASS new Promise() threw exception TypeError: Promise constructor takes a function argument.
27-
PASS Promise() threw exception TypeError: Cannot call a constructor without |new|.
27+
PASS Promise() threw exception TypeError: Cannot call a constructor Promise without |new|.
2828
PASS new Promise(1) threw exception TypeError: Promise constructor takes a function argument.
2929
PASS new Promise('hello') threw exception TypeError: Promise constructor takes a function argument.
3030
PASS new Promise([]) threw exception TypeError: Promise constructor takes a function argument.
3131
PASS new Promise({}) threw exception TypeError: Promise constructor takes a function argument.
3232
PASS new Promise(null) threw exception TypeError: Promise constructor takes a function argument.
3333
PASS new Promise(undefined) threw exception TypeError: Promise constructor takes a function argument.
34-
PASS Promise(1) threw exception TypeError: Cannot call a constructor without |new|.
35-
PASS Promise('hello') threw exception TypeError: Cannot call a constructor without |new|.
36-
PASS Promise([]) threw exception TypeError: Cannot call a constructor without |new|.
37-
PASS Promise({}) threw exception TypeError: Cannot call a constructor without |new|.
38-
PASS Promise(null) threw exception TypeError: Cannot call a constructor without |new|.
39-
PASS Promise(undefined) threw exception TypeError: Cannot call a constructor without |new|.
34+
PASS Promise(1) threw exception TypeError: Cannot call a constructor Promise without |new|.
35+
PASS Promise('hello') threw exception TypeError: Cannot call a constructor Promise without |new|.
36+
PASS Promise([]) threw exception TypeError: Cannot call a constructor Promise without |new|.
37+
PASS Promise({}) threw exception TypeError: Cannot call a constructor Promise without |new|.
38+
PASS Promise(null) threw exception TypeError: Cannot call a constructor Promise without |new|.
39+
PASS Promise(undefined) threw exception TypeError: Cannot call a constructor Promise without |new|.
4040

4141
Promise statics
4242

LayoutTests/js/class-syntax-call-expected.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
44

55

66
PASS new A
7-
PASS A():::"TypeError: Cannot call a class constructor without |new|"
7+
PASS A():::"TypeError: Cannot call a class constructor A without |new|"
88
PASS new B
9-
PASS B():::"TypeError: Cannot call a class constructor without |new|"
9+
PASS B():::"TypeError: Cannot call a class constructor B without |new|"
1010
PASS new (class { constructor() {} })()
1111
PASS (class { constructor() {} })():::"TypeError: Cannot call a class constructor without |new|"
1212
PASS new (class extends null { constructor() { super() } })():::"TypeError: function is not a constructor (evaluating 'super()')"

LayoutTests/js/class-syntax-default-constructor-expected.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
44

55

66
PASS new A instanceof A
7-
PASS A():::TypeError: Cannot call a class constructor without |new|
7+
PASS A():::TypeError: Cannot call a class constructor A without |new|
88
PASS A.prototype.constructor instanceof Function
99
PASS A.prototype.constructor.name:::"A"
1010
PASS new B instanceof A; new B instanceof A
11-
PASS B():::TypeError: Cannot call a class constructor without |new|
11+
PASS B():::TypeError: Cannot call a class constructor B without |new|
1212
PASS B.prototype.constructor.name:::"B"
1313
PASS A !== B
1414
PASS A.prototype.constructor !== B.prototype.constructor

LayoutTests/js/script-tests/Promise-types.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ shouldThrow("new Promise({})", "'TypeError: Promise constructor takes a function
4747
shouldThrow("new Promise(null)", "'TypeError: Promise constructor takes a function argument'");
4848
shouldThrow("new Promise(undefined)", "'TypeError: Promise constructor takes a function argument'");
4949

50-
shouldThrow("Promise(1)", "'TypeError: Cannot call a constructor without |new|'");
51-
shouldThrow("Promise('hello')", "'TypeError: Cannot call a constructor without |new|'");
52-
shouldThrow("Promise([])", "'TypeError: Cannot call a constructor without |new|'");
53-
shouldThrow("Promise({})", "'TypeError: Cannot call a constructor without |new|'");
54-
shouldThrow("Promise(null)", "'TypeError: Cannot call a constructor without |new|'");
55-
shouldThrow("Promise(undefined)", "'TypeError: Cannot call a constructor without |new|'");
50+
shouldThrow("Promise(1)", "'TypeError: Cannot call a constructor Promise without |new|'");
51+
shouldThrow("Promise('hello')", "'TypeError: Cannot call a constructor Promise without |new|'");
52+
shouldThrow("Promise([])", "'TypeError: Cannot call a constructor Promise without |new|'");
53+
shouldThrow("Promise({})", "'TypeError: Cannot call a constructor Promise without |new|'");
54+
shouldThrow("Promise(null)", "'TypeError: Cannot call a constructor Promise without |new|'");
55+
shouldThrow("Promise(undefined)", "'TypeError: Cannot call a constructor Promise without |new|'");
5656

5757
// Promise statics
5858
debug("");
@@ -73,4 +73,3 @@ shouldNotThrow("Promise.reject(1)");
7373
shouldBeType("Promise.resolve(1)", "Promise");
7474
shouldBeType("Promise.reject(1)", "Promise");
7575

76-

LayoutTests/js/script-tests/class-syntax-call.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ function shouldNotThrow(s) {
3232
}
3333

3434
shouldNotThrow('new A');
35-
shouldThrow('A()', '"TypeError: Cannot call a class constructor without |new|"');
35+
shouldThrow('A()', '"TypeError: Cannot call a class constructor A without |new|"');
3636
shouldNotThrow('new B');
37-
shouldThrow('B()', '"TypeError: Cannot call a class constructor without |new|"');
37+
shouldThrow('B()', '"TypeError: Cannot call a class constructor B without |new|"');
3838
shouldNotThrow('new (class { constructor() {} })()');
3939
shouldThrow('(class { constructor() {} })()', '"TypeError: Cannot call a class constructor without |new|"');
4040
shouldThrow('new (class extends null { constructor() { super() } })()', '"TypeError: function is not a constructor (evaluating \'super()\')"');

LayoutTests/js/script-tests/class-syntax-default-constructor.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ class A { };
4343
class B extends A { };
4444

4545
shouldBeTrue('new A instanceof A');
46-
shouldThrow('A()', '"TypeError: Cannot call a class constructor without |new|"');
46+
shouldThrow('A()', '"TypeError: Cannot call a class constructor A without |new|"');
4747
shouldBeTrue('A.prototype.constructor instanceof Function');
4848
shouldBe('A.prototype.constructor.name', '"A"');
4949
shouldBeTrue('new B instanceof A; new B instanceof A');
50-
shouldThrow('B()', '"TypeError: Cannot call a class constructor without |new|"');
50+
shouldThrow('B()', '"TypeError: Cannot call a class constructor B without |new|"');
5151
shouldBe('B.prototype.constructor.name', '"B"');
5252
shouldBeTrue('A !== B');
5353
shouldBeTrue('A.prototype.constructor !== B.prototype.constructor');

0 commit comments

Comments
 (0)