// 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. 'use strict'; function ID(x) { return x; } (function TestClassMethodString() { class C { a() { return 'A'} ['b']() { return 'B'; } c() { return 'C'; } [ID('d')]() { return 'D'; } } assertEquals('A', new C().a()); assertEquals('B', new C().b()); assertEquals('C', new C().c()); assertEquals('D', new C().d()); assertArrayEquals([], Object.keys(C.prototype)); assertArrayEquals(['constructor', 'a', 'b', 'c', 'd'], Object.getOwnPropertyNames(C.prototype)); })(); (function TestClassMethodNumber() { class C { a() { return 'A'; } [1]() { return 'B'; } c() { return 'C'; } [ID(2)]() { return 'D'; } } assertEquals('A', new C().a()); assertEquals('B', new C()[1]()); assertEquals('C', new C().c()); assertEquals('D', new C()[2]()); // Array indexes first. assertArrayEquals([], Object.keys(C.prototype)); assertArrayEquals(['1', '2', 'constructor', 'a', 'c'], Object.getOwnPropertyNames(C.prototype)); })(); (function TestClassMethodSymbol() { var sym1 = Symbol(); var sym2 = Symbol(); class C { a() { return 'A'; } [sym1]() { return 'B'; } c() { return 'C'; } [ID(sym2)]() { return 'D'; } } assertEquals('A', new C().a()); assertEquals('B', new C()[sym1]()); assertEquals('C', new C().c()); assertEquals('D', new C()[sym2]()); assertArrayEquals([], Object.keys(C.prototype)); assertArrayEquals(['constructor', 'a', 'c'], Object.getOwnPropertyNames(C.prototype)); assertArrayEquals([sym1, sym2], Object.getOwnPropertySymbols(C.prototype)); })(); (function TestStaticClassMethodString() { class C { static a() { return 'A'} static ['b']() { return 'B'; } static c() { return 'C'; } static ['d']() { return 'D'; } } assertEquals('A', C.a()); assertEquals('B', C.b()); assertEquals('C', C.c()); assertEquals('D', C.d()); assertArrayEquals([], Object.keys(C)); assertArrayEquals(['length', 'name', 'prototype', 'a', 'b', 'c', 'd'], Object.getOwnPropertyNames(C)); })(); (function TestStaticClassMethodNumber() { class C { static a() { return 'A'; } static [1]() { return 'B'; } static c() { return 'C'; } static [2]() { return 'D'; } } assertEquals('A', C.a()); assertEquals('B', C[1]()); assertEquals('C', C.c()); assertEquals('D', C[2]()); // Array indexes first. assertArrayEquals([], Object.keys(C)); assertArrayEquals(['1', '2', 'length', 'name', 'prototype', 'a', 'c'], Object.getOwnPropertyNames(C)); })(); (function TestStaticClassMethodSymbol() { var sym1 = Symbol(); var sym2 = Symbol(); class C { static a() { return 'A'; } static [sym1]() { return 'B'; } static c() { return 'C'; } static [sym2]() { return 'D'; } } assertEquals('A', C.a()); assertEquals('B', C[sym1]()); assertEquals('C', C.c()); assertEquals('D', C[sym2]()); assertArrayEquals([], Object.keys(C)); assertArrayEquals(['length', 'name', 'prototype', 'a', 'c'], Object.getOwnPropertyNames(C)); assertArrayEquals([sym1, sym2], Object.getOwnPropertySymbols(C)); })(); function assertIteratorResult(value, done, result) { assertEquals({ value: value, done: done}, result); } (function TestGeneratorComputedName() { class C { *['a']() { yield 1; yield 2; } } var iter = new C().a(); assertIteratorResult(1, false, iter.next()); assertIteratorResult(2, false, iter.next()); assertIteratorResult(undefined, true, iter.next()); assertArrayEquals([], Object.keys(C.prototype)); assertArrayEquals(['constructor', 'a'], Object.getOwnPropertyNames(C.prototype)); })(); (function TestToNameSideEffects() { var counter = 0; var key1 = { toString: function() { assertEquals(0, counter++); return 'b'; } }; var key2 = { toString: function() { assertEquals(1, counter++); return 'd'; } }; class C { a() { return 'A'; } [key1]() { return 'B'; } c() { return 'C'; } [key2]() { return 'D'; } } assertEquals(2, counter); assertEquals('A', new C().a()); assertEquals('B', new C().b()); assertEquals('C', new C().c()); assertEquals('D', new C().d()); assertArrayEquals([], Object.keys(C.prototype)); assertArrayEquals(['constructor', 'a', 'b', 'c', 'd'], Object.getOwnPropertyNames(C.prototype)); })(); (function TestToNameSideEffectsNumbers() { var counter = 0; var key1 = { valueOf: function() { assertEquals(0, counter++); return 1; }, toString: null }; var key2 = { valueOf: function() { assertEquals(1, counter++); return 2; }, toString: null }; class C { a() { return 'A'; } [key1]() { return 'B'; } c() { return 'C'; } [key2]() { return 'D'; } } assertEquals(2, counter); assertEquals('A', new C().a()); assertEquals('B', new C()[1]()); assertEquals('C', new C().c()); assertEquals('D', new C()[2]()); // Array indexes first. assertArrayEquals([], Object.keys(C.prototype)); assertArrayEquals(['1', '2', 'constructor', 'a', 'c'], Object.getOwnPropertyNames(C.prototype)); })(); (function TestLength() { class C { static ['length']() { return 42; } } assertEquals(42, C.length()); class C1 { static get ['length']() { return 'A'; } } assertEquals('A', C1.length); class C2 { static get length() { assertUnreachable(); } static get ['length']() { return 'B'; } } assertEquals('B', C2.length); class C3 { static get length() { assertUnreachable(); } static get ['length']() { assertUnreachable(); } static get ['length']() { return 'C'; } } assertEquals('C', C3.length); class C4 { static get ['length']() { assertUnreachable(); } static get length() { return 'D'; } } assertEquals('D', C4.length); })(); (function TestGetter() { class C { get ['a']() { return 'A'; } } assertEquals('A', new C().a); class C2 { get b() { assertUnreachable(); } get ['b']() { return 'B'; } } assertEquals('B', new C2().b); class C3 { get c() { assertUnreachable(); } get ['c']() { assertUnreachable(); } get ['c']() { return 'C'; } } assertEquals('C', new C3().c); class C4 { get ['d']() { assertUnreachable(); } get d() { return 'D'; } } assertEquals('D', new C4().d); })(); (function TestSetter() { var calls = 0; class C { set ['a'](_) { calls++; } } new C().a = 'A'; assertEquals(1, calls); calls = 0; class C2 { set b(_) { assertUnreachable(); } set ['b'](_) { calls++; } } new C2().b = 'B'; assertEquals(1, calls); calls = 0; class C3 { set c(_) { assertUnreachable() } set ['c'](_) { assertUnreachable() } set ['c'](_) { calls++ } } new C3().c = 'C'; assertEquals(1, calls); calls = 0; class C4 { set ['d'](_) { assertUnreachable() } set d(_) { calls++ } } new C4().d = 'D'; assertEquals(1, calls); })(); (function TestPrototype() { assertThrows(function() { class C { static ['prototype']() { return 1; } } }, TypeError); assertThrows(function() { class C2 { static get ['prototype']() { return 2; } } }, TypeError); assertThrows(function() { class C3 { static set ['prototype'](x) { assertEquals(3, x); } } }, TypeError); assertThrows(function() { class C4 { static *['prototype']() { yield 1; yield 2; } } }, TypeError); })(); (function TestPrototypeConcat() { assertThrows(function() { class C { static ['pro' + 'tot' + 'ype']() { return 1; } } }, TypeError); assertThrows(function() { class C2 { static get ['pro' + 'tot' + 'ype']() { return 2; } } }, TypeError); assertThrows(function() { class C3 { static set ['pro' + 'tot' + 'ype'](x) { assertEquals(3, x); } } }, TypeError); assertThrows(function() { class C4 { static *['pro' + 'tot' + 'ype']() { yield 1; yield 2; } } }, TypeError); })(); (function TestConstructor() { // Normally a constructor property is not allowed. class C { ['constructor']() { return 1; } } assertTrue(C !== C.prototype.constructor); assertEquals(1, new C().constructor()); class C2 { get ['constructor']() { return 2; } } assertEquals(2, new C2().constructor); var calls = 0; class C3 { set ['constructor'](x) { assertEquals(3, x); calls++; } } new C3().constructor = 3; assertEquals(1, calls); class C4 { *['constructor']() { yield 1; yield 2; } } var iter = new C4().constructor(); assertIteratorResult(1, false, iter.next()); assertIteratorResult(2, false, iter.next()); assertIteratorResult(undefined, true, iter.next()); })(); (function TestExceptionInName() { function MyError() {}; function throwMyError() { throw new MyError(); } assertThrows(function() { class C { [throwMyError()]() {} } }, MyError); assertThrows(function() { class C { get [throwMyError()]() { return 42; } } }, MyError); assertThrows(function() { class C { set [throwMyError()](_) { } } }, MyError); })(); (function TestTdzName() { assertThrows(function() { class C { [C]() {} } }, ReferenceError); assertThrows(function() { class C { get [C]() { return 42; } } }, ReferenceError); assertThrows(function() { class C { set [C](_) { } } }, ReferenceError); })(); // The following tests deal with computed and statically known properties // of the same name overwriting each other. // // More concretely, we consider the cases where: // The computed property appears ... // - before an existing data property (case 1) // - after an existing data property (case 2) // - before an existing getter and setter pair (case 3) // - before an existing getter xor setter (case 4) // - after an existing getter and setter pair (case 5) // - after an existing getter xor setter (case 6) // - in between two existing accessors (case 7) // // For each of the 7 cases above, there exists A and B variants, indicating // whether the computed property refers to an accessor (variant A) or a // plain property (variant B). // |expect_getter| and |expect_setter| must be undefined if and only if we are // expecting |b| to be a data property. function TestOverwritingHelper(clazz, expect_getter, expect_setter) { var proto = clazz.prototype; var desc = Object.getOwnPropertyDescriptor(proto, 'b'); if (desc.hasOwnProperty('value')) { assertEquals(undefined, expect_getter); assertEquals(undefined, expect_setter); assertEquals('B', proto.b()); } else { assertEquals("boolean", typeof expect_getter); assertEquals("boolean", typeof expect_setter); if (expect_getter) { assertEquals('B', proto.b); } else { assertEquals(undefined, desc.getter); } assertEquals(expect_setter, desc.set !== undefined); } assertEquals('A', proto.a()); assertEquals('C', proto.c()); assertEquals('D', proto.d()); assertArrayEquals([], Object.keys(proto)); assertArrayEquals(['constructor', 'a', 'b', 'c', 'd'], Object.getOwnPropertyNames(proto)); } (function TestOverwriting1A() { class C { a() { return 'A'} get [ID('b')]() { return 'Bx'; } c() { return 'C'; } b() { return 'B'; } d() { return 'D'; } } TestOverwritingHelper(C); })(); (function TestOverwriting1B() { class C { a() { return 'A'} [ID('b')]() { return 'Bx'; } c() { return 'C'; } b() { return 'B'; } d() { return 'D'; } } TestOverwritingHelper(C); })(); (function TestOverwriting2A() { class C { a() { return 'A'} b() { return 'Bx'; } c() { return 'C'; } get [ID('b')]() { return 'B'; } d() { return 'D'; } } TestOverwritingHelper(C, true, false); })(); (function TestOverwriting2B() { class C { a() { return 'A'} b() { return 'Bx'; } c() { return 'C'; } [ID('b')]() { return 'B'; } d() { return 'D'; } } TestOverwritingHelper(C); })(); (function TestOverwriting3A() { class C { a() { return 'A'} get [ID('b')]() { return 'Bx'; } c() { return 'C'; } get b() { return 'B'; } set b(foo) { this.x = foo; } d() { return 'D'; } } TestOverwritingHelper(C, true, true); })(); (function TestOverwriting3B() { class C { a() { return 'A'} [ID('b')]() { return 'Bx'; } c() { return 'C'; } get b() { return 'B'; } set b(foo) { this.x = foo; } d() { return 'D'; } } TestOverwritingHelper(C, true, true); })(); (function TestOverwriting4A() { class C { a() { return 'A'} get [ID('b')]() { return 'Bx'; } c() { return 'C'; } get b() { return 'B'; } d() { return 'D'; } } TestOverwritingHelper(C, true, false); })(); (function TestOverwriting4B() { class C { a() { return 'A'} [ID('b')]() { return 'Bx'; } c() { return 'C'; } get b() { return 'B'; } d() { return 'D'; } } TestOverwritingHelper(C, true, false); })(); (function TestOverwriting5A() { class C { a() { return 'A'} get b() { return 'Bx'; } set b(foo) { this.x = foo; } c() { return 'C'; } get [ID('b')]() { return 'B'; } d() { return 'D'; } } TestOverwritingHelper(C, true, true); })(); (function TestOverwriting5B() { class C { a() { return 'A'} get b() { return 'Bx'; } set b(foo) { this.x = foo; } c() { return 'C'; } [ID('b')]() { return 'B'; } d() { return 'D'; } } TestOverwritingHelper(C); })(); (function TestOverwriting6A() { class C { a() { return 'A'} set b(foo) { this.x = foo; } c() { return 'C'; } get [ID('b')]() { return 'B'; } d() { return 'D'; } } TestOverwritingHelper(C, true, true); })(); (function TestOverwriting6B() { class C { a() { return 'A'} set b(foo) { this.x = foo; } c() { return 'C'; } [ID('b')]() { return 'B'; } d() { return 'D'; } } TestOverwritingHelper(C); })(); (function TestOverwriting7A() { class C { a() { return 'A'} get b() { return 'Bx'; } c() { return 'C'; } get [ID('b')]() { return 'B'; } d() { return 'D'; } set b(foo) { this.x = foo; } } TestOverwritingHelper(C, true, true); })(); (function TestOverwriting7B() { class C { a() { return 'A'} set b(foo) { this.x = foo; } c() { return 'C'; } [ID('b')]() { return 'Bx'; } d() { return 'D'; } get b() { return 'B'; } } TestOverwritingHelper(C, true, false); })();