v8/test/mjsunit/harmony/public-static-class-fields.js
Sathya Gunasekaran bc324dbd9b [class] Fix class field name initialization
Previously when class names were computed and set as part of
StoreDataPropertyInLiteral calls, it was observable to static fields
as these static fields are initialized right after the classes were
constructed but before the class names were installed.
This caused the name property to be undefined for this case.

Instead, this patch always forces the creation of a name property on
the class constructor when static class fields are used. This patch
does kill the class boilerplate optimization, but currently all static
class fields are installed using a runtime call to CreateDataProperty
so this isn't any worse when using static class fields.

In the future, this can be optimized away by storing the name on the
boilerplate.

There is spec discussion here:
https://github.com/tc39/proposal-class-fields/issues/85

There isn't a resolution yet, there's still discussion about whether
to have the name be undefined always for static class field
initializers. But, I don't think that's useful as it would always kill
our boilerplate optimization (like this patch does ..., but without the
future optimization potential).

Bug: v8:5367
Change-Id: I14afdf7ece3f2d9fa3c659d2c0bc3806e0b17abb
Reviewed-on: https://chromium-review.googlesource.com/c/1281002
Reviewed-by: Mythri Alle <mythria@chromium.org>
Reviewed-by: Daniel Ehrenberg <littledan@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56686}
2018-10-16 11:02:21 +00:00

473 lines
7.1 KiB
JavaScript

// Copyright 2017 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: --harmony-public-fields --harmony-static-fields
"use strict";
{
class C {
static a;
}
assertEquals(undefined, C.a);
let descriptor = Object.getOwnPropertyDescriptor(C, 'a');
assertTrue(C.hasOwnProperty('a'));
assertTrue(descriptor.writable);
assertTrue(descriptor.enumerable);
assertTrue(descriptor.configurable);
let c = new C;
assertEquals(undefined, c.a);
}
{
let x = 'a';
class C {
static a;
static hasOwnProperty = function() { return 1; }
static b = x;
static c = 1;
}
assertEquals(undefined, C.a);
assertEquals('a', C.b);
assertEquals(1, C.c);
assertEquals(1, C.hasOwnProperty());
let c = new C;
assertEquals(undefined, c.a);
assertEquals(undefined, c.b);
assertEquals(undefined, c.c);
}
{
assertThrows(() => {
class C {
static x = Object.freeze(this);
static c = 42;
}
}, TypeError);
}
{
class C {
static c = this;
static d = () => this;
}
assertEquals(C, C.c);
assertEquals(C, C.d());
let c = new C;
assertEquals(undefined, c.c);
assertEquals(undefined, c.d);
}
{
class C {
static c = 1;
static d = this.c;
}
assertEquals(1, C.c);
assertEquals(1, C.d);
let c = new C;
assertEquals(undefined, c.c);
assertEquals(undefined, c.d);
}
{
class C {
static b = 1;
static c = () => this.b;
}
assertEquals(1, C.b);
assertEquals(1, C.c());
let c = new C;
assertEquals(undefined, c.c);
}
{
let x = 'a';
class C {
static b = 1;
static c = () => this.b;
static e = () => x;
}
assertEquals(1, C.b);
assertEquals('a', C.e());
let a = {b : 2 };
assertEquals(1, C.c.call(a));
let c = new C;
assertEquals(undefined, c.b);
assertEquals(undefined, c.c);
}
{
let x = 'a';
class C {
static c = 1;
static d = function() { return this.c; };
static e = function() { return x; };
}
assertEquals(1, C.c);
assertEquals(1, C.d());
assertEquals('a', C.e());
C.c = 2;
assertEquals(2, C.d());
let a = {c : 3 };
assertEquals(3, C.d.call(a));
assertThrows(C.d.bind(undefined));
let c = new C;
assertEquals(undefined, c.c);
assertEquals(undefined, c.d);
assertEquals(undefined, c.e);
}
{
class C {
static c = function() { return 1 };
}
assertEquals('c', C.c.name);
}
{
let d = function() { return new.target; }
class C {
static c = d;
}
assertEquals(undefined, C.c());
assertEquals(new d, new C.c());
}
{
class C {
static c = () => new.target;
}
assertEquals(undefined, C.c());
}
{
class C {
static c = () => {
let b;
class A {
constructor() {
b = new.target;
}
};
new A;
assertEquals(A, b);
}
}
C.c();
}
{
class C {
static c = new.target;
}
assertEquals(undefined, C.c);
}
{
class B {
static d = 1;
static b = () => this.d;
}
class C extends B {
static c = super.d;
static d = () => super.d;
static e = () => super.b();
}
assertEquals(1, C.c);
assertEquals(1, C.d());
assertEquals(1, C.e());
}
{
let foo = undefined;
class B {
static set d(x) {
foo = x;
}
}
class C extends B {
static d = 2;
}
assertEquals(undefined, foo);
assertEquals(2, C.d);
}
{
let C = class {
static c;
};
assertEquals("C", C.name);
}
{
class C {
static c = new C;
}
assertTrue(C.c instanceof C);
}
(function test() {
function makeC() {
var x = 1;
return class {
static a = () => () => x;
}
}
let C = makeC();
let f = C.a();
assertEquals(1, f());
})()
{
let c = "c";
class C {
static ["a"] = 1;
static ["b"];
static [c];
}
assertEquals(1, C.a);
assertEquals(undefined, C.b);
assertEquals(undefined, C[c]);
}
{
let log = [];
function run(i) {
log.push(i);
return i;
}
class C {
static [run(1)] = run(6);
static [run(2)] = run(7);
[run(3)]() { run(9);}
static [run(4)] = run(8);
static [run(5)]() { throw new Error('should not execute');};
}
let c = new C;
c[3]();
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
}
function x() {
// This tests lazy parsing.
return function() {
let log = [];
function run(i) {
log.push(i);
return i;
}
class C {
static [run(1)] = run(6);
static [run(2)] = run(7);
[run(3)]() { run(9);}
static [run(4)] = run(8);
static [run(5)]() { throw new Error('should not execute');};
}
let c = new C;
c[3]();
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
}
}
x()();
{
let log = [];
function run(i) {
log.push(i);
return i;
}
class C {
[run(1)] = run(7);
[run(2)] = run(8);
[run(3)]() { run(9);}
static [run(4)] = run(6);
[run(5)]() { throw new Error('should not execute');};
}
let c = new C;
c[3]();
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
}
function y() {
// This tests lazy parsing.
return function() {
let log = [];
function run(i) {
log.push(i);
return i;
}
class C {
[run(1)] = run(7);
[run(2)] = run(8);
[run(3)]() { run(9);}
static [run(4)] = run(6);
[run(5)]() { throw new Error('should not execute');};
}
let c = new C;
c[3]();
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
}
}
y()();
{
class C {}
class D {
static [C];
}
assertThrows(() => { class X { static [X] } });
assertEquals(undefined, D[C]);
}
{
function t() {
return class {
static ['x'] = 2;
}
}
let klass = t();
let obj = new klass;
assertEquals(2, klass.x);
}
{
let x = 'a';
class C {
a;
b = x;
c = 1;
hasOwnProperty() { return 1;}
static [x] = 2;
static b = 3;
static d;
}
assertEquals(2, C.a);
assertEquals(3, C.b);
assertEquals(undefined, C.d);
assertEquals(undefined, C.c);
let c = new C;
assertEquals(undefined, c.a);
assertEquals('a', c.b);
assertEquals(1, c.c);
assertEquals(undefined, c.d);
assertEquals(1, c.hasOwnProperty());
}
{
function t() {
return class {
['x'] = 1;
static ['x'] = 2;
}
}
let klass = t();
let obj = new klass;
assertEquals(1, obj.x);
assertEquals(2, klass.x);
}
{
class X {
static p = function() { return arguments[0]; }
}
assertEquals(1, X.p(1));
}
{
class X {
static t = () => {
function p() { return arguments[0]; };
return p;
}
}
let p = X.t();
assertEquals(1, p(1));
}
{
class X {
static t = () => {
function p() { return eval("arguments[0]"); };
return p;
}
}
let p = X.t();
assertEquals(1, p(1));
}
{
class X {
static p = eval("(function() { return arguments[0]; })(1)");
}
assertEquals(1, X.p);
}
{
let p = { z: class { static y = this.name } }
assertEquals(p.z.y, 'z');
let q = { ["z"]: class { static y = this.name } }
assertEquals(q.z.y, 'z');
const C = class {
static x = this.name;
}
assertEquals(C.x, 'C');
}