Title: [269801] trunk
Revision
269801
Author
[email protected]
Date
2020-11-13 14:32:01 -0800 (Fri, 13 Nov 2020)

Log Message

[JSC] Use symbols as identifiers for class fields computed names storage
https://bugs.webkit.org/show_bug.cgi?id=216172

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

Use private symbols for the property keys of the class fields with
computed names. This is cleaner than using raw numeric identifiers and
will be less cumbersome when we add static fields. It also prevents
potential collisions if other features want to store data in the class
scope.

JSTests:

* stress/class-fields-harmony.js: new test, make sure
setFunctionName works properly with computed fields.

Source/_javascript_Core:

* bytecompiler/NodesCodegen.cpp:
(JSC::PropertyListNode::emitSaveComputedFieldName): adapt a comment.
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseClass): use private identifiers for computed fields property keys.
(JSC::Parser<LexerType>::parseInstanceFieldInitializerSourceElements): ditto.
* parser/ParserArena.cpp:
(JSC::IdentifierArena::makePrivateIdentifier): method to create a private identifier.
* parser/ParserArena.h:
* runtime/CachedTypes.cpp:
(JSC::CachedUniquedStringImplBase::encode): consider registered symbols, they are used by the parser now.
(JSC::CachedUniquedStringImplBase::decode const): ditto.
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
(JSC::VM::privateSymbolRegistry): create a private symbol registry too.

Source/WTF:

* wtf/text/SymbolImpl.cpp:
(WTF::RegisteredSymbolImpl::createPrivate): add a method to create a registered private symbol from a string key.
* wtf/text/SymbolImpl.h:
* wtf/text/SymbolRegistry.cpp:
(WTF::SymbolRegistry::symbolForKey): consider that we can hold private symbols now too.
* wtf/text/SymbolRegistry.h:
(WTF::SymbolRegistry::SymbolRegistry): new enum type for public/private symbols.

Modified Paths

Diff

Modified: trunk/JSTests/ChangeLog (269800 => 269801)


--- trunk/JSTests/ChangeLog	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/JSTests/ChangeLog	2020-11-13 22:32:01 UTC (rev 269801)
@@ -1,3 +1,19 @@
+2020-11-13  Xan López  <[email protected]>
+
+        [JSC] Use symbols as identifiers for class fields computed names storage
+        https://bugs.webkit.org/show_bug.cgi?id=216172
+
+        Reviewed by Yusuke Suzuki.
+
+        Use private symbols for the property keys of the class fields with
+        computed names. This is cleaner than using raw numeric identifiers and
+        will be less cumbersome when we add static fields. It also prevents
+        potential collisions if other features want to store data in the class
+        scope.
+
+        * stress/class-fields-harmony.js: new test, make sure
+        setFunctionName works properly with computed fields.
+
 2020-11-13  Yusuke Suzuki  <[email protected]>
 
         Unreviewed, skip new ICU related tests in MIPS and ARMv7 and adjust it for ICU 65

Modified: trunk/JSTests/stress/class-fields-harmony.js (269800 => 269801)


--- trunk/JSTests/stress/class-fields-harmony.js	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/JSTests/stress/class-fields-harmony.js	2020-11-13 22:32:01 UTC (rev 269801)
@@ -906,3 +906,24 @@
   assertSame(thisInConstructor, c);
 }
 
+// Additional tests by the WebKit project.
+
+{
+  let x = 0;
+  let y = 'foo';
+  let z = { name: 'test' };
+
+  let C = class {
+    [x] = () => {
+      return 2;
+    };
+    [y] = class {};
+    [z] = class D {};
+  }
+
+  let c = new C();
+  assertSame(c[x](), 2);
+  assertSame(c[x].name, '0');
+  assertSame(c[y].name, 'foo');
+  assertSame(c[z].name, 'D');
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (269800 => 269801)


--- trunk/Source/_javascript_Core/ChangeLog	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-11-13 22:32:01 UTC (rev 269801)
@@ -1,3 +1,32 @@
+2020-11-13  Xan López  <[email protected]>
+
+        [JSC] Use symbols as identifiers for class fields computed names storage
+        https://bugs.webkit.org/show_bug.cgi?id=216172
+
+        Reviewed by Yusuke Suzuki.
+
+        Use private symbols for the property keys of the class fields with
+        computed names. This is cleaner than using raw numeric identifiers and
+        will be less cumbersome when we add static fields. It also prevents
+        potential collisions if other features want to store data in the class
+        scope.
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::PropertyListNode::emitSaveComputedFieldName): adapt a comment.
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseClass): use private identifiers for computed fields property keys.
+        (JSC::Parser<LexerType>::parseInstanceFieldInitializerSourceElements): ditto.
+        * parser/ParserArena.cpp:
+        (JSC::IdentifierArena::makePrivateIdentifier): method to create a private identifier.
+        * parser/ParserArena.h:
+        * runtime/CachedTypes.cpp:
+        (JSC::CachedUniquedStringImplBase::encode): consider registered symbols, they are used by the parser now.
+        (JSC::CachedUniquedStringImplBase::decode const): ditto.
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+        (JSC::VM::privateSymbolRegistry): create a private symbol registry too.
+
 2020-11-13  Sergey Rubanov  <[email protected]>
 
         WebAssembly: opcodes for table.grow and table.size are mixed up

Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (269800 => 269801)


--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2020-11-13 22:32:01 UTC (rev 269801)
@@ -802,7 +802,7 @@
     ASSERT(node.isComputedClassField());
     RefPtr<RegisterID> propertyExpr;
 
-    // The 'name' refers to a synthetic numeric variable name in the private name scope, where the property key is saved for later use.
+    // The 'name' refers to a synthetic private name in the class scope, where the property key is saved for later use.
     const Identifier& description = *node.name();
     Variable var = generator.variable(description);
     ASSERT(!var.local());
@@ -4817,12 +4817,14 @@
 void DefineFieldNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 {
     RefPtr<RegisterID> value = generator.newTemporary();
+    bool shouldSetFunctionName = false;
 
     if (!m_assign)
         generator.emitLoad(value.get(), jsUndefined());
     else {
         generator.emitNode(value.get(), m_assign);
-        if (m_ident && generator.shouldSetFunctionName(m_assign))
+        shouldSetFunctionName = generator.shouldSetFunctionName(m_assign);
+        if (m_ident && shouldSetFunctionName && m_type != DefineFieldNode::Type::ComputedName)
             generator.emitSetFunctionName(value.get(), *m_ident);
     }
 
@@ -4850,7 +4852,7 @@
         // https://bugs.webkit.org/show_bug.cgi?id=198330
 
         // For ComputedNames, the _expression_ has already been evaluated earlier during evaluation of a ClassExprNode.
-        // Here, `m_ident` refers to an integer ID in a class lexical scope, containing the value already converted to an _expression_.
+        // Here, `m_ident` refers to private symbol ID in a class lexical scope, containing the value already converted to an _expression_.
         Variable var = generator.variable(*m_ident);
         ASSERT_WITH_MESSAGE(!var.local(), "Computed names must be stored in captured variables");
 
@@ -4858,6 +4860,8 @@
         RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
         RefPtr<RegisterID> privateName = generator.newTemporary();
         generator.emitGetFromScope(privateName.get(), scope.get(), var, ThrowIfNotFound);
+        if (shouldSetFunctionName)
+            generator.emitSetFunctionName(value.get(), privateName.get());
         generator.emitProfileType(privateName.get(), var, m_position, m_position + m_ident->length());
         generator.emitCallDefineProperty(generator.thisRegister(), privateName.get(), value.get(), nullptr, nullptr, BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable | BytecodeGenerator::PropertyEnumerable, m_position);
         break;

Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (269800 => 269801)


--- trunk/Source/_javascript_Core/parser/Parser.cpp	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp	2020-11-13 22:32:01 UTC (rev 269801)
@@ -2837,6 +2837,8 @@
     return context.createClassDeclStatement(location, classExpr, classStart, classEnd, classStartLine, classEndLine);
 }
 
+static constexpr ASCIILiteral instanceComputedNamePrefix { "instanceComputedName"_s };
+
 template <typename LexerType>
 template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(TreeBuilder& context, FunctionNameRequirements requirements, ParserClassInfo<TreeBuilder>& info)
 {
@@ -2996,7 +2998,7 @@
             }
 
             if (computedPropertyName) {
-                ident = &m_parserArena.identifierArena().makeNumericIdentifier(m_vm, numComputedFields++);
+                ident = &m_parserArena.identifierArena().makePrivateIdentifier(m_vm, instanceComputedNamePrefix, numComputedFields++);
                 DeclarationResultMask declarationResult = classScope->declareLexicalVariable(ident, true);
                 ASSERT_UNUSED(declarationResult, declarationResult == DeclarationResult::Valid);
                 classScope->useVariable(ident, false);
@@ -3077,7 +3079,6 @@
 
         JSTokenLocation fieldLocation = tokenLocation();
         const Identifier* ident = nullptr;
-        TreeExpression computedPropertyName = 0;
         DefineFieldNode::Type type = DefineFieldNode::Type::Name;
         switch (m_token.m_type) {
         case PRIVATENAME:
@@ -3101,14 +3102,15 @@
             ASSERT(ident);
             next();
             break;
-        case OPENBRACKET:
+        case OPENBRACKET: {
             next();
-            computedPropertyName = parseAssignmentExpression(context);
+            TreeExpression computedPropertyName = parseAssignmentExpression(context);
             failIfFalse(computedPropertyName, "Cannot parse computed property name");
             handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name");
-            ident = &m_parserArena.identifierArena().makeNumericIdentifier(m_vm, numComputedFields++);
+            ident = &m_parserArena.identifierArena().makePrivateIdentifier(m_vm, instanceComputedNamePrefix, numComputedFields++);
             type = DefineFieldNode::Type::ComputedName;
             break;
+        }
         default:
             if (m_token.m_type & KeywordTokenFlag)
                 goto namedKeyword;

Modified: trunk/Source/_javascript_Core/parser/ParserArena.cpp (269800 => 269801)


--- trunk/Source/_javascript_Core/parser/ParserArena.cpp	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/_javascript_Core/parser/ParserArena.cpp	2020-11-13 22:32:01 UTC (rev 269801)
@@ -107,4 +107,12 @@
     return m_identifiers.last();
 }
 
+const Identifier& IdentifierArena::makePrivateIdentifier(VM& vm, ASCIILiteral prefix, unsigned identifier)
+{
+    String symbolName = makeString(prefix, identifier);
+    auto symbol = vm.privateSymbolRegistry().symbolForKey(symbolName);
+    m_identifiers.append(Identifier::fromUid(symbol));
+    return m_identifiers.last();
 }
+
+}

Modified: trunk/Source/_javascript_Core/parser/ParserArena.h (269800 => 269801)


--- trunk/Source/_javascript_Core/parser/ParserArena.h	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/_javascript_Core/parser/ParserArena.h	2020-11-13 22:32:01 UTC (rev 269801)
@@ -52,6 +52,7 @@
 
         const Identifier& makeBigIntDecimalIdentifier(VM&, const Identifier&, uint8_t radix);
         const Identifier& makeNumericIdentifier(VM&, double number);
+        const Identifier& makePrivateIdentifier(VM&, ASCIILiteral, unsigned);
 
     public:
         static const int MaximumCachableCharacter = 128;

Modified: trunk/Source/_javascript_Core/runtime/CachedTypes.cpp (269800 => 269801)


--- trunk/Source/_javascript_Core/runtime/CachedTypes.cpp	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/_javascript_Core/runtime/CachedTypes.cpp	2020-11-13 22:32:01 UTC (rev 269801)
@@ -702,14 +702,18 @@
     {
         m_isAtomic = string.isAtom();
         m_isSymbol = string.isSymbol();
+        m_isRegistered = false;
         m_isWellKnownSymbol = false;
+        m_isPrivate = false;
         RefPtr<StringImpl> impl = const_cast<StringImpl*>(&string);
 
         if (m_isSymbol) {
             SymbolImpl* symbol = static_cast<SymbolImpl*>(impl.get());
+            m_isRegistered = symbol->isRegistered();
+            m_isPrivate = symbol->isPrivate();
             if (!symbol->isNullSymbol()) {
                 // We have special handling for well-known symbols.
-                if (!symbol->isPrivate()) {
+                if (!m_isPrivate) {
                     m_isWellKnownSymbol = true;
                     impl = symbol->substring(strlen("Symbol."));
                 }
@@ -742,10 +746,17 @@
                 return AtomStringImpl::add(buffer, m_length).leakRef();
 
             SymbolImpl* symbol;
-            if (m_isWellKnownSymbol)
-                symbol = decoder.vm().propertyNames->builtinNames().lookUpWellKnownSymbol(buffer, m_length);
+            VM& vm = decoder.vm();
+            if (m_isRegistered) {
+                String str(buffer, m_length);
+                if (m_isPrivate)
+                    symbol = static_cast<SymbolImpl*>(&vm.privateSymbolRegistry().symbolForKey(str).leakRef());
+                else
+                    symbol = static_cast<SymbolImpl*>(&vm.symbolRegistry().symbolForKey(str).leakRef());
+            } else if (m_isWellKnownSymbol)
+                symbol = vm.propertyNames->builtinNames().lookUpWellKnownSymbol(buffer, m_length);
             else
-                symbol = decoder.vm().propertyNames->builtinNames().lookUpPrivateName(buffer, m_length);
+                symbol = vm.propertyNames->builtinNames().lookUpPrivateName(buffer, m_length);
             RELEASE_ASSERT(symbol);
             String str = symbol;
             StringImpl* impl = str.releaseImpl().get();
@@ -773,6 +784,8 @@
     bool m_isSymbol : 1;
     bool m_isWellKnownSymbol : 1;
     bool m_isAtomic : 1;
+    bool m_isRegistered : 1;
+    bool m_isPrivate : 1;
     unsigned m_length;
 };
 

Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (269800 => 269801)


--- trunk/Source/_javascript_Core/runtime/VM.cpp	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp	2020-11-13 22:32:01 UTC (rev 269801)
@@ -382,6 +382,7 @@
     , topCallFrame(CallFrame::noCaller())
     , deferredWorkTimer(DeferredWorkTimer::create(*this))
     , m_atomStringTable(vmType == Default ? Thread::current().atomStringTable() : new AtomStringTable)
+    , m_privateSymbolRegistry(WTF::SymbolRegistry::Type::PrivateSymbol)
     , propertyNames(nullptr)
     , emptyList(new ArgList)
     , machineCodeBytesPerBytecodeWordForBaselineJIT(makeUnique<SimpleStats>())

Modified: trunk/Source/_javascript_Core/runtime/VM.h (269800 => 269801)


--- trunk/Source/_javascript_Core/runtime/VM.h	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/_javascript_Core/runtime/VM.h	2020-11-13 22:32:01 UTC (rev 269801)
@@ -714,6 +714,7 @@
 
     AtomStringTable* m_atomStringTable;
     WTF::SymbolRegistry m_symbolRegistry;
+    WTF::SymbolRegistry m_privateSymbolRegistry;
     CommonIdentifiers* propertyNames;
     const ArgList* emptyList;
     SmallStrings smallStrings;
@@ -725,6 +726,7 @@
 
     AtomStringTable* atomStringTable() const { return m_atomStringTable; }
     WTF::SymbolRegistry& symbolRegistry() { return m_symbolRegistry; }
+    WTF::SymbolRegistry& privateSymbolRegistry() { return m_privateSymbolRegistry; }
 
     Strong<JSBigInt> heapBigIntConstantOne;
 

Modified: trunk/Source/WTF/ChangeLog (269800 => 269801)


--- trunk/Source/WTF/ChangeLog	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/WTF/ChangeLog	2020-11-13 22:32:01 UTC (rev 269801)
@@ -1,3 +1,24 @@
+2020-11-13  Xan López  <[email protected]>
+
+        [JSC] Use symbols as identifiers for class fields computed names storage
+        https://bugs.webkit.org/show_bug.cgi?id=216172
+
+        Reviewed by Yusuke Suzuki.
+
+        Use private symbols for the property keys of the class fields with
+        computed names. This is cleaner than using raw numeric identifiers and
+        will be less cumbersome when we add static fields. It also prevents
+        potential collisions if other features want to store data in the class
+        scope.
+
+        * wtf/text/SymbolImpl.cpp:
+        (WTF::RegisteredSymbolImpl::createPrivate): add a method to create a registered private symbol from a string key.
+        * wtf/text/SymbolImpl.h:
+        * wtf/text/SymbolRegistry.cpp:
+        (WTF::SymbolRegistry::symbolForKey): consider that we can hold private symbols now too.
+        * wtf/text/SymbolRegistry.h:
+        (WTF::SymbolRegistry::SymbolRegistry): new enum type for public/private symbols.
+
 2020-11-12  Darin Adler  <[email protected]>
 
         Remove unused advanced plug-in features: snapshotting and plug-in load policy

Modified: trunk/Source/WTF/wtf/text/SymbolImpl.cpp (269800 => 269801)


--- trunk/Source/WTF/wtf/text/SymbolImpl.cpp	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/WTF/wtf/text/SymbolImpl.cpp	2020-11-13 22:32:01 UTC (rev 269801)
@@ -79,4 +79,13 @@
     return adoptRef(*new RegisteredSymbolImpl(rep.m_data16, rep.length(), *ownerRep, symbolRegistry));
 }
 
+Ref<RegisteredSymbolImpl> RegisteredSymbolImpl::createPrivate(StringImpl& rep, SymbolRegistry& symbolRegistry)
+{
+    auto* ownerRep = (rep.bufferOwnership() == BufferSubstring) ? rep.substringBuffer() : &rep;
+    ASSERT(ownerRep->bufferOwnership() != BufferSubstring);
+    if (rep.is8Bit())
+        return adoptRef(*new RegisteredSymbolImpl(rep.m_data8, rep.length(), *ownerRep, symbolRegistry, s_flagIsRegistered | s_flagIsPrivate));
+    return adoptRef(*new RegisteredSymbolImpl(rep.m_data16, rep.length(), *ownerRep, symbolRegistry, s_flagIsRegistered | s_flagIsPrivate));
+}
+
 } // namespace WTF

Modified: trunk/Source/WTF/wtf/text/SymbolImpl.h (269800 => 269801)


--- trunk/Source/WTF/wtf/text/SymbolImpl.h	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/WTF/wtf/text/SymbolImpl.h	2020-11-13 22:32:01 UTC (rev 269801)
@@ -156,15 +156,16 @@
     void clearSymbolRegistry() { m_symbolRegistry = nullptr; }
 
     static Ref<RegisteredSymbolImpl> create(StringImpl& rep, SymbolRegistry&);
+    static Ref<RegisteredSymbolImpl> createPrivate(StringImpl& rep, SymbolRegistry&);
 
-    RegisteredSymbolImpl(const LChar* characters, unsigned length, Ref<StringImpl>&& base, SymbolRegistry& registry)
-        : SymbolImpl(characters, length, WTFMove(base), s_flagIsRegistered)
+    RegisteredSymbolImpl(const LChar* characters, unsigned length, Ref<StringImpl>&& base, SymbolRegistry& registry, Flags flags = s_flagIsRegistered)
+        : SymbolImpl(characters, length, WTFMove(base), flags)
         , m_symbolRegistry(&registry)
     {
     }
 
-    RegisteredSymbolImpl(const UChar* characters, unsigned length, Ref<StringImpl>&& base, SymbolRegistry& registry)
-        : SymbolImpl(characters, length, WTFMove(base), s_flagIsRegistered)
+    RegisteredSymbolImpl(const UChar* characters, unsigned length, Ref<StringImpl>&& base, SymbolRegistry& registry, Flags flags = s_flagIsRegistered)
+        : SymbolImpl(characters, length, WTFMove(base), flags)
         , m_symbolRegistry(&registry)
     {
     }

Modified: trunk/Source/WTF/wtf/text/SymbolRegistry.cpp (269800 => 269801)


--- trunk/Source/WTF/wtf/text/SymbolRegistry.cpp	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/WTF/wtf/text/SymbolRegistry.cpp	2020-11-13 22:32:01 UTC (rev 269801)
@@ -28,6 +28,11 @@
 
 namespace WTF {
 
+SymbolRegistry::SymbolRegistry(Type type)
+    : m_symbolType(type)
+{
+}
+
 SymbolRegistry::~SymbolRegistry()
 {
     for (auto& key : m_table) {
@@ -44,9 +49,14 @@
         return *static_cast<SymbolImpl*>(addResult.iterator->impl())->asRegisteredSymbolImpl();
     }
 
-    auto symbol = RegisteredSymbolImpl::create(*rep.impl(), *this);
-    *addResult.iterator = SymbolRegistryKey(&symbol.get());
-    return symbol;
+    RefPtr<RegisteredSymbolImpl> symbol;
+    if (m_symbolType == Type::PrivateSymbol)
+        symbol = RegisteredSymbolImpl::createPrivate(*rep.impl(), *this);
+    else
+        symbol = RegisteredSymbolImpl::create(*rep.impl(), *this);
+
+    *addResult.iterator = SymbolRegistryKey(symbol.get());
+    return symbol.releaseNonNull();
 }
 
 void SymbolRegistry::remove(RegisteredSymbolImpl& uid)

Modified: trunk/Source/WTF/wtf/text/SymbolRegistry.h (269800 => 269801)


--- trunk/Source/WTF/wtf/text/SymbolRegistry.h	2020-11-13 22:29:34 UTC (rev 269800)
+++ trunk/Source/WTF/wtf/text/SymbolRegistry.h	2020-11-13 22:32:01 UTC (rev 269801)
@@ -78,7 +78,8 @@
     WTF_MAKE_FAST_ALLOCATED;
     WTF_MAKE_NONCOPYABLE(SymbolRegistry);
 public:
-    SymbolRegistry() = default;
+    enum class Type : uint8_t { PublicSymbol, PrivateSymbol };
+    WTF_EXPORT_PRIVATE SymbolRegistry(Type = Type::PublicSymbol);
     WTF_EXPORT_PRIVATE ~SymbolRegistry();
 
     WTF_EXPORT_PRIVATE Ref<RegisteredSymbolImpl> symbolForKey(const String&);
@@ -87,6 +88,7 @@
 
 private:
     HashSet<SymbolRegistryKey> m_table;
+    Type m_symbolType;
 };
 
 inline SymbolRegistryKey::SymbolRegistryKey(StringImpl* uid)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to