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()));
}