Diff
Modified: trunk/LayoutTests/ChangeLog (181972 => 181973)
--- trunk/LayoutTests/ChangeLog 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/LayoutTests/ChangeLog 2015-03-25 21:33:59 UTC (rev 181973)
@@ -1,3 +1,36 @@
+2015-03-25 Joseph Pecoraro <pecor...@apple.com>
+
+ ES6: Classes: Program level class statement throws exception in strict mode
+ https://bugs.webkit.org/show_bug.cgi?id=143038
+
+ Reviewed by Ryosuke Niwa.
+
+ This updates a number of existing tests that were relying on
+ poor behavior. `shouldBe` and friends use eval within a function
+ not at the global scope. This means `shouldBe('class X { ... }')`
+ behaves like `shouldBe('var x = ...')` not `shouldBe('x = ...')`.
+ This means `x` will not be available in the next `shouldBe` call.
+
+ Add a test specifically to cover the scoping of the class name
+ in regular and strict mode code. Currently we treat it like var
+ with one failing test that would pass when we treat it like let.
+
+ * js/class-syntax-name.html: Added.
+ * js/script-tests/class-syntax-name.js: Added.
+ (runTestShouldBe):
+ (runTestShouldBeTrue):
+ (runTestShouldThrow):
+ (runTestShouldNotThrow):
+ Test class name scoping.
+
+ * js/class-syntax-call-expected.txt:
+ * js/class-syntax-declaration-expected.txt:
+ * js/class-syntax-default-constructor-expected.txt:
+ * js/class-syntax-name-expected.txt: Added.
+ * js/script-tests/class-syntax-call.js:
+ * js/script-tests/class-syntax-declaration.js:
+ * js/script-tests/class-syntax-default-constructor.js:
+
2015-03-25 Mark Lam <mark....@apple.com>
Gardening: rebaseline after r181907.
Modified: trunk/LayoutTests/js/class-syntax-call-expected.txt (181972 => 181973)
--- trunk/LayoutTests/js/class-syntax-call-expected.txt 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/LayoutTests/js/class-syntax-call-expected.txt 2015-03-25 21:33:59 UTC (rev 181973)
@@ -3,9 +3,9 @@
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-PASS class A { constructor() {} }; new A did not throw exception.
+PASS new A did not throw exception.
PASS A() threw exception TypeError: Cannot call a class constructor.
-PASS class B extends A { constructor() { super() } }; new B did not throw exception.
+PASS new B did not throw exception.
PASS B() threw exception TypeError: Cannot call a class constructor.
PASS new (class { constructor() {} })() did not throw exception.
PASS (class { constructor() {} })() threw exception TypeError: Cannot call a class constructor.
Modified: trunk/LayoutTests/js/class-syntax-declaration-expected.txt (181972 => 181973)
--- trunk/LayoutTests/js/class-syntax-declaration-expected.txt 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/LayoutTests/js/class-syntax-declaration-expected.txt 2015-03-25 21:33:59 UTC (rev 181973)
@@ -16,15 +16,17 @@
PASS (new A).__proto__ is A.prototype
PASS A.prototype.constructor is A
PASS class threw exception SyntaxError: Unexpected end of script.
+PASS class [ threw exception SyntaxError: Unexpected token '['.
+PASS class { threw exception SyntaxError: Class statements must have a name..
PASS class X { threw exception SyntaxError: Unexpected end of script.
PASS class X { ( } threw exception SyntaxError: Unexpected token '('. Expected an identifier..
PASS class X {} did not throw exception.
PASS class X { constructor() {} constructor() {} } threw exception SyntaxError: Cannot declare multiple constructors in a single class..
PASS class X { constructor() {} static constructor() { return staticMethodValue; } } did not throw exception.
-PASS X.constructor() is staticMethodValue
+PASS class X { constructor() {} static constructor() { return staticMethodValue; } }; X.constructor() is staticMethodValue
PASS class X { constructor() {} static prototype() {} } threw exception SyntaxError: Cannot declare a static method named 'prototype'..
PASS class X { constructor() {} prototype() { return instanceMethodValue; } } did not throw exception.
-PASS (new X).prototype() is instanceMethodValue
+PASS class X { constructor() {} prototype() { return instanceMethodValue; } }; (new X).prototype() is instanceMethodValue
PASS class X { constructor() {} set foo(a) {} } did not throw exception.
PASS class X { constructor() {} set foo({x, y}) {} } did not throw exception.
PASS class X { constructor() {} set foo() {} } threw exception SyntaxError: Unexpected token ')'. setter functions must have one parameter..
Modified: trunk/LayoutTests/js/class-syntax-default-constructor-expected.txt (181972 => 181973)
--- trunk/LayoutTests/js/class-syntax-default-constructor-expected.txt 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/LayoutTests/js/class-syntax-default-constructor-expected.txt 2015-03-25 21:33:59 UTC (rev 181973)
@@ -3,11 +3,11 @@
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-PASS class A { }; new A instanceof A is true
+PASS new A instanceof A is true
PASS A() threw exception TypeError: Cannot call a class constructor.
PASS A.prototype.constructor instanceof Function is true
PASS A.prototype.constructor.name is "A"
-PASS class B extends A { }; new B instanceof A; new B instanceof A is true
+PASS new B instanceof A; new B instanceof A is true
PASS B() threw exception TypeError: Cannot call a class constructor.
PASS B.prototype.constructor.name is "B"
PASS A !== B is true
Added: trunk/LayoutTests/js/class-syntax-name-expected.txt (0 => 181973)
--- trunk/LayoutTests/js/class-syntax-name-expected.txt (rev 0)
+++ trunk/LayoutTests/js/class-syntax-name-expected.txt 2015-03-25 21:33:59 UTC (rev 181973)
@@ -0,0 +1,122 @@
+Tests for ES6 class name semantics in class statements and expressions
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Class statement
+PASS A threw exception ReferenceError: Can't find variable: A.
+PASS 'use strict'; A threw exception ReferenceError: Can't find variable: A.
+PASS class {} threw exception SyntaxError: Class statements must have a name..
+PASS 'use strict'; class {} threw exception SyntaxError: Class statements must have a name..
+PASS class { constructor() {} } threw exception SyntaxError: Class statements must have a name..
+PASS 'use strict'; class { constructor() {} } threw exception SyntaxError: Class statements must have a name..
+PASS class A { constructor() {} } did not throw exception.
+PASS 'use strict'; class A { constructor() {} } did not throw exception.
+PASS class A { constructor() {} }; A.toString() is 'function A() {}'
+PASS 'use strict'; class A { constructor() {} }; A.toString() is 'function A() {}'
+PASS class A { constructor() {} }; (new A) instanceof A is true
+PASS 'use strict'; class A { constructor() {} }; (new A) instanceof A is true
+PASS class A { constructor() { this.base = A; } }; (new A).base.toString() is 'function A() { this.base = A; }'
+PASS 'use strict'; class A { constructor() { this.base = A; } }; (new A).base.toString() is 'function A() { this.base = A; }'
+PASS class A { constructor() {} }; class B extends A {}; did not throw exception.
+PASS 'use strict'; class A { constructor() {} }; class B extends A {}; did not throw exception.
+PASS class A { constructor() {} }; class B extends A { constructor() {} }; B.toString() is 'function B() {}'
+PASS 'use strict'; class A { constructor() {} }; class B extends A { constructor() {} }; B.toString() is 'function B() {}'
+PASS class A { constructor() {} }; class B extends A {}; (new B) instanceof A is true
+PASS 'use strict'; class A { constructor() {} }; class B extends A {}; (new B) instanceof A is true
+PASS class A { constructor() {} }; class B extends A {}; (new B) instanceof B is true
+PASS 'use strict'; class A { constructor() {} }; class B extends A {}; (new B) instanceof B is true
+PASS class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).base.toString() is 'function A() {}'
+PASS 'use strict'; class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).base.toString() is 'function A() {}'
+PASS class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).derived.toString() is 'function B() { super(); this.base = A; this.derived = B; }'
+PASS 'use strict'; class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).derived.toString() is 'function B() { super(); this.base = A; this.derived = B; }'
+
+Class _expression_
+PASS A threw exception ReferenceError: Can't find variable: A.
+PASS 'use strict'; A threw exception ReferenceError: Can't find variable: A.
+PASS (class {}) did not throw exception.
+PASS 'use strict'; (class {}) did not throw exception.
+PASS (class { constructor(){} }) did not throw exception.
+PASS 'use strict'; (class { constructor(){} }) did not throw exception.
+PASS typeof (class {}) is "function"
+PASS 'use strict'; typeof (class {}) is "function"
+PASS (class A {}) did not throw exception.
+PASS 'use strict'; (class A {}) did not throw exception.
+PASS typeof (class A {}) is "function"
+PASS 'use strict'; typeof (class A {}) is "function"
+PASS (class A {}); A threw exception ReferenceError: Can't find variable: A.
+PASS 'use strict'; (class A {}); A threw exception ReferenceError: Can't find variable: A.
+PASS new (class A {}) did not throw exception.
+PASS 'use strict'; new (class A {}) did not throw exception.
+PASS typeof (new (class A {})) is "object"
+PASS 'use strict'; typeof (new (class A {})) is "object"
+PASS (new (class A { constructor() { this.base = A; } })).base did not throw exception.
+PASS 'use strict'; (new (class A { constructor() { this.base = A; } })).base did not throw exception.
+PASS (new (class A { constructor() { this.base = A; } })).base.toString() is "function A() { this.base = A; }"
+PASS 'use strict'; (new (class A { constructor() { this.base = A; } })).base.toString() is "function A() { this.base = A; }"
+PASS class A {}; (class B extends A {}) did not throw exception.
+PASS 'use strict'; class A {}; (class B extends A {}) did not throw exception.
+PASS class A {}; (class B extends A {}); B threw exception ReferenceError: Can't find variable: B.
+PASS 'use strict'; class A {}; (class B extends A {}); B threw exception ReferenceError: Can't find variable: B.
+PASS class A {}; new (class B extends A {}) did not throw exception.
+PASS 'use strict'; class A {}; new (class B extends A {}) did not throw exception.
+PASS class A {}; new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } }) did not throw exception.
+PASS 'use strict'; class A {}; new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } }) did not throw exception.
+PASS class A {}; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })) instanceof A is true
+PASS 'use strict'; class A {}; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })) instanceof A is true
+PASS class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).base.toString() is 'function A() {}'
+PASS 'use strict'; class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).base.toString() is 'function A() {}'
+PASS class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).derived.toString() is 'function B() { super(); this.base = A; this.derived = B; }'
+PASS 'use strict'; class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).derived.toString() is 'function B() { super(); this.base = A; this.derived = B; }'
+
+Class _expression_ assignment to variable
+PASS A threw exception ReferenceError: Can't find variable: A.
+PASS 'use strict'; A threw exception ReferenceError: Can't find variable: A.
+PASS var VarA = class {} did not throw exception.
+PASS 'use strict'; var VarA = class {} did not throw exception.
+PASS var VarA = class { constructor() {} }; VarA.toString() is 'function () {}'
+PASS 'use strict'; var VarA = class { constructor() {} }; VarA.toString() is 'function () {}'
+PASS VarA threw exception ReferenceError: Can't find variable: VarA.
+PASS 'use strict'; VarA threw exception ReferenceError: Can't find variable: VarA.
+PASS var VarA = class A { constructor() {} } did not throw exception.
+PASS 'use strict'; var VarA = class A { constructor() {} } did not throw exception.
+PASS var VarA = class A { constructor() {} }; VarA.toString() is 'function A() {}'
+PASS 'use strict'; var VarA = class A { constructor() {} }; VarA.toString() is 'function A() {}'
+PASS var VarA = class A { constructor() {} }; A.toString() threw exception ReferenceError: Can't find variable: A.
+PASS 'use strict'; var VarA = class A { constructor() {} }; A.toString() threw exception ReferenceError: Can't find variable: A.
+PASS var VarA = class A { constructor() {} }; (new VarA) instanceof VarA is true
+PASS 'use strict'; var VarA = class A { constructor() {} }; (new VarA) instanceof VarA is true
+PASS var VarA = class A { constructor() { this.base = A; } }; (new VarA).base.toString() is 'function A() { this.base = A; }'
+PASS 'use strict'; var VarA = class A { constructor() { this.base = A; } }; (new VarA).base.toString() is 'function A() { this.base = A; }'
+PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; did not throw exception.
+PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; did not throw exception.
+PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; B threw exception ReferenceError: Can't find variable: B.
+PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; B threw exception ReferenceError: Can't find variable: B.
+PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; VarB.toString() is 'function B() {}'
+PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; VarB.toString() is 'function B() {}'
+PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarA is true
+PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarA is true
+PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarB is true
+PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarB is true
+PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() { super(); this.base = VarA; this.derived = B; this.derivedVar = VarB; } }; (new VarB).base === VarA is true
+PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() { super(); this.base = VarA; this.derived = B; this.derivedVar = VarB; } }; (new VarB).base === VarA is true
+PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() { super(); this.base = VarA; this.derived = B; this.derivedVar = VarB; } }; (new VarB).derived === VarB is true
+PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() { super(); this.base = VarA; this.derived = B; this.derivedVar = VarB; } }; (new VarB).derived === VarB is true
+PASS var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() { super(); this.base = VarA; this.derived = B; this.derivedVar = VarB; } }; (new VarB).derivedVar === VarB is true
+PASS 'use strict'; var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() { super(); this.base = VarA; this.derived = B; this.derivedVar = VarB; } }; (new VarB).derivedVar === VarB is true
+
+Class statement binding in other circumstances
+PASS var result = A; result threw exception ReferenceError: Can't find variable: A.
+PASS 'use strict'; var result = A; result threw exception ReferenceError: Can't find variable: A.
+FAIL var result = A; class A {}; result should throw an exception. Was undefined.
+FAIL 'use strict'; var result = A; class A {}; result should throw an exception. Was undefined.
+PASS class A {}; var result = A; result did not throw exception.
+PASS 'use strict'; class A {}; var result = A; result did not throw exception.
+PASS eval('var Foo = 10'); Foo is 10
+PASS 'use strict'; eval('var Foo = 10'); Foo threw exception ReferenceError: Can't find variable: Foo.
+PASS eval('class Bar { constructor() {} }'); Bar.toString() is 'function Bar() {}'
+PASS 'use strict'; eval('class Bar { constructor() {} }'); Bar.toString() threw exception ReferenceError: Can't find variable: Bar.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/js/class-syntax-name.html (0 => 181973)
--- trunk/LayoutTests/js/class-syntax-name.html (rev 0)
+++ trunk/LayoutTests/js/class-syntax-name.html 2015-03-25 21:33:59 UTC (rev 181973)
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>
Modified: trunk/LayoutTests/js/script-tests/class-syntax-call.js (181972 => 181973)
--- trunk/LayoutTests/js/script-tests/class-syntax-call.js 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/LayoutTests/js/script-tests/class-syntax-call.js 2015-03-25 21:33:59 UTC (rev 181973)
@@ -1,8 +1,11 @@
description('Tests for calling the constructors of ES6 classes');
-shouldNotThrow('class A { constructor() {} }; new A');
+class A { constructor() {} };
+class B extends A { constructor() { super() } };
+
+shouldNotThrow('new A');
shouldThrow('A()', '"TypeError: Cannot call a class constructor"');
-shouldNotThrow('class B extends A { constructor() { super() } }; new B');
+shouldNotThrow('new B');
shouldThrow('B()', '"TypeError: Cannot call a class constructor"');
shouldNotThrow('new (class { constructor() {} })()');
shouldThrow('(class { constructor() {} })()', '"TypeError: Cannot call a class constructor"');
Modified: trunk/LayoutTests/js/script-tests/class-syntax-declaration.js (181972 => 181973)
--- trunk/LayoutTests/js/script-tests/class-syntax-declaration.js 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/LayoutTests/js/script-tests/class-syntax-declaration.js 2015-03-25 21:33:59 UTC (rev 181973)
@@ -28,15 +28,17 @@
shouldBe("A.prototype.constructor", "A");
shouldThrow("class", "'SyntaxError: Unexpected end of script'");
+shouldThrow("class [", "'SyntaxError: Unexpected token \\'[\\''");
+shouldThrow("class {", "'SyntaxError: Class statements must have a name.'");
shouldThrow("class X {", "'SyntaxError: Unexpected end of script'");
shouldThrow("class X { ( }", "'SyntaxError: Unexpected token \\'(\\'. Expected an identifier.'");
shouldNotThrow("class X {}");
shouldThrow("class X { constructor() {} constructor() {} }", "'SyntaxError: Cannot declare multiple constructors in a single class.'");
shouldNotThrow("class X { constructor() {} static constructor() { return staticMethodValue; } }");
-shouldBe("X.constructor()", "staticMethodValue");
+shouldBe("class X { constructor() {} static constructor() { return staticMethodValue; } }; X.constructor()", "staticMethodValue");
shouldThrow("class X { constructor() {} static prototype() {} }", "'SyntaxError: Cannot declare a static method named \\'prototype\\'.'");
shouldNotThrow("class X { constructor() {} prototype() { return instanceMethodValue; } }");
-shouldBe("(new X).prototype()", "instanceMethodValue");
+shouldBe("class X { constructor() {} prototype() { return instanceMethodValue; } }; (new X).prototype()", "instanceMethodValue");
shouldNotThrow("class X { constructor() {} set foo(a) {} }");
shouldNotThrow("class X { constructor() {} set foo({x, y}) {} }");
Modified: trunk/LayoutTests/js/script-tests/class-syntax-default-constructor.js (181972 => 181973)
--- trunk/LayoutTests/js/script-tests/class-syntax-default-constructor.js 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/LayoutTests/js/script-tests/class-syntax-default-constructor.js 2015-03-25 21:33:59 UTC (rev 181973)
@@ -1,11 +1,14 @@
description('Tests for ES6 class syntax default constructor');
-shouldBeTrue('class A { }; new A instanceof A');
+class A { };
+class B extends A { };
+
+shouldBeTrue('new A instanceof A');
shouldThrow('A()', '"TypeError: Cannot call a class constructor"');
shouldBeTrue('A.prototype.constructor instanceof Function');
shouldBe('A.prototype.constructor.name', '"A"');
-shouldBeTrue('class B extends A { }; new B instanceof A; new B instanceof A');
+shouldBeTrue('new B instanceof A; new B instanceof A');
shouldThrow('B()', '"TypeError: Cannot call a class constructor"');
shouldBe('B.prototype.constructor.name', '"B"');
shouldBeTrue('A !== B');
Added: trunk/LayoutTests/js/script-tests/class-syntax-name.js (0 => 181973)
--- trunk/LayoutTests/js/script-tests/class-syntax-name.js (rev 0)
+++ trunk/LayoutTests/js/script-tests/class-syntax-name.js 2015-03-25 21:33:59 UTC (rev 181973)
@@ -0,0 +1,88 @@
+description('Tests for ES6 class name semantics in class statements and expressions');
+
+function runTestShouldBe(statement, result) {
+ shouldBe(statement, result);
+ shouldBe("'use strict'; " + statement, result);
+}
+
+function runTestShouldBeTrue(statement) {
+ shouldBeTrue(statement);
+ shouldBeTrue("'use strict'; " + statement);
+}
+
+function runTestShouldThrow(statement) {
+ shouldThrow(statement);
+ shouldThrow("'use strict'; " + statement);
+}
+
+function runTestShouldNotThrow(statement) {
+ shouldNotThrow(statement);
+ shouldNotThrow("'use strict'; " + statement);
+}
+
+// Class statement. Class name added to global scope. Class name is available inside class scope and in global scope.
+debug('Class statement');
+runTestShouldThrow("A");
+runTestShouldThrow("class {}");
+runTestShouldThrow("class { constructor() {} }");
+runTestShouldNotThrow("class A { constructor() {} }");
+runTestShouldBe("class A { constructor() {} }; A.toString()", "'function A() {}'");
+runTestShouldBeTrue("class A { constructor() {} }; (new A) instanceof A");
+runTestShouldBe("class A { constructor() { this.base = A; } }; (new A).base.toString()", "'function A() { this.base = A; }'");
+runTestShouldNotThrow("class A { constructor() {} }; class B extends A {};");
+runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() {} }; B.toString()", "'function B() {}'");
+runTestShouldBeTrue("class A { constructor() {} }; class B extends A {}; (new B) instanceof A");
+runTestShouldBeTrue("class A { constructor() {} }; class B extends A {}; (new B) instanceof B");
+runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).base.toString()", "'function A() {}'");
+runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).derived.toString()", "'function B() { super(); this.base = A; this.derived = B; }'");
+
+// Class _expression_. Class name not added to scope. Class name is available inside class scope.
+debug(''); debug('Class _expression_');
+runTestShouldThrow("A");
+runTestShouldNotThrow("(class {})");
+runTestShouldNotThrow("(class { constructor(){} })");
+runTestShouldBe("typeof (class {})", '"function"');
+runTestShouldNotThrow("(class A {})");
+runTestShouldBe("typeof (class A {})", '"function"');
+runTestShouldThrow("(class A {}); A");
+runTestShouldNotThrow("new (class A {})");
+runTestShouldBe("typeof (new (class A {}))", '"object"');
+runTestShouldNotThrow("(new (class A { constructor() { this.base = A; } })).base");
+runTestShouldBe("(new (class A { constructor() { this.base = A; } })).base.toString()", '"function A() { this.base = A; }"');
+runTestShouldNotThrow("class A {}; (class B extends A {})");
+runTestShouldThrow("class A {}; (class B extends A {}); B");
+runTestShouldNotThrow("class A {}; new (class B extends A {})");
+runTestShouldNotThrow("class A {}; new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })");
+runTestShouldBeTrue("class A {}; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })) instanceof A");
+runTestShouldBe("class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).base.toString()", "'function A() {}'");
+runTestShouldBe("class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).derived.toString()", "'function B() { super(); this.base = A; this.derived = B; }'");
+
+// Assignment of a class _expression_ to a variable. Variable name available in scope, class name is not. Class name is available inside class scope.
+debug(''); debug('Class _expression_ assignment to variable');
+runTestShouldThrow("A");
+runTestShouldNotThrow("var VarA = class {}");
+runTestShouldBe("var VarA = class { constructor() {} }; VarA.toString()", "'function () {}'");
+runTestShouldThrow("VarA");
+runTestShouldNotThrow("var VarA = class A { constructor() {} }");
+runTestShouldBe("var VarA = class A { constructor() {} }; VarA.toString()", "'function A() {}'");
+runTestShouldThrow("var VarA = class A { constructor() {} }; A.toString()");
+runTestShouldBeTrue("var VarA = class A { constructor() {} }; (new VarA) instanceof VarA");
+runTestShouldBe("var VarA = class A { constructor() { this.base = A; } }; (new VarA).base.toString()", "'function A() { this.base = A; }'");
+runTestShouldNotThrow("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} };");
+runTestShouldThrow("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; B");
+runTestShouldBe("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; VarB.toString()", "'function B() {}'");
+runTestShouldBeTrue("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarA");
+runTestShouldBeTrue("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarB");
+runTestShouldBeTrue("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() { super(); this.base = VarA; this.derived = B; this.derivedVar = VarB; } }; (new VarB).base === VarA");
+runTestShouldBeTrue("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() { super(); this.base = VarA; this.derived = B; this.derivedVar = VarB; } }; (new VarB).derived === VarB");
+runTestShouldBeTrue("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() { super(); this.base = VarA; this.derived = B; this.derivedVar = VarB; } }; (new VarB).derivedVar === VarB");
+
+// FIXME: Class statement binding should be like `let`, not `var`.
+debug(''); debug('Class statement binding in other circumstances');
+runTestShouldThrow("var result = A; result");
+runTestShouldThrow("var result = A; class A {}; result");
+runTestShouldNotThrow("class A {}; var result = A; result");
+shouldBe("eval('var Foo = 10'); Foo", "10");
+shouldThrow("'use strict'; eval('var Foo = 10'); Foo");
+shouldBe("eval('class Bar { constructor() {} }'); Bar.toString()", "'function Bar() {}'");
+shouldThrow("'use strict'; eval('class Bar { constructor() {} }'); Bar.toString()");
Modified: trunk/Source/_javascript_Core/ChangeLog (181972 => 181973)
--- trunk/Source/_javascript_Core/ChangeLog 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-03-25 21:33:59 UTC (rev 181973)
@@ -1,3 +1,28 @@
+2015-03-25 Joseph Pecoraro <pecor...@apple.com>
+
+ ES6: Classes: Program level class statement throws exception in strict mode
+ https://bugs.webkit.org/show_bug.cgi?id=143038
+
+ Reviewed by Ryosuke Niwa.
+
+ Classes expose a name to the current lexical environment. This treats
+ "class X {}" like "var X = class X {}". Ideally it would be "let X = class X {}".
+ Also, improve error messages for class statements where the class is missing a name.
+
+ * parser/Parser.h:
+ * parser/Parser.cpp:
+ (JSC::Parser<LexerType>::parseClass):
+ Fill name in info parameter if needed. Better error message if name is needed and missing.
+
+ (JSC::Parser<LexerType>::parseClassDeclaration):
+ Pass info parameter to get name, and expose the name as a variable name.
+
+ (JSC::Parser<LexerType>::parsePrimaryExpression):
+ Pass info parameter that is ignored.
+
+ * parser/ParserFunctionInfo.h:
+ Add a parser info for class, to extract the name.
+
2015-03-25 Yusuke Suzuki <utatane....@gmail.com>
New map and set modification tests in r181922 fails
Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (181972 => 181973)
--- trunk/Source/_javascript_Core/parser/Parser.cpp 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp 2015-03-25 21:33:59 UTC (rev 181973)
@@ -1469,9 +1469,14 @@
JSTextPosition classStart = tokenStartPosition();
unsigned classStartLine = tokenLine();
- TreeClassExpression classExpr = parseClass(context, FunctionNeedsName);
+ ParserClassInfo<TreeBuilder> info;
+ TreeClassExpression classExpr = parseClass(context, FunctionNeedsName, info);
failIfFalse(classExpr, "Failed to parse class");
+ declareVariable(info.className);
+ // FIXME: This should be like `let`, not `var`.
+ context.addVar(info.className, DeclarationStacks::HasInitializer);
+
JSTextPosition classEnd = lastTokenEndPosition();
unsigned classEndLine = tokenLine();
@@ -1479,7 +1484,7 @@
}
template <typename LexerType>
-template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(TreeBuilder& context, FunctionRequirements requirements)
+template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(TreeBuilder& context, FunctionRequirements requirements, ParserClassInfo<TreeBuilder>& info)
{
ASSERT(match(CLASSTOKEN));
JSTokenLocation location(tokenLocation());
@@ -1491,9 +1496,12 @@
const Identifier* className = nullptr;
if (match(IDENT)) {
className = m_token.m_data.ident;
+ info.className = className;
next();
failIfFalse(classScope->declareVariable(className), "'", className->impl(), "' is not a valid class name");
} else if (requirements == FunctionNeedsName) {
+ if (match(OPENBRACE))
+ semanticFail("Class statements must have a name");
semanticFailureDueToKeyword("class name");
failDueToUnexpectedToken();
} else
@@ -2263,8 +2271,10 @@
return context.createFunctionExpr(location, info);
}
#if ENABLE(ES6_CLASS_SYNTAX)
- case CLASSTOKEN:
- return parseClass(context, FunctionNoRequirements);
+ case CLASSTOKEN: {
+ ParserClassInfo<TreeBuilder> info;
+ return parseClass(context, FunctionNoRequirements, info);
+ }
#endif
case OPENBRACE:
if (strictMode())
Modified: trunk/Source/_javascript_Core/parser/Parser.h (181972 => 181973)
--- trunk/Source/_javascript_Core/parser/Parser.h 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/Source/_javascript_Core/parser/Parser.h 2015-03-25 21:33:59 UTC (rev 181973)
@@ -774,7 +774,7 @@
template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionRequirements, FunctionParseMode, bool nameIsInContainingScope, ConstructorKind, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&);
#if ENABLE(ES6_CLASS_SYNTAX)
- template <class TreeBuilder> NEVER_INLINE TreeClassExpression parseClass(TreeBuilder&, FunctionRequirements);
+ template <class TreeBuilder> NEVER_INLINE TreeClassExpression parseClass(TreeBuilder&, FunctionRequirements, ParserClassInfo<TreeBuilder>&);
#endif
ALWAYS_INLINE int isBinaryOperator(JSTokenType);
Modified: trunk/Source/_javascript_Core/parser/ParserFunctionInfo.h (181972 => 181973)
--- trunk/Source/_javascript_Core/parser/ParserFunctionInfo.h 2015-03-25 21:19:05 UTC (rev 181972)
+++ trunk/Source/_javascript_Core/parser/ParserFunctionInfo.h 2015-03-25 21:33:59 UTC (rev 181973)
@@ -40,6 +40,13 @@
unsigned bodyStartColumn = 0;
};
+#if ENABLE(ES6_CLASS_SYNTAX)
+template <class TreeBuilder>
+struct ParserClassInfo {
+ const Identifier* className = 0;
+};
+#endif
+
}
#endif