// Copyright 2014 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: --allow-natives-syntax --harmony-do-expressions (function TestBasics() { var C = class C {} assertEquals(typeof C, 'function'); assertEquals(C.__proto__, Function.prototype); assertEquals(Object.prototype, Object.getPrototypeOf(C.prototype)); assertEquals(Function.prototype, Object.getPrototypeOf(C)); assertEquals('C', C.name); class D {} assertEquals(typeof D, 'function'); assertEquals(D.__proto__, Function.prototype); assertEquals(Object.prototype, Object.getPrototypeOf(D.prototype)); assertEquals(Function.prototype, Object.getPrototypeOf(D)); assertEquals('D', D.name); class D2 { constructor() {} } assertEquals('D2', D2.name); var E = class {} assertEquals('E', E.name); // Should be 'E'. var F = class { constructor() {} }; assertEquals('F', F.name); // Should be 'F'. })(); (function TestBasicsExtends() { class C extends null {} assertEquals(typeof C, 'function'); assertEquals(C.__proto__, Function.prototype); assertEquals(null, Object.getPrototypeOf(C.prototype)); class D extends C {} assertEquals(typeof D, 'function'); assertEquals(D.__proto__, C); assertEquals(C.prototype, Object.getPrototypeOf(D.prototype)); })(); (function TestSideEffectInExtends() { var calls = 0; class C {} class D extends (calls++, C) {} assertEquals(1, calls); assertEquals(typeof D, 'function'); assertEquals(D.__proto__, C); assertEquals(C.prototype, Object.getPrototypeOf(D.prototype)); })(); (function TestInvalidExtends() { assertThrows(function() { class C extends 42 {} }, TypeError); assertThrows(function() { // Function but its .prototype is not null or a function. class C extends Math.abs {} }, TypeError); assertThrows(function() { Math.abs.prototype = 42; class C extends Math.abs {} }, TypeError); delete Math.abs.prototype; assertThrows(function() { function* g() {} class C extends g {} }, TypeError); })(); (function TestConstructorProperty() { class C {} assertEquals(C, C.prototype.constructor); var descr = Object.getOwnPropertyDescriptor(C.prototype, 'constructor'); assertTrue(descr.configurable); assertFalse(descr.enumerable); assertTrue(descr.writable); })(); (function TestPrototypeProperty() { class C {} var descr = Object.getOwnPropertyDescriptor(C, 'prototype'); assertFalse(descr.configurable); assertFalse(descr.enumerable); assertFalse(descr.writable); })(); (function TestConstructor() { var count = 0; class C { constructor() { assertEquals(Object.getPrototypeOf(this), C.prototype); count++; } } assertEquals(C, C.prototype.constructor); var descr = Object.getOwnPropertyDescriptor(C.prototype, 'constructor'); assertTrue(descr.configurable); assertFalse(descr.enumerable); assertTrue(descr.writable); var c = new C(); assertEquals(1, count); assertEquals(Object.getPrototypeOf(c), C.prototype); })(); (function TestImplicitConstructor() { class C {} var c = new C(); assertEquals(Object.getPrototypeOf(c), C.prototype); })(); (function TestConstructorStrict() { class C { constructor() { assertThrows(function() { nonExistingBinding = 42; }, ReferenceError); } } new C(); })(); (function TestSuperInConstructor() { var calls = 0; class B {} B.prototype.x = 42; class C extends B { constructor() { super(); calls++; assertEquals(42, super.x); } } new C; assertEquals(1, calls); })(); (function TestStrictMode() { class C {} with ({a: 1}) { assertEquals(1, a); } assertThrows('class C extends function B() { with ({}); return B; }() {}', SyntaxError); var D = class extends function() { arguments.caller; } {}; assertThrows(function() { Object.getPrototypeOf(D).arguments; }, TypeError); assertThrows(function() { new D; }, TypeError); })(); (function TestToString() { class C {} assertEquals('class C {}', C.toString()); class D { constructor() { 42; } } assertEquals('class D { constructor() { 42; } }', D.toString()); class E { x() { 42; } } assertEquals('class E { x() { 42; } }', E.toString()); })(); function assertMethodDescriptor(object, name) { var descr = Object.getOwnPropertyDescriptor(object, name); assertTrue(descr.configurable); assertFalse(descr.enumerable); assertTrue(descr.writable); assertEquals('function', typeof descr.value); assertFalse('prototype' in descr.value); } function assertGetterDescriptor(object, name) { var descr = Object.getOwnPropertyDescriptor(object, name); assertTrue(descr.configurable); assertFalse(descr.enumerable); assertEquals('function', typeof descr.get); assertFalse('prototype' in descr.get); assertEquals(undefined, descr.set); } function assertSetterDescriptor(object, name) { var descr = Object.getOwnPropertyDescriptor(object, name); assertTrue(descr.configurable); assertFalse(descr.enumerable); assertEquals(undefined, descr.get); assertEquals('function', typeof descr.set); assertFalse('prototype' in descr.set); } function assertAccessorDescriptor(object, name) { var descr = Object.getOwnPropertyDescriptor(object, name); assertTrue(descr.configurable); assertFalse(descr.enumerable); assertEquals('function', typeof descr.get); assertEquals('function', typeof descr.set); assertFalse('prototype' in descr.get); assertFalse('prototype' in descr.set); } (function TestMethods() { class C { method() { return 1; } static staticMethod() { return 2; } method2() { return 3; } static staticMethod2() { return 4; } } assertMethodDescriptor(C.prototype, 'method'); assertMethodDescriptor(C.prototype, 'method2'); assertMethodDescriptor(C, 'staticMethod'); assertMethodDescriptor(C, 'staticMethod2'); assertEquals(1, new C().method()); assertEquals(2, C.staticMethod()); assertEquals(3, new C().method2()); assertEquals(4, C.staticMethod2()); })(); (function TestGetters() { class C { get x() { return 1; } static get staticX() { return 2; } get y() { return 3; } static get staticY() { return 4; } } assertGetterDescriptor(C.prototype, 'x'); assertGetterDescriptor(C.prototype, 'y'); assertGetterDescriptor(C, 'staticX'); assertGetterDescriptor(C, 'staticY'); assertEquals(1, new C().x); assertEquals(2, C.staticX); assertEquals(3, new C().y); assertEquals(4, C.staticY); })(); (function TestSetters() { var x, staticX, y, staticY; class C { set x(v) { x = v; } static set staticX(v) { staticX = v; } set y(v) { y = v; } static set staticY(v) { staticY = v; } } assertSetterDescriptor(C.prototype, 'x'); assertSetterDescriptor(C.prototype, 'y'); assertSetterDescriptor(C, 'staticX'); assertSetterDescriptor(C, 'staticY'); assertEquals(1, new C().x = 1); assertEquals(1, x); assertEquals(2, C.staticX = 2); assertEquals(2, staticX); assertEquals(3, new C().y = 3); assertEquals(3, y); assertEquals(4, C.staticY = 4); assertEquals(4, staticY); })(); (function TestSideEffectsInPropertyDefine() { function B() {} B.prototype = { constructor: B, set m(v) { throw Error(); } }; class C extends B { m() { return 1; } } assertEquals(1, new C().m()); })(); (function TestAccessors() { class C { constructor(x) { this._x = x; } get x() { return this._x; } set x(v) { this._x = v; } static get staticX() { return this._x; } static set staticX(v) { this._x = v; } } assertAccessorDescriptor(C.prototype, 'x'); assertAccessorDescriptor(C, 'staticX'); var c = new C(1); c._x = 1; assertEquals(1, c.x); c.x = 2; assertEquals(2, c._x); C._x = 3; assertEquals(3, C.staticX); C._x = 4; assertEquals(4, C.staticX ); })(); (function TestProto() { class C { __proto__() { return 1; } } assertMethodDescriptor(C.prototype, '__proto__'); assertEquals(1, new C().__proto__()); })(); (function TestProtoStatic() { class C { static __proto__() { return 1; } } assertMethodDescriptor(C, '__proto__'); assertEquals(1, C.__proto__()); })(); (function TestProtoAccessor() { class C { get __proto__() { return this._p; } set __proto__(v) { this._p = v; } } assertAccessorDescriptor(C.prototype, '__proto__'); var c = new C(); c._p = 1; assertEquals(1, c.__proto__); c.__proto__ = 2; assertEquals(2, c.__proto__); })(); (function TestStaticProtoAccessor() { class C { static get __proto__() { return this._p; } static set __proto__(v) { this._p = v; } } assertAccessorDescriptor(C, '__proto__'); C._p = 1; assertEquals(1, C.__proto__); C.__proto__ = 2; assertEquals(2, C.__proto__); })(); (function TestSettersOnProto() { function Base() {} Base.prototype = { set constructor(_) { assertUnreachable(); }, set m(_) { assertUnreachable(); } }; Object.defineProperty(Base, 'staticM', { set: function() { assertUnreachable(); } }); class C extends Base { m() { return 1; } static staticM() { return 2; } } assertEquals(1, new C().m()); assertEquals(2, C.staticM()); })(); (function TestConstructableButNoPrototype() { var Base = function() {}.bind(); assertThrows(function() { class C extends Base {} }, TypeError); })(); (function TestPrototypeGetter() { var calls = 0; var Base = function() {}.bind(); Object.defineProperty(Base, 'prototype', { get: function() { calls++; return null; }, configurable: true }); class C extends Base {} assertEquals(1, calls); calls = 0; Object.defineProperty(Base, 'prototype', { get: function() { calls++; return 42; }, configurable: true }); assertThrows(function() { class C extends Base {} }, TypeError); assertEquals(1, calls); })(); (function TestPrototypeSetter() { var Base = function() {}.bind(); Object.defineProperty(Base, 'prototype', { set: function() { assertUnreachable(); } }); assertThrows(function() { class C extends Base {} }, TypeError); })(); (function TestSuperInMethods() { class B { method() { return 1; } get x() { return 2; } } class C extends B { method() { assertEquals(2, super.x); return super.method(); } } assertEquals(1, new C().method()); })(); (function TestSuperInGetter() { class B { method() { return 1; } get x() { return 2; } } class C extends B { get y() { assertEquals(2, super.x); return super.method(); } } assertEquals(1, new C().y); })(); (function TestSuperInSetter() { class B { method() { return 1; } get x() { return 2; } } class C extends B { set y(v) { assertEquals(3, v); assertEquals(2, super.x); assertEquals(1, super.method()); } } assertEquals(3, new C().y = 3); })(); (function TestSuperInStaticMethods() { class B { static method() { return 1; } static get x() { return 2; } } class C extends B { static method() { assertEquals(2, super.x); return super.method(); } } assertEquals(1, C.method()); })(); (function TestSuperInStaticGetter() { class B { static method() { return 1; } static get x() { return 2; } } class C extends B { static get x() { assertEquals(2, super.x); return super.method(); } } assertEquals(1, C.x); })(); (function TestSuperInStaticSetter() { class B { static method() { return 1; } static get x() { return 2; } } class C extends B { static set x(v) { assertEquals(3, v); assertEquals(2, super.x); assertEquals(1, super.method()); } } assertEquals(3, C.x = 3); })(); (function TestNumericPropertyNames() { class B { 1() { return 1; } get 2() { return 2; } set 3(_) {} static 4() { return 4; } static get 5() { return 5; } static set 6(_) {} } assertMethodDescriptor(B.prototype, '1'); assertGetterDescriptor(B.prototype, '2'); assertSetterDescriptor(B.prototype, '3'); assertMethodDescriptor(B, '4'); assertGetterDescriptor(B, '5'); assertSetterDescriptor(B, '6'); class C extends B { 1() { return super[1](); } get 2() { return super[2]; } static 4() { return super[4](); } static get 5() { return super[5]; } } assertEquals(1, new C()[1]()); assertEquals(2, new C()[2]); assertEquals(4, C[4]()); assertEquals(5, C[5]); })(); (function TestDefaultConstructorNoCrash() { // Regression test for https://code.google.com/p/v8/issues/detail?id=3661 class C {} assertThrows(function () {C();}, TypeError); assertThrows(function () {C(1);}, TypeError); assertTrue(new C() instanceof C); assertTrue(new C(1) instanceof C); })(); (function TestConstructorCall(){ var realmIndex = Realm.create(); var otherTypeError = Realm.eval(realmIndex, "TypeError"); var A = Realm.eval(realmIndex, '"use strict"; class A {}'); var instance = new A(); var constructor = instance.constructor; var otherTypeError = Realm.eval(realmIndex, 'TypeError'); if (otherTypeError === TypeError) { throw Error('Should not happen!'); } // ES6 9.2.1[[Call]] throws a TypeError in the caller context/Realm when the // called function is a classConstructor assertThrows(function() { Realm.eval(realmIndex, "A()") }, otherTypeError); assertThrows(function() { instance.constructor() }, TypeError); assertThrows(function() { A() }, TypeError); // ES6 9.3.1 call() first activates the callee context before invoking the // method. The TypeError from the constructor is thus thrown in the other // Realm. assertThrows(function() { Realm.eval(realmIndex, "A.call()") }, otherTypeError); assertThrows(function() { constructor.call() }, otherTypeError); assertThrows(function() { A.call() }, otherTypeError); })(); (function TestConstructorCallOptimized() { class A { }; function invoke_constructor() { A() } function call_constructor() { A.call() } function apply_constructor() { A.apply() } for (var i=0; i<3; i++) { assertThrows(invoke_constructor); assertThrows(call_constructor); assertThrows(apply_constructor); } // Make sure we still check for class constructors when calling optimized // code. %OptimizeFunctionOnNextCall(invoke_constructor); assertThrows(invoke_constructor); %OptimizeFunctionOnNextCall(call_constructor); assertThrows(call_constructor); %OptimizeFunctionOnNextCall(apply_constructor); assertThrows(apply_constructor); })(); (function TestDefaultConstructor() { var calls = 0; class Base { constructor() { calls++; } } class Derived extends Base {} var object = new Derived; assertEquals(1, calls); calls = 0; assertThrows(function() { Derived(); }, TypeError); assertEquals(0, calls); })(); (function TestDefaultConstructorArguments() { var args, self; class Base { constructor() { self = this; args = arguments; } } class Derived extends Base {} new Derived; assertEquals(0, args.length); new Derived(0, 1, 2); assertEquals(3, args.length); assertTrue(self instanceof Derived); var arr = new Array(100); var obj = {}; assertThrows(function() {Derived.apply(obj, arr);}, TypeError); })(); (function TestDefaultConstructorArguments2() { var args; class Base { constructor(x, y) { args = arguments; } } class Derived extends Base {} new Derived; assertEquals(0, args.length); new Derived(1); assertEquals(1, args.length); assertEquals(1, args[0]); new Derived(1, 2, 3); assertEquals(3, args.length); assertEquals(1, args[0]); assertEquals(2, args[1]); assertEquals(3, args[2]); })(); (function TestNameBindingConst() { assertThrows('class C { constructor() { C = 42; } }; new C();', TypeError); assertThrows('new (class C { constructor() { C = 42; } })', TypeError); assertThrows('class C { m() { C = 42; } }; new C().m()', TypeError); assertThrows('new (class C { m() { C = 42; } }).m()', TypeError); assertThrows('class C { get x() { C = 42; } }; new C().x', TypeError); assertThrows('(new (class C { get x() { C = 42; } })).x', TypeError); assertThrows('class C { set x(_) { C = 42; } }; new C().x = 15;', TypeError); assertThrows('(new (class C { set x(_) { C = 42; } })).x = 15;', TypeError); })(); (function TestNameBinding() { var C2; class C { constructor() { C2 = C; } m() { C2 = C; } get x() { C2 = C; } set x(_) { C2 = C; } } new C(); assertEquals(C, C2); C2 = undefined; new C().m(); assertEquals(C, C2); C2 = undefined; new C().x; assertEquals(C, C2); C2 = undefined; new C().x = 1; assertEquals(C, C2); })(); (function TestNameBindingExpression() { var C3; var C = class C2 { constructor() { assertEquals(C2, C); C3 = C2; } m() { assertEquals(C2, C); C3 = C2; } get x() { assertEquals(C2, C); C3 = C2; } set x(_) { assertEquals(C2, C); C3 = C2; } } new C(); assertEquals(C, C3); C3 = undefined; new C().m(); assertEquals(C, C3); C3 = undefined; new C().x; assertEquals(C, C3); C3 = undefined; new C().x = 1; assertEquals(C, C3); })(); (function TestNameBindingInExtendsExpression() { assertThrows(function() { class x extends x {} }, ReferenceError); assertThrows(function() { (class x extends x {}); }, ReferenceError); assertThrows(function() { var x = (class x extends x {}); }, ReferenceError); })(); (function TestThisAccessRestriction() { class Base {} (function() { class C extends Base { constructor() { var y; super(); } }; new C(); }()); assertThrows(function() { class C extends Base { constructor() { super(this.x); } }; new C(); }, ReferenceError); assertThrows(function() { class C extends Base { constructor() { super(this); } }; new C(); }, ReferenceError); assertThrows(function() { class C extends Base { constructor() { super.method(); super(this); } }; new C(); }, ReferenceError); assertThrows(function() { class C extends Base { constructor() { super(super.method()); } }; new C(); }, ReferenceError); assertThrows(function() { class C extends Base { constructor() { super(super()); } }; new C(); }, ReferenceError); assertThrows(function() { class C extends Base { constructor() { super(1, 2, Object.getPrototypeOf(this)); } }; new C(); }, ReferenceError); (function() { class C extends Base { constructor() { { super(1, 2); } } }; new C(); }()); (function() { class C extends Base { constructor() { if (1) super(); } }; new C(); }()); class C1 extends Object { constructor() { 'use strict'; super(); } }; new C1(); class C2 extends Object { constructor() { ; 'use strict';;;;; super(); } }; new C2(); class C3 extends Object { constructor() { ; 'use strict';;;;; // This is a comment. super(); } }; new C3(); }()); function testClassRestrictedProperties(C) { assertEquals(false, C.hasOwnProperty("arguments")); assertThrows(function() { return C.arguments; }, TypeError); assertThrows(function() { C.arguments = {}; }, TypeError); assertEquals(false, C.hasOwnProperty("caller")); assertThrows(function() { return C.caller; }, TypeError); assertThrows(function() { C.caller = {}; }, TypeError); assertEquals(false, (new C).method.hasOwnProperty("arguments")); assertThrows(function() { return new C().method.arguments; }, TypeError); assertThrows(function() { new C().method.arguments = {}; }, TypeError); assertEquals(false, (new C).method.hasOwnProperty("caller")); assertThrows(function() { return new C().method.caller; }, TypeError); assertThrows(function() { new C().method.caller = {}; }, TypeError); } (function testRestrictedPropertiesStrict() { "use strict"; class ClassWithDefaultConstructor { method() {} } class Class { constructor() {} method() {} } class DerivedClassWithDefaultConstructor extends Class {} class DerivedClass extends Class { constructor() { super(); } } testClassRestrictedProperties(ClassWithDefaultConstructor); testClassRestrictedProperties(Class); testClassRestrictedProperties(DerivedClassWithDefaultConstructor); testClassRestrictedProperties(DerivedClass); testClassRestrictedProperties(class { method() {} }); testClassRestrictedProperties(class { constructor() {} method() {} }); testClassRestrictedProperties(class extends Class { }); testClassRestrictedProperties( class extends Class { constructor() { super(); } }); })(); (function testRestrictedPropertiesSloppy() { class ClassWithDefaultConstructor { method() {} } class Class { constructor() {} method() {} } class DerivedClassWithDefaultConstructor extends Class {} class DerivedClass extends Class { constructor() { super(); } } testClassRestrictedProperties(ClassWithDefaultConstructor); testClassRestrictedProperties(Class); testClassRestrictedProperties(DerivedClassWithDefaultConstructor); testClassRestrictedProperties(DerivedClass); testClassRestrictedProperties(class { method() {} }); testClassRestrictedProperties(class { constructor() {} method() {} }); testClassRestrictedProperties(class extends Class { }); testClassRestrictedProperties( class extends Class { constructor() { super(); } }); })(); (function testReturnFromClassLiteral() { function usingDoExpressionInBody() { let x = 42; let dummy = function() {x}; try { class C { dummy() {C} [do {return}]() {} }; } finally { return x; } } assertEquals(42, usingDoExpressionInBody()); function usingDoExpressionInExtends() { let x = 42; let dummy = function() {x}; try { class C extends (do {return}) { dummy() {C} }; } finally { return x; } } assertEquals(42, usingDoExpressionInExtends()); function usingYieldInBody() { function* foo() { class C { [yield]() {} } } var g = foo(); g.next(); return g.return(42).value; } assertEquals(42, usingYieldInBody()); function usingYieldInExtends() { function* foo() { class C extends (yield) {}; } var g = foo(); g.next(); return g.return(42).value; } assertEquals(42, usingYieldInExtends()); })();