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()"); 116