Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 1 | // Copyright 2015 the V8 project authors. All rights reserved. |
| 2 | // Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
| 3 | // |
| 4 | // Redistribution and use in source and binary forms, with or without |
| 5 | // modification, are permitted provided that the following conditions |
| 6 | // are met: |
| 7 | // 1. Redistributions of source code must retain the above copyright |
| 8 | // notice, this list of conditions and the following disclaimer. |
| 9 | // 2. Redistributions in binary form must reproduce the above copyright |
| 10 | // notice, this list of conditions and the following disclaimer in the |
| 11 | // documentation and/or other materials provided with the distribution. |
| 12 | // |
| 13 | // THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY |
| 14 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 16 | // DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| 20 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 22 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 23 | |
| 24 | // Flags: --harmony-sloppy |
| 25 | |
| 26 | description('Tests for ES6 class name semantics in class statements and expressions'); |
| 27 | |
| 28 | function runTestShouldBe(statement, result) { |
| 29 | shouldBe(statement, result); |
| 30 | shouldBe("'use strict'; " + statement, result); |
| 31 | } |
| 32 | |
| 33 | function runTestShouldBeTrue(statement) { |
| 34 | shouldBeTrue(statement); |
| 35 | shouldBeTrue("'use strict'; " + statement); |
| 36 | } |
| 37 | |
| 38 | function runTestShouldThrow(statement) { |
| 39 | shouldThrow(statement); |
| 40 | shouldThrow("'use strict'; " + statement); |
| 41 | } |
| 42 | |
| 43 | function runTestShouldNotThrow(statement) { |
| 44 | shouldNotThrow(statement); |
| 45 | shouldNotThrow("'use strict'; " + statement); |
| 46 | } |
| 47 | |
| 48 | // Class statement. Class name added to global scope. Class name is available inside class scope and in global scope. |
| 49 | debug('Class statement'); |
| 50 | runTestShouldThrow("A"); |
| 51 | runTestShouldThrow("class {}"); |
| 52 | runTestShouldThrow("class { constructor() {} }"); |
| 53 | runTestShouldNotThrow("class A { constructor() {} }"); |
| 54 | runTestShouldBe("class A { constructor() {} }; A.toString()", "'class A { constructor() {} }'"); |
| 55 | runTestShouldBeTrue("class A { constructor() {} }; (new A) instanceof A"); |
| 56 | runTestShouldBe("class A { constructor() { this.base = A; } }; (new A).base.toString()", "'class A { constructor() { this.base = A; } }'"); |
| 57 | runTestShouldNotThrow("class A { constructor() {} }; class B extends A {};"); |
| 58 | runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() {} }; B.toString()", "'class B extends A { constructor() {} }'"); |
| 59 | runTestShouldBeTrue("class A { constructor() {} }; class B extends A {}; (new B) instanceof A"); |
| 60 | runTestShouldBeTrue("class A { constructor() {} }; class B extends A {}; (new B) instanceof B"); |
| 61 | runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).base.toString()", "'class A { constructor() {} }'"); |
| 62 | runTestShouldBe("class A { constructor() {} }; class B extends A { constructor() { super(); this.base = A; this.derived = B; } }; (new B).derived.toString()", "'class B extends A { constructor() { super(); this.base = A; this.derived = B; } }'"); |
| 63 | |
| 64 | // Class expression. Class name not added to scope. Class name is available inside class scope. |
| 65 | debug(''); debug('Class expression'); |
| 66 | runTestShouldThrow("A"); |
| 67 | runTestShouldNotThrow("(class {})"); |
| 68 | runTestShouldNotThrow("(class { constructor(){} })"); |
| 69 | runTestShouldBe("typeof (class {})", '"function"'); |
| 70 | runTestShouldNotThrow("(class A {})"); |
| 71 | runTestShouldBe("typeof (class A {})", '"function"'); |
| 72 | runTestShouldThrow("(class A {}); A"); |
| 73 | runTestShouldNotThrow("new (class A {})"); |
| 74 | runTestShouldBe("typeof (new (class A {}))", '"object"'); |
| 75 | runTestShouldNotThrow("(new (class A { constructor() { this.base = A; } })).base"); |
| 76 | runTestShouldBe("(new (class A { constructor() { this.base = A; } })).base.toString()", '"class A { constructor() { this.base = A; } }"'); |
| 77 | runTestShouldNotThrow("class A {}; (class B extends A {})"); |
| 78 | runTestShouldThrow("class A {}; (class B extends A {}); B"); |
| 79 | runTestShouldNotThrow("class A {}; new (class B extends A {})"); |
| 80 | runTestShouldNotThrow("class A {}; new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })"); |
| 81 | runTestShouldBeTrue("class A {}; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })) instanceof A"); |
| 82 | runTestShouldBe("class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).base.toString()", "'class A { constructor() {} }'"); |
| 83 | runTestShouldBe("class A { constructor() {} }; (new (class B extends A { constructor() { super(); this.base = A; this.derived = B; } })).derived.toString()", "'class B extends A { constructor() { super(); this.base = A; this.derived = B; } }'"); |
| 84 | |
| 85 | // Assignment of a class expression to a variable. Variable name available in scope, class name is not. Class name is available inside class scope. |
| 86 | debug(''); debug('Class expression assignment to variable'); |
| 87 | runTestShouldThrow("A"); |
| 88 | runTestShouldNotThrow("var VarA = class {}"); |
| 89 | runTestShouldBe("var VarA = class { constructor() {} }; VarA.toString()", "'class { constructor() {} }'"); |
| 90 | runTestShouldThrow("VarA"); |
| 91 | runTestShouldNotThrow("var VarA = class A { constructor() {} }"); |
| 92 | runTestShouldBe("var VarA = class A { constructor() {} }; VarA.toString()", "'class A { constructor() {} }'"); |
| 93 | runTestShouldThrow("var VarA = class A { constructor() {} }; A.toString()"); |
| 94 | runTestShouldBeTrue("var VarA = class A { constructor() {} }; (new VarA) instanceof VarA"); |
| 95 | runTestShouldBe("var VarA = class A { constructor() { this.base = A; } }; (new VarA).base.toString()", "'class A { constructor() { this.base = A; } }'"); |
| 96 | runTestShouldNotThrow("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} };"); |
| 97 | runTestShouldThrow("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; B"); |
| 98 | runTestShouldBe("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { constructor() {} }; VarB.toString()", "'class B extends VarA { constructor() {} }'"); |
| 99 | runTestShouldBeTrue("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarA"); |
| 100 | runTestShouldBeTrue("var VarA = class A { constructor() {} }; var VarB = class B extends VarA { }; (new VarB) instanceof VarB"); |
| 101 | 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"); |
| 102 | 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"); |
| 103 | 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"); |
| 104 | |
| 105 | // FIXME: Class statement binding should be like `let`, not `var`. |
| 106 | debug(''); debug('Class statement binding in other circumstances'); |
| 107 | runTestShouldThrow("var result = A; result"); |
| 108 | runTestShouldThrow("var result = A; class A {}; result"); |
| 109 | runTestShouldThrow("class A { constructor() { A = 1; } }; new A"); |
| 110 | runTestShouldBe("class A { constructor() { } }; A = 1; A", "1"); |
| 111 | runTestShouldNotThrow("class A {}; var result = A; result"); |
| 112 | shouldBe("eval('var Foo = 10'); Foo", "10"); |
| 113 | shouldThrow("'use strict'; eval('var Foo = 10'); Foo"); |
| 114 | shouldBe("eval('class Bar { constructor() {} }; Bar.toString()')", "'class Bar { constructor() {} }'"); |
| 115 | shouldThrow("'use strict'; eval('class Bar { constructor() {} }'); Bar.toString()"); |