RESOLVED FIXED 143404
[ES6] Implement Symbol.for and Symbol.keyFor
https://bugs.webkit.org/show_bug.cgi?id=143404
Summary [ES6] Implement Symbol.for and Symbol.keyFor
Yusuke Suzuki
Reported 2015-04-04 12:32:58 PDT
To complete Symbol implementation before exposing it to web pages, we implement Symbol.for and Symbol.keyFor.
Attachments
Patch (20.58 KB, patch)
2015-04-04 12:59 PDT, Yusuke Suzuki
no flags
Patch (25.24 KB, patch)
2015-04-04 13:19 PDT, Yusuke Suzuki
no flags
Patch (25.15 KB, patch)
2015-04-05 18:07 PDT, Yusuke Suzuki
no flags
Patch (37.44 KB, patch)
2015-04-09 13:39 PDT, Yusuke Suzuki
no flags
Patch (37.52 KB, patch)
2015-04-09 13:44 PDT, Yusuke Suzuki
no flags
Patch (42.55 KB, patch)
2015-04-09 15:53 PDT, Yusuke Suzuki
no flags
Patch (40.20 KB, patch)
2015-04-09 16:09 PDT, Yusuke Suzuki
no flags
Patch (42.24 KB, patch)
2015-04-09 16:36 PDT, Yusuke Suzuki
no flags
Patch (43.13 KB, patch)
2015-04-09 16:44 PDT, Yusuke Suzuki
no flags
Patch (43.26 KB, patch)
2015-04-12 10:12 PDT, Yusuke Suzuki
ggaren: review+
try to fix the build (913 bytes, patch)
2015-04-16 16:22 PDT, Geoffrey Garen
ap: review+
ap: commit-queue+
Yusuke Suzuki
Comment 1 2015-04-04 12:59:22 PDT
Yusuke Suzuki
Comment 2 2015-04-04 13:19:38 PDT
WebKit Commit Bot
Comment 3 2015-04-04 13:21:18 PDT
Attachment 250135 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/SymbolConstructor.cpp:43: Alphabetical sorting problem. [build/include_order] [4] Total errors found: 1 in 17 files If any of these errors are false positives, please file a bug against check-webkit-style.
Yusuke Suzuki
Comment 4 2015-04-05 18:07:33 PDT
WebKit Commit Bot
Comment 5 2015-04-05 18:10:02 PDT
Attachment 250183 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/SymbolConstructor.cpp:43: Alphabetical sorting problem. [build/include_order] [4] Total errors found: 1 in 17 files If any of these errors are false positives, please file a bug against check-webkit-style.
Yusuke Suzuki
Comment 6 2015-04-07 13:31:29 PDT
Could anyone take a look?
Geoffrey Garen
Comment 7 2015-04-07 14:28:02 PDT
Comment on attachment 250183 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=250183&action=review > Source/JavaScriptCore/runtime/SymbolRegistry.cpp:51 > + Symbol* symbol = Symbol::create(exec, key); Is it spec-compliant that we'll return a new symbol object every time here? For example, is it spec-compliant that 'Symbol.for("a") !== Symbol.for("a")'? My reading of 19.4.2.1 says that it is not OK, and that we must return the same Symbol we returned last time, since the spec says "If SameValue(e.[[key]], stringKey) is true, return e.[[symbol]]". Also, the MDN documentation provides the example, "Symbol.for("bar") === Symbol.for("bar"); // true". Side question: If we are required to share a symbol across realms, how do we prevent information leakage across realms through the symbol prototype? I think this API might be broken by design, and not implementable in a web browser. > Source/JavaScriptCore/runtime/SymbolRegistry.h:39 > +class SymbolRegistry { This registry leaks memory because it has no way to remove UIDs once they are no longer in use: for (var i = 0; i < 10000000; ++i) Symbol.for(i); // Lots of memory now in use. You need a way to make the entries in this registry weak. WeakGCMap or HashMap<Weak<T>> might work for that purpose.
Geoffrey Garen
Comment 8 2015-04-07 14:32:36 PDT
> Is it spec-compliant that we'll return a new symbol object every time here? > For example, is it spec-compliant that 'Symbol.for("a") !== Symbol.for("a")'? Ah, I see that you've modified ==, ===, Map, and Set to allow for equality across distinct symbol objects. So, my only concern here is the memory leak.
Yusuke Suzuki
Comment 9 2015-04-07 14:38:49 PDT
Comment on attachment 250183 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=250183&action=review Thank you for your review. >> Source/JavaScriptCore/runtime/SymbolRegistry.h:39 >> +class SymbolRegistry { > > This registry leaks memory because it has no way to remove UIDs once they are no longer in use: > > for (var i = 0; i < 10000000; ++i) > Symbol.for(i); > > // Lots of memory now in use. > > You need a way to make the entries in this registry weak. WeakGCMap or HashMap<Weak<T>> might work for that purpose. Right. Symbol.key always returns symbols. So users cannot determine that this symbols is newly created or looked up if users don't have reference to the previously registered symbol. So I think we can hold symbols in SymbolRegistry as weak references. I'll change it :D
Yusuke Suzuki
Comment 10 2015-04-08 08:07:35 PDT
Comment on attachment 250183 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=250183&action=review >>> Source/JavaScriptCore/runtime/SymbolRegistry.h:39 >>> +class SymbolRegistry { >> >> This registry leaks memory because it has no way to remove UIDs once they are no longer in use: >> >> for (var i = 0; i < 10000000; ++i) >> Symbol.for(i); >> >> // Lots of memory now in use. >> >> You need a way to make the entries in this registry weak. WeakGCMap or HashMap<Weak<T>> might work for that purpose. > > Right. > Symbol.key always returns symbols. So users cannot determine that this symbols is newly created or looked up if users don't have reference to the previously registered symbol. > So I think we can hold symbols in SymbolRegistry as weak references. > I'll change it :D After investigating it, I found difficult issues. Weak & WeakGCMap work for JSCell objects that's managed by GC, however, AtomicStringImpl is not GC-managed object. (Managed by RefCount) And since distinct Symbols can be equal, we cannot deal with it by holding Weak<Symbol>. For example, AtomicStringImpl* uid = ... Symbol* sym1 = Symbol::create(vm, uid); Symbol* sym2 = Symbol::create(vm, uid); // Now sym1 and sym2 are equal in JS context. Weak<Symbol> weak(sym1) // If weak becomes null, we can know sym1 is collected. // However, sym2 still may exist. We now allow for equality across distinct symbols to just store AtomicStringImpl* in PropertyTable. If we don't allow it, we need to store Symbol* into PropertyTable and GC need to mark them (so adding visitChildren for PropertyTable becomes necessary)
Yusuke Suzuki
Comment 11 2015-04-08 10:01:29 PDT
OK. I'll integrate WeakPtrFactory into StringImpl only if StringImpl is unique. This class may reside as if substringBuffer().
Yusuke Suzuki
Comment 12 2015-04-09 13:39:34 PDT
WebKit Commit Bot
Comment 13 2015-04-09 13:41:39 PDT
Attachment 250460 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/SymbolConstructor.cpp:44: Bad include order. Mixing system and custom headers. [build/include_order] [4] Total errors found: 1 in 21 files If any of these errors are false positives, please file a bug against check-webkit-style.
Yusuke Suzuki
Comment 14 2015-04-09 13:44:16 PDT
WebKit Commit Bot
Comment 15 2015-04-09 13:45:50 PDT
Attachment 250462 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/SymbolConstructor.cpp:44: Bad include order. Mixing system and custom headers. [build/include_order] [4] Total errors found: 1 in 21 files If any of these errors are false positives, please file a bug against check-webkit-style.
Yusuke Suzuki
Comment 16 2015-04-09 15:53:14 PDT
WebKit Commit Bot
Comment 17 2015-04-09 15:55:55 PDT
Attachment 250478 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/SymbolConstructor.cpp:44: Bad include order. Mixing system and custom headers. [build/include_order] [4] Total errors found: 1 in 23 files If any of these errors are false positives, please file a bug against check-webkit-style.
Yusuke Suzuki
Comment 18 2015-04-09 16:09:00 PDT
WebKit Commit Bot
Comment 19 2015-04-09 16:11:25 PDT
Attachment 250481 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/SymbolConstructor.cpp:44: Bad include order. Mixing system and custom headers. [build/include_order] [4] Total errors found: 1 in 23 files If any of these errors are false positives, please file a bug against check-webkit-style.
Yusuke Suzuki
Comment 20 2015-04-09 16:36:29 PDT
WebKit Commit Bot
Comment 21 2015-04-09 16:38:39 PDT
Attachment 250486 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/SymbolConstructor.cpp:44: Bad include order. Mixing system and custom headers. [build/include_order] [4] Total errors found: 1 in 23 files If any of these errors are false positives, please file a bug against check-webkit-style.
Yusuke Suzuki
Comment 22 2015-04-09 16:44:08 PDT
WebKit Commit Bot
Comment 23 2015-04-09 16:46:07 PDT
Attachment 250487 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/SymbolConstructor.cpp:44: Bad include order. Mixing system and custom headers. [build/include_order] [4] Total errors found: 1 in 23 files If any of these errors are false positives, please file a bug against check-webkit-style.
Yusuke Suzuki
Comment 24 2015-04-09 16:48:50 PDT
OK, I've confirmed the patch passes the JSC tests. So the patch is ready for review.
Darin Adler
Comment 25 2015-04-09 23:02:19 PDT
Comment on attachment 250487 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=250487&action=review Test coverage seems a bit light. Does it really exercise all the code paths? Generally speaking, I think classes are easier to read if we keep function bodies out of them. We can still mark them inline, but the class itself is hard to read if it’s full of the function bodies and not just the declarations. > Source/JavaScriptCore/runtime/SymbolConstructor.cpp:117 > + JSValue sym = exec->argument(0); I suggest calling this local variable argument, symbolValue, or even just symbol, rather than sym. > Source/JavaScriptCore/runtime/SymbolConstructor.cpp:122 > + Symbol* symbol = asSymbol(sym); > + AtomicStringImpl* uid = symbol->privateName().uid(); I think this would read better in a single line rather than with a local variable. > Source/JavaScriptCore/runtime/VM.h:282 > + std::unique_ptr<WTF::SymbolRegistry> m_symbolRegistry; Why a pointer to a symbol registry instead of an entire symbol registry? Just costs us an extra pointer dereference all the time. > Source/JavaScriptCore/runtime/VM.h:293 > + WTF::SymbolRegistry* symbolRegistry() { return m_symbolRegistry.get(); } Should return a reference rather than a pointer, since it can never be null. > Source/WTF/wtf/text/StringImpl.cpp:295 > + RefPtr<StringImpl> rep = argRep; I don’t understand why we need this change. It looks to me like everything here would have worked if we just left rep as before. > Source/WTF/wtf/text/SymbolRegistry.cpp:40 > + WTF_MAKE_NONCOPYABLE(SymbolRegistryLocker); No needed. Deriving from SpinLockHolder already makes this non-copyable. > Source/WTF/wtf/text/SymbolRegistry.cpp:42 > + SymbolRegistryLocker(SymbolRegistry* symbolRegistry) Should take a reference rather than a pointer. > Source/WTF/wtf/text/SymbolRegistry.cpp:53 > + SymbolRegistryLocker(SymbolRegistry*) { } Should take a reference rather than a pointer. > Source/WTF/wtf/text/SymbolRegistry.cpp:59 > + : m_table() Why is this necessary? > Source/WTF/wtf/text/SymbolRegistry.cpp:61 > + , m_spinLock() Why is this necessary? > Source/WTF/wtf/text/SymbolRegistry.cpp:68 > + for (auto key : m_table) Should use auto& here instead of auto to avoid doing an extra ref/deref on every key. > Source/WTF/wtf/text/SymbolRegistry.cpp:72 > +Ref<StringImpl> SymbolRegistry::symbolForKey(String rep) This should take const String&, not String. Also not sure why we are taking String here and StringImpl elsewhere in the class. > Source/WTF/wtf/text/SymbolRegistry.cpp:77 > + if (addResult.isNewEntry) { I’d normally suggest putting the “not new” case first and doing an early return in that case. > Source/WTF/wtf/text/SymbolRegistry.cpp:86 > +String SymbolRegistry::keyForSymbol(StringImpl* uid) Should take a reference, not a pointer. > Source/WTF/wtf/text/SymbolRegistry.cpp:95 > +void SymbolRegistry::remove(StringImpl* uid) Should take a reference, not a pointer. > Source/WTF/wtf/text/SymbolRegistry.h:33 > +#include <wtf/HashSet.h> > +#include <wtf/WTFThreadData.h> > +#include <wtf/text/StringHash.h> > +#include <wtf/text/StringImpl.h> > +#include <wtf/text/WTFString.h> Too many includes. Don’t need to include headers that are included by others that are already in the list. > Source/WTF/wtf/text/SymbolRegistry.h:48 > + : m_impl() > + , m_hash() Shouldn’t need to do either of these. > Source/WTF/wtf/text/SymbolRegistry.h:52 > + explicit SymbolRegistryKey(StringImpl* uid) Should take a reference, not a pointer. > Source/WTF/wtf/text/SymbolRegistry.h:54 > + , m_hash(0) Strange to use m_hash() above and m_hash(0) here. I suggest using neither. > Source/WTF/wtf/text/SymbolRegistry.h:88 > + StringImpl* m_impl; > + unsigned m_hash; Should initialize both of these here: StringImpl* m_impl { nullptr }; unsigned m_hash { 0 }; > Source/WTF/wtf/text/SymbolRegistry.h:93 > + struct Hash : public StringHash { No need for public, since public is the default for struct inheritance. > Source/WTF/wtf/text/SymbolRegistry.h:114 > + WTF_MAKE_FAST_ALLOCATED; Should not use this, and should not allocate this on the heap. > Source/WTF/wtf/text/SymbolRegistry.h:121 > + WTF_EXPORT_PRIVATE SymbolRegistry(); > + WTF_EXPORT_PRIVATE ~SymbolRegistry(); > + > + WTF_EXPORT_PRIVATE Ref<StringImpl> symbolForKey(String); > + WTF_EXPORT_PRIVATE String keyForSymbol(StringImpl* uid); Are you sure all of these need to be exported?
Yusuke Suzuki
Comment 26 2015-04-11 06:48:31 PDT
Comment on attachment 250487 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=250487&action=review >> Source/JavaScriptCore/runtime/SymbolConstructor.cpp:117 >> + JSValue sym = exec->argument(0); > > I suggest calling this local variable argument, symbolValue, or even just symbol, rather than sym. OK, rename it to symbolValue. >> Source/JavaScriptCore/runtime/SymbolConstructor.cpp:122 >> + AtomicStringImpl* uid = symbol->privateName().uid(); > > I think this would read better in a single line rather than with a local variable. Fixed. >> Source/JavaScriptCore/runtime/VM.h:282 >> + std::unique_ptr<WTF::SymbolRegistry> m_symbolRegistry; > > Why a pointer to a symbol registry instead of an entire symbol registry? Just costs us an extra pointer dereference all the time. So, using SymbolRegistry directly instead of using a pointer. >> Source/JavaScriptCore/runtime/VM.h:293 >> + WTF::SymbolRegistry* symbolRegistry() { return m_symbolRegistry.get(); } > > Should return a reference rather than a pointer, since it can never be null. Fixed. >> Source/WTF/wtf/text/StringImpl.cpp:295 >> + RefPtr<StringImpl> rep = argRep; > > I don’t understand why we need this change. It looks to me like everything here would have worked if we just left rep as before. Oops, it's used for debugging purpose. I'll fix it. >> Source/WTF/wtf/text/SymbolRegistry.cpp:40 >> + WTF_MAKE_NONCOPYABLE(SymbolRegistryLocker); > > No needed. Deriving from SpinLockHolder already makes this non-copyable. OK, dropped. >> Source/WTF/wtf/text/SymbolRegistry.cpp:42 >> + SymbolRegistryLocker(SymbolRegistry* symbolRegistry) > > Should take a reference rather than a pointer. Changed. >> Source/WTF/wtf/text/SymbolRegistry.cpp:53 >> + SymbolRegistryLocker(SymbolRegistry*) { } > > Should take a reference rather than a pointer. Changed. >> Source/WTF/wtf/text/SymbolRegistry.cpp:59 >> + : m_table() > > Why is this necessary? OK, constructor is now dropped since there's no special construction. >> Source/WTF/wtf/text/SymbolRegistry.cpp:61 >> + , m_spinLock() > > Why is this necessary? Ditto. >> Source/WTF/wtf/text/SymbolRegistry.cpp:68 >> + for (auto key : m_table) > > Should use auto& here instead of auto to avoid doing an extra ref/deref on every key. Since SymbolRegistryKey doesn't have ownership to StringImpl* (when StringImpl* is destructed, SymbolRegistryKey is removed by SymbolRegistry::remove), so ref/deref doesn't occur. But, to align to the styles in WebKit, I'll use auto& :D >> Source/WTF/wtf/text/SymbolRegistry.cpp:72 >> +Ref<StringImpl> SymbolRegistry::symbolForKey(String rep) > > This should take const String&, not String. Also not sure why we are taking String here and StringImpl elsewhere in the class. OK, use const String&. This is because String represents just String, but StringImpl is used as uid (unique id) (like in JSC::Identifier class). SymbolRegistry::symbolForKey takes String and returnes Symbol (StringImpl uid). SymbolRegistry::keyForSymbol takes Symbol (StringImpl* uid) and returns String. >> Source/WTF/wtf/text/SymbolRegistry.cpp:77 >> + if (addResult.isNewEntry) { > > I’d normally suggest putting the “not new” case first and doing an early return in that case. Fixed :) >> Source/WTF/wtf/text/SymbolRegistry.cpp:86 >> +String SymbolRegistry::keyForSymbol(StringImpl* uid) > > Should take a reference, not a pointer. Here, StringImpl* is used as uid (StringImpl* uid), not String. So I think using StringImpl* is better (like in JSC::Identifier / JSC::PropertyName class). >> Source/WTF/wtf/text/SymbolRegistry.cpp:95 >> +void SymbolRegistry::remove(StringImpl* uid) > > Should take a reference, not a pointer. Ditto. >> Source/WTF/wtf/text/SymbolRegistry.h:33 >> +#include <wtf/text/WTFString.h> > > Too many includes. Don’t need to include headers that are included by others that are already in the list. Dropped several headers. >> Source/WTF/wtf/text/SymbolRegistry.h:48 >> + , m_hash() > > Shouldn’t need to do either of these. Replace it with SymbolRegistryKey() = default; >> Source/WTF/wtf/text/SymbolRegistry.h:52 >> + explicit SymbolRegistryKey(StringImpl* uid) > > Should take a reference, not a pointer. To align to the JSC::Identifier/JSC::PropertyName, use StringImpl* here (which represents uid). >> Source/WTF/wtf/text/SymbolRegistry.h:54 >> + , m_hash(0) > > Strange to use m_hash() above and m_hash(0) here. I suggest using neither. Use this style. >> Source/WTF/wtf/text/SymbolRegistry.h:88 >> + unsigned m_hash; > > Should initialize both of these here: > > StringImpl* m_impl { nullptr }; > unsigned m_hash { 0 }; Looks very nice. >> Source/WTF/wtf/text/SymbolRegistry.h:93 >> + struct Hash : public StringHash { > > No need for public, since public is the default for struct inheritance. Dropped. >> Source/WTF/wtf/text/SymbolRegistry.h:114 >> + WTF_MAKE_FAST_ALLOCATED; > > Should not use this, and should not allocate this on the heap. OK, dropped. >> Source/WTF/wtf/text/SymbolRegistry.h:121 >> + WTF_EXPORT_PRIVATE String keyForSymbol(StringImpl* uid); > > Are you sure all of these need to be exported? They are called from JSC.
Yusuke Suzuki
Comment 27 2015-04-12 10:12:56 PDT
WebKit Commit Bot
Comment 28 2015-04-12 10:16:22 PDT
Attachment 250611 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/runtime/SymbolConstructor.cpp:44: Bad include order. Mixing system and custom headers. [build/include_order] [4] Total errors found: 1 in 23 files If any of these errors are false positives, please file a bug against check-webkit-style.
Yusuke Suzuki
Comment 29 2015-04-14 17:37:34 PDT
Could you review this patch?
Geoffrey Garen
Comment 30 2015-04-16 12:39:14 PDT
Comment on attachment 250611 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=250611&action=review I'm a bit worried about the WEB_THREAD thread safety here, but the rest of the patch looks good. > Source/WTF/wtf/text/SymbolRegistry.cpp:55 > +#if USE(WEB_THREAD) > + > +class SymbolRegistryLocker : public SpinLockHolder { > +public: > + SymbolRegistryLocker(SymbolRegistry& symbolRegistry) > + : SpinLockHolder(symbolRegistry.spinLock()) > + { > + } > +}; > + > +#else > + > +class SymbolRegistryLocker { > + WTF_MAKE_NONCOPYABLE(SymbolRegistryLocker); > +public: > + SymbolRegistryLocker(SymbolRegistry&) { } > +}; > + > +#endif // USE(WEB_THREAD) Why does the symbol registry need this locking, even though the identifier table does not?
Yusuke Suzuki
Comment 31 2015-04-16 15:15:22 PDT
Comment on attachment 250611 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=250611&action=review Thank you for your review! >> Source/WTF/wtf/text/SymbolRegistry.cpp:55 >> +#endif // USE(WEB_THREAD) > > Why does the symbol registry need this locking, even though the identifier table does not? Currently, AtomicString.cpp protects AtomicStringTable with AtomicStringTableLocker. It seems that it is introduced since WebCore in iOS is executed in 2 threads, the main UI thread and the WebThread. https://bugs.webkit.org/show_bug.cgi?id=108139 So in this time, I followed the design. Since this function can be called at the same time to AtomicString::remove. But seeing AtomicStringTable.cpp now, I think this gaurd for SymbolRegistry is not necessary because SymbolRegistry is owned by JSC VM and StringImpl* used for Symbol cannot be used outside the JSC world because it's hash value is special value. So I think we can drop this lock, is it correct?
Geoffrey Garen
Comment 32 2015-04-16 15:30:51 PDT
> So I think we can drop this lock, is it correct? Yes, I think you are right.
Yusuke Suzuki
Comment 33 2015-04-16 15:34:35 PDT
(In reply to comment #32) > > So I think we can drop this lock, is it correct? > > Yes, I think you are right. Thanks! So I'll land it without locks ;)
Yusuke Suzuki
Comment 34 2015-04-16 15:47:42 PDT
Alexey Proskuryakov
Comment 35 2015-04-16 16:10:27 PDT
This broke Windows build: HeapInlines.h(234): error C2248: 'JSC::SymbolConstructor::StructureFlags' : cannot access protected member declared in class 'JSC::SymbolConstructor'
Alexey Proskuryakov
Comment 36 2015-04-16 16:18:33 PDT
Oh, and Mac too. Is anyone available to fix now?
Geoffrey Garen
Comment 37 2015-04-16 16:22:08 PDT
Created attachment 250972 [details] try to fix the build
Benjamin Poulain
Comment 38 2015-04-16 16:29:22 PDT
Yusuke Suzuki
Comment 39 2015-04-16 20:21:42 PDT
(In reply to comment #38) > Committed r182921: <http://trac.webkit.org/changeset/182921> Oops!!!! Thank you for notifying / fixing that. I missed it. Sorry for troubling you.
Note You need to log in before you can comment on or make changes to this bug.