Title: [270043] trunk
Revision
270043
Author
[email protected]
Date
2020-11-19 12:45:50 -0800 (Thu, 19 Nov 2020)

Log Message

[JSC] Add support for static private class fields
https://bugs.webkit.org/show_bug.cgi?id=214297

Patch by Xan López <[email protected]> on 2020-11-19
Reviewed by Yusuke Suzuki.

JSTests:

Copy V8 tests for static private fields and add the test262
flag. Also add a couple more of our own tests for the sake of
completeness.

* stress/class-fields-static-private-harmony.js: Added, with a couple additional tests.
* stress/resources/harmony-support.js:
(assertDoesNotThrow): added.
* test262/config.yaml:

Source/_javascript_Core:

Static private fields come trivially now that both private and
static (public) fields are implemented.

* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseClass): accept static private fields if the runtime option allows it.
* runtime/Options.cpp:
(JSC::Options::recomputeDependentOptions): usePrivateStaticClassFields depends on usePrivateClassFields.
* runtime/OptionsList.h: add runtime option to enable static private fields.
* tools/JSDollarVM.cpp: add a method to check for private symbols in the stress tests.
(JSC::JSC_DEFINE_HOST_FUNCTION):
(JSC::JSDollarVM::finishCreation):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (270042 => 270043)


--- trunk/JSTests/ChangeLog	2020-11-19 20:00:41 UTC (rev 270042)
+++ trunk/JSTests/ChangeLog	2020-11-19 20:45:50 UTC (rev 270043)
@@ -1,3 +1,19 @@
+2020-11-19  Xan López  <[email protected]>
+
+        [JSC] Add support for static private class fields
+        https://bugs.webkit.org/show_bug.cgi?id=214297
+
+        Reviewed by Yusuke Suzuki.
+
+        Copy V8 tests for static private fields and add the test262
+        flag. Also add a couple more of our own tests for the sake of
+        completeness.
+
+        * stress/class-fields-static-private-harmony.js: Added, with a couple additional tests.
+        * stress/resources/harmony-support.js:
+        (assertDoesNotThrow): added.
+        * test262/config.yaml:
+
 2020-11-18  Dmitry Bezhetskov  <[email protected]>
 
         [WASM-References] Remove subtyping rule for externref and funcref

Modified: trunk/JSTests/stress/class-fields-private-harmony.js (270042 => 270043)


--- trunk/JSTests/stress/class-fields-private-harmony.js	2020-11-19 20:00:41 UTC (rev 270042)
+++ trunk/JSTests/stress/class-fields-private-harmony.js	2020-11-19 20:45:50 UTC (rev 270043)
@@ -361,8 +361,6 @@
   assertThrows(() => new C, TypeError);
 }
 
-/*
-FIXME: we don't have %SymbolIsPrivate()
 {
   let symbol = Symbol();
 
@@ -375,8 +373,8 @@
 
   var p = new Proxy(new C, {
     get: function(target, name) {
-      if (typeof(arg) === 'symbol') {
-        assertFalse(%SymbolIsPrivate(name));
+      if (typeof(name) === 'symbol') {
+        assertFalse($vm.isPrivateSymbol(name));
       }
       return target[name];
     }
@@ -386,7 +384,6 @@
   assertThrows(() => p.setA(1), TypeError);
   assertEquals(1, p[symbol]);
 }
-*/
 
 {
   class C {

Added: trunk/JSTests/stress/class-fields-static-private-harmony.js (0 => 270043)


--- trunk/JSTests/stress/class-fields-static-private-harmony.js	                        (rev 0)
+++ trunk/JSTests/stress/class-fields-static-private-harmony.js	2020-11-19 20:45:50 UTC (rev 270043)
@@ -0,0 +1,418 @@
+//@ requireOptions("--usePrivateStaticClassFields=1")
+//@ defaultNoEagerRun
+
+// Copyright 2018 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 of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"use strict";
+
+load("./resources/harmony-support.js");
+
+{
+  class C {
+    static #a;
+    static getA() { return this.#a; }
+  }
+
+  assertEquals(undefined, C.a);
+  assertEquals(undefined, C.getA());
+
+  let c = new C;
+  assertEquals(undefined, c.a);
+}
+
+{
+  class C {
+    static #a = 1;
+    static getA() { return this.#a; }
+  }
+
+  assertEquals(undefined, C.a);
+  assertEquals(1, C.getA());
+
+  let c = new C;
+  assertEquals(undefined, c.a);
+}
+
+{
+  class C {
+    static #a = 1;
+    static #b = this.#a;
+    static getB() { return this.#b; }
+  }
+
+  assertEquals(1, C.getB());
+
+  let c = new C;
+  assertEquals(undefined, c.getB);
+}
+
+{
+  class C {
+    static #a = 1;
+    static getA() { return this.#a; }
+    constructor() {
+      assertThrows(() => this.#a, TypeError);
+      C.#a = 2;
+    }
+  }
+
+  assertEquals(1, C.getA());
+
+  let c = new C;
+  assertThrows(() => C.prototype.getA.call(c));
+  assertEquals(2, C.getA());
+}
+
+{
+  class C {
+    static #a = this;
+    static #b = () => this;
+    static getA() { return this.#a; }
+    static getB() { return this.#b; }
+  }
+
+  assertSame(C, C.getA());
+  assertSame(C, C.getB()());
+}
+
+{
+  class C {
+    static #a = this;
+    static #b = function() { return this; };
+    static getA() { return this.#a; }
+    static getB() { return this.#b; }
+  }
+
+  assertSame(C, C.getA());
+  assertSame(C, C.getB().call(C));
+  assertSame(undefined, C.getB()());
+}
+
+
+{
+  class C {
+    static #a = function() { return 1 };
+    static getA() {return this.#a;}
+  }
+
+  assertEquals('#a', C.getA().name);
+}
+
+{
+  let d = function() { return new.target; }
+  class C {
+    static #c = d;
+    static getC() { return this.#c; }
+  }
+
+  assertEquals(undefined, C.getC()());
+  assertSame(new d, new (C.getC()));
+}
+
+{
+  class C {
+    static #a = 1;
+    static getA(instance) { return instance.#a; }
+  }
+
+  class B { }
+
+  assertEquals(undefined, C.a);
+  assertEquals(1, C.getA(C));
+  assertThrows(() => C.getA(B), TypeError);
+}
+
+{
+  class A {
+    static #a = 1;
+    static getA() { return this.#a; }
+  }
+
+  class B extends A {}
+  assertThrows(() => B.getA(), TypeError);
+}
+
+{
+  class A {
+    static #a = 1;
+    static getA() { return A.#a; }
+  }
+
+  class B extends A {}
+  assertSame(1, B.getA());
+}
+
+{
+  let prototypeLookup = false;
+  class A {
+    static set a(val) {
+      prototypeLookup = true;
+    }
+
+    static get a() { return undefined; }
+  }
+
+  class C extends A {
+    static #a = 1;
+    static getA() { return this.#a; }
+  }
+
+  assertEquals(1, C.getA());
+  assertEquals(false, prototypeLookup);
+}
+
+{
+  class A {
+    static a = 1;
+  }
+
+  class B extends A {
+    static #b = this.a;
+    static getB() { return this.#b; }
+  }
+
+  assertEquals(1, B.getB());
+}
+
+{
+  class A {
+    static #a = 1;
+    static getA() { return this.#a; }
+  }
+
+  class B extends A {
+    static getA() { return super.getA(); }
+  }
+
+  assertThrows(() => B.getA(), TypeError);
+}
+
+{
+  class A {
+    static #a = 1;
+    static getA() { return this.#a;}
+  }
+
+  class B extends A {
+    static #a = 2;
+    static get_A() { return this.#a;}
+  }
+
+  assertEquals(1, A.getA());
+  assertThrows(() => B.getA(), TypeError);
+  assertEquals(2, B.get_A());
+}
+
+{
+  let foo = undefined;
+  class A {
+    static #a = (function() { foo = 1; })();
+  }
+
+  assertEquals(1, foo);
+}
+
+{
+  let foo = undefined;
+  class A extends class {} {
+    static #a = (function() { foo = 1; })();
+  }
+
+  assertEquals(1, foo);
+}
+
+{
+  function makeClass() {
+    return class {
+      static #a;
+      static setA(val) { this.#a = val; }
+      static getA() { return this.#a; }
+    }
+  }
+
+  let classA = makeClass();
+  let classB = makeClass();
+
+  assertEquals(undefined, classA.getA());
+  assertEquals(undefined, classB.getA());
+
+  classA.setA(3);
+  assertEquals(3, classA.getA());
+  assertEquals(undefined, classB.getA());
+
+  classB.setA(5);
+  assertEquals(3, classA.getA());
+  assertEquals(5, classB.getA());
+
+  assertThrows(() => classA.getA.call(classB), TypeError);
+  assertThrows(() => classB.getA.call(classA), TypeError);
+}
+
+{
+  let value = undefined;
+
+  new class {
+    static #a = 1;
+    static getA() { return this.#a; }
+
+    constructor() {
+      new class C {
+        static #a = 2;
+        constructor() {
+          value = C.#a;
+        }
+      }
+    }
+  }
+
+  assertEquals(2, value);
+}
+
+{
+  class A {
+    static #a = 1;
+    static b = class {
+      static getA() { return this.#a; }
+      static get_A(val) { return val.#a; }
+    }
+  }
+
+  assertEquals(1, A.b.getA.call(A));
+  assertEquals(1, A.b.get_A(A));
+}
+
+{
+  assertThrows(() => class { static b = this.#a; static #a = 1 }, TypeError);
+}
+
+{
+  let symbol = Symbol();
+
+  class C {
+    static #a = 1;
+    static [symbol] = 1;
+    static getA() { return this.#a; }
+    static setA(val) { this.#a = val; }
+  }
+
+  var p = new Proxy(C, {
+    get: function(target, name) {
+      if (typeof(name) === 'symbol') {
+        assertFalse($vm.isPrivateSymbol(name));
+      }
+      return target[name];
+    }
+  });
+
+  assertThrows(() => p.getA(), TypeError);
+  assertThrows(() => p.setA(1), TypeError);
+  assertEquals(1, p[symbol]);
+}
+
+{
+  class C {
+    static #b = Object.freeze(this);
+    static getA() { return this.#a; }
+    static #a = 1;
+  }
+
+  assertEquals(1, C.getA());
+}
+
+{
+  class C {
+    static #a = 1;
+    static getA() { return eval('this.#a'); }
+  }
+
+  assertEquals(1, C.getA());
+}
+
+{
+  var C;
+  eval('C = class { static #a = 1; static getA() { return eval(\'this.#a\'); }}');
+
+  assertEquals(1, C.getA());
+}
+
+{
+  class C {
+    static #a = 1;
+    static getA() { return this.#a; }
+    static setA() { eval('this.#a = 4'); }
+  }
+
+  assertEquals(1, C.getA());
+  C.setA();
+  assertEquals(4, C.getA());
+}
+
+{
+  class C {
+    static getA() { return eval('this.#a'); }
+  }
+
+  assertThrows(() => C.getA(), SyntaxError);
+}
+
+// Additional tests by the WebKit project.
+function shouldThrowSyntaxError(script) {
+    let error;
+    try {
+        eval(script);
+    } catch (e) {
+        error = e;
+    }
+
+    if (!(error instanceof SyntaxError))
+        throw new Error('Expected SyntaxError!');
+}
+
+shouldThrowSyntaxError("class C { #foo = 0; static #foo = 1; }");
+shouldThrowSyntaxError("class C { static #foo = 0; #foo = 1; }");
+
+{
+  class C {
+    static #foo = 0;
+    testAccess(X) { return X.#foo; }
+  }
+
+  class D {
+    static #foo = 0;
+  }
+
+  let c = new C();
+  assertDoesNotThrow(c.testAccess(C));
+  assertThrows(() => c.testAccess(D), TypeError);
+}

Modified: trunk/JSTests/stress/resources/harmony-support.js (270042 => 270043)


--- trunk/JSTests/stress/resources/harmony-support.js	2020-11-19 20:00:41 UTC (rev 270042)
+++ trunk/JSTests/stress/resources/harmony-support.js	2020-11-19 20:45:50 UTC (rev 270043)
@@ -194,3 +194,18 @@
       typeof type_opt == 'function' ? type_opt : 'any exception';
   fail(expected, 'no exception', 'expected thrown exception');
 }
+
+/**
+ * Runs code() and asserts that it does not throws an exception.
+ */
+function assertDoesNotThrow(code, name_opt) {
+  try {
+    if (typeof code == 'function') {
+      code();
+    } else {
+      eval(code);
+    }
+  } catch (e) {
+    fail("no exception", "threw an exception: " + (e.message || e));
+  }
+}

Modified: trunk/JSTests/test262/config.yaml (270042 => 270043)


--- trunk/JSTests/test262/config.yaml	2020-11-19 20:00:41 UTC (rev 270042)
+++ trunk/JSTests/test262/config.yaml	2020-11-19 20:45:50 UTC (rev 270043)
@@ -5,6 +5,7 @@
   FinalizationRegistry: useWeakRefs
   class-fields-private: usePrivateClassFields
   class-static-fields-public: usePublicStaticClassFields
+  class-static-fields-private: usePrivateStaticClassFields
   Intl.DateTimeFormat-dayPeriod: useIntlDateTimeFormatDayPeriod
   SharedArrayBuffer: useSharedArrayBuffer
   Atomics: useSharedArrayBuffer
@@ -20,7 +21,6 @@
 
     - arbitrary-module-namespace-names
     - class-methods-private
-    - class-static-fields-private
     - class-static-methods-private
     - cleanupSome
     - host-gc-required

Modified: trunk/Source/_javascript_Core/ChangeLog (270042 => 270043)


--- trunk/Source/_javascript_Core/ChangeLog	2020-11-19 20:00:41 UTC (rev 270042)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-11-19 20:45:50 UTC (rev 270043)
@@ -1,3 +1,22 @@
+2020-11-19  Xan López  <[email protected]>
+
+        [JSC] Add support for static private class fields
+        https://bugs.webkit.org/show_bug.cgi?id=214297
+
+        Reviewed by Yusuke Suzuki.
+
+        Static private fields come trivially now that both private and
+        static (public) fields are implemented.
+
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseClass): accept static private fields if the runtime option allows it.
+        * runtime/Options.cpp:
+        (JSC::Options::recomputeDependentOptions): usePrivateStaticClassFields depends on usePrivateClassFields.
+        * runtime/OptionsList.h: add runtime option to enable static private fields.
+        * tools/JSDollarVM.cpp: add a method to check for private symbols in the stress tests.
+        (JSC::JSC_DEFINE_HOST_FUNCTION):
+        (JSC::JSDollarVM::finishCreation):
+
 2020-11-19  Adrian Perez de Castro  <[email protected]>
 
         [JSC] Build failed due to unknown values in LLIntDesiredOffsets.h

Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (270042 => 270043)


--- trunk/Source/_javascript_Core/parser/Parser.cpp	2020-11-19 20:00:41 UTC (rev 270042)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp	2020-11-19 20:45:50 UTC (rev 270043)
@@ -2971,7 +2971,8 @@
             ASSERT(Options::usePrivateClassFields());
             JSToken token = m_token;
             ident = m_token.m_data.ident;
-            failIfTrue(tag == ClassElementTag::Static, "Static class element cannot be private");
+            if (!Options::usePrivateStaticClassFields())
+                failIfTrue(tag == ClassElementTag::Static, "Static class element cannot be private");
             failIfTrue(isGetter || isSetter, "Cannot parse class method with private name");
             ASSERT(ident);
             next();

Modified: trunk/Source/_javascript_Core/runtime/Options.cpp (270042 => 270043)


--- trunk/Source/_javascript_Core/runtime/Options.cpp	2020-11-19 20:00:41 UTC (rev 270042)
+++ trunk/Source/_javascript_Core/runtime/Options.cpp	2020-11-19 20:45:50 UTC (rev 270043)
@@ -551,6 +551,9 @@
 
         FOR_EACH_JSC_EXPERIMENTAL_OPTION(DISABLE_TIERS);
     }
+
+    if (Options::usePrivateStaticClassFields())
+        Options::usePrivateClassFields() = true;
 }
 
 inline void* Options::addressOfOption(Options::ID id)

Modified: trunk/Source/_javascript_Core/runtime/OptionsList.h (270042 => 270043)


--- trunk/Source/_javascript_Core/runtime/OptionsList.h	2020-11-19 20:00:41 UTC (rev 270042)
+++ trunk/Source/_javascript_Core/runtime/OptionsList.h	2020-11-19 20:45:50 UTC (rev 270043)
@@ -525,6 +525,7 @@
     v(Bool, exposeCustomSettersOnGlobalObjectForTesting, false, Normal, nullptr) \
     v(Bool, useJITCage, canUseJITCage(), Normal, nullptr) \
     v(Bool, usePublicStaticClassFields, true, Normal, "If true, the parser will understand public static data fields inside classes.") \
+    v(Bool, usePrivateStaticClassFields, false, Normal, "If true, the parser will understand private static data fields inside classes.") \
 
 enum OptionEquivalence {
     SameOption,

Modified: trunk/Source/_javascript_Core/tools/JSDollarVM.cpp (270042 => 270043)


--- trunk/Source/_javascript_Core/tools/JSDollarVM.cpp	2020-11-19 20:00:41 UTC (rev 270042)
+++ trunk/Source/_javascript_Core/tools/JSDollarVM.cpp	2020-11-19 20:45:50 UTC (rev 270043)
@@ -1869,6 +1869,7 @@
 static JSC_DECLARE_HOST_FUNCTION(functionUseJIT);
 static JSC_DECLARE_HOST_FUNCTION(functionIsGigacageEnabled);
 static JSC_DECLARE_HOST_FUNCTION(functionToUncacheableDictionary);
+static JSC_DECLARE_HOST_FUNCTION(functionIsPrivateSymbol);
 
 const ClassInfo JSDollarVM::s_info = { "DollarVM", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDollarVM) };
 
@@ -3377,6 +3378,16 @@
     return JSValue::encode(object);
 }
 
+JSC_DEFINE_HOST_FUNCTION(functionIsPrivateSymbol, (JSGlobalObject*, CallFrame* callFrame))
+{
+    DollarVMAssertScope assertScope;
+
+    if (!(callFrame->argument(0).isSymbol()))
+        return JSValue::encode(jsBoolean(false));
+
+    return JSValue::encode(jsBoolean(asSymbol(callFrame->argument(0))->uid().isPrivate()));
+}
+
 constexpr unsigned jsDollarVMPropertyAttributes = PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete;
 
 void JSDollarVM::finishCreation(VM& vm)
@@ -3527,6 +3538,8 @@
 
     addFunction(vm, "toUncacheableDictionary", functionToUncacheableDictionary, 1);
 
+    addFunction(vm, "isPrivateSymbol", functionIsPrivateSymbol, 1);
+
     m_objectDoingSideEffectPutWithoutCorrectSlotStatusStructure.set(vm, this, ObjectDoingSideEffectPutWithoutCorrectSlotStatus::createStructure(vm, globalObject, jsNull()));
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to