Source/JavaScriptCore/ChangeLog

112015-04-12 Yusuke Suzuki <utatane.tea@gmail.com>
22
 3 [ES6] Implement Symbol.for and Symbol.keyFor
 4 https://bugs.webkit.org/show_bug.cgi?id=143404
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 This patch implements Symbol.for and Symbol.keyFor.
 9 SymbolRegistry maintains registered StringImpl* symbols.
 10 And to make this mapping enabled over realms,
 11 VM owns this mapping (not JSGlobalObject).
 12
 13 While there's Default AtomicStringTable per thread,
 14 SymbolRegistry should not exist over VMs.
 15 So everytime VM is created, SymbolRegistry is also created.
 16
 17 In SymbolRegistry implementation, we don't leverage WeakGCMap (or weak reference design).
 18 Theres are several reasons.
 19 1. StringImpl* which represents identity of Symbols is not GC-managed object.
 20 So we cannot use WeakGCMap directly.
 21 While Symbol* is GC-managed object, holding weak reference to Symbol* doesn't maintain JS symbols (exposed primitive values to users) liveness,
 22 because distinct Symbol* can exist.
 23 Distinct Symbol* means the Symbol* object that pointer value (Symbol*) is different from weakly referenced Symbol* but held StringImpl* is the same.
 24
 25 2. We don't use WTF::WeakPtr. If we add WeakPtrFactory into StringImpl's member, we can track StringImpl*'s liveness by WeakPtr.
 26 However there's problem about when we prune staled entries in SymbolRegistry.
 27 Since the memory allocated for the Symbol is typically occupied by allocated symbolized StringImpl*'s content,
 28 and it is not in GC-heap.
 29 While heavily registering Symbols and storing StringImpl* into SymbolRegistry, Heap's EdenSpace is not so occupied.
 30 So GC typically attempt to perform EdenCollection, and it doesn't call WeakGCMap's pruleStaleEntries callback.
 31 As a result, before pruning staled entries in SymbolRegistry, fast malloc-ed memory fills up the system memory.
 32
 33 So instead of using Weak reference, we take relatively easy design.
 34 When we register symbolized StringImpl* into SymbolRegistry, symbolized StringImpl* is aware of that.
 35 And when destructing it, it removes its reference from SymbolRegistry as if atomic StringImpl do so with AtomicStringTable.
 36
 37 * CMakeLists.txt:
 38 * DerivedSources.make:
 39 * runtime/SymbolConstructor.cpp:
 40 (JSC::SymbolConstructor::getOwnPropertySlot):
 41 (JSC::symbolConstructorFor):
 42 (JSC::symbolConstructorKeyFor):
 43 * runtime/SymbolConstructor.h:
 44 * runtime/VM.cpp:
 45 * runtime/VM.h:
 46 (JSC::VM::symbolRegistry):
 47 * tests/stress/symbol-registry.js: Added.
 48 (test):
 49
 502015-04-12 Yusuke Suzuki <utatane.tea@gmail.com>
 51
352 [ES6] Implement Array.prototype.values
453 https://bugs.webkit.org/show_bug.cgi?id=143633
554

Source/WTF/ChangeLog

 12015-04-12 Yusuke Suzuki <utatane.tea@gmail.com>
 2
 3 [ES6] Implement Symbol.for and Symbol.keyFor
 4 https://bugs.webkit.org/show_bug.cgi?id=143404
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 When we register symbolized StringImpl* into SymbolRegistry, symbolized StringImpl* is aware of that.
 9 And when destructing it, it removes its reference from SymbolRegistry as if atomic StringImpl do so with AtomicStringTable.
 10 While AtomicStringTable (in WebCore case) exists in thread local storage,
 11 SymbolRegistry exists per VM and StringImpl* has a reference to the registered SymbolRegistry.
 12
 13 Since StringImpl has isSymbol etc. members, it's class is aware of Symbol use cases.
 14 So introduce SymbolRegistry in WTF layers as if AtomicStringTable.
 15
 16 * WTF.vcxproj/WTF.vcxproj:
 17 * WTF.vcxproj/WTF.vcxproj.filters:
 18 * WTF.xcodeproj/project.pbxproj:
 19 * wtf/CMakeLists.txt:
 20 * wtf/text/AtomicString.cpp:
 21 (WTF::AtomicString::addSlowCase):
 22 (WTF::AtomicString::findSlowCase):
 23 (WTF::AtomicString::findInternal):
 24 (WTF::AtomicString::find): Deleted.
 25 * wtf/text/AtomicString.h:
 26 (WTF::AtomicString::find):
 27 * wtf/text/StringImpl.cpp:
 28 (WTF::StringImpl::~StringImpl):
 29 (WTF::StringImpl::createSymbol):
 30 (WTF::StringImpl::createSymbolEmpty):
 31 * wtf/text/StringImpl.h:
 32 (WTF::StringImpl::StringImpl):
 33 (WTF::StringImpl::extractFoldedStringInSymbol):
 34 (WTF::StringImpl::symbolRegistry):
 35 (WTF::StringImpl::createSymbolEmpty): Deleted.
 36 * wtf/text/SymbolRegistry.cpp: Added.
 37 (WTF::SymbolRegistryLocker::SymbolRegistryLocker):
 38 (WTF::SymbolRegistry::~SymbolRegistry):
 39 (WTF::SymbolRegistry::symbolForKey):
 40 (WTF::SymbolRegistry::keyForSymbol):
 41 (WTF::SymbolRegistry::remove):
 42 * wtf/text/SymbolRegistry.h: Added.
 43 (WTF::SymbolRegistryKey::hash):
 44 (WTF::SymbolRegistryKey::impl):
 45 (WTF::SymbolRegistryKey::isHashTableDeletedValue):
 46 (WTF::SymbolRegistryKey::hashTableDeletedValue):
 47 (WTF::DefaultHash<SymbolRegistryKey>::Hash::hash):
 48 (WTF::DefaultHash<SymbolRegistryKey>::Hash::equal):
 49 (WTF::HashTraits<SymbolRegistryKey>::isEmptyValue):
 50 (WTF::SymbolRegistry::spinLock):
 51 (WTF::SymbolRegistryKey::SymbolRegistryKey):
 52
1532015-04-08 Sam Weinig <sam@webkit.org>
254
355 Allow LaunchServices to handle URLs on link navigations

Source/JavaScriptCore/CMakeLists.txt

@@set(JavaScriptCore_RUNTIME_SOURCES
565565 runtime/StructureIDTable.cpp
566566 runtime/StructureRareData.cpp
567567 runtime/Symbol.cpp
 568 runtime/SymbolConstructor.cpp
568569 runtime/SymbolObject.cpp
569570 runtime/SymbolPrototype.cpp
570  runtime/SymbolConstructor.cpp
571571 runtime/SymbolTable.cpp
572572 runtime/TestRunnerUtils.cpp
573573 runtime/TypeLocationCache.cpp

@@set(JavaScriptCore_LUT_FILES
611611 runtime/RegExpPrototype.cpp
612612 runtime/StringConstructor.cpp
613613 runtime/StringIteratorPrototype.cpp
 614 runtime/SymbolConstructor.cpp
614615 runtime/SymbolPrototype.cpp
615616)
616617

Source/JavaScriptCore/DerivedSources.make

@@all : \
5858 RegExpObject.lut.h \
5959 StringConstructor.lut.h \
6060 StringIteratorPrototype.lut.h \
 61 SymbolConstructor.lut.h \
6162 SymbolPrototype.lut.h \
6263 udis86_itab.h \
6364 Bytecodes.h \

Source/JavaScriptCore/runtime/SymbolConstructor.cpp

3030#include "Error.h"
3131#include "JSCInlines.h"
3232#include "JSGlobalObject.h"
 33#include "Symbol.h"
3334#include "SymbolPrototype.h"
 35#include <wtf/text/SymbolRegistry.h>
 36
 37namespace JSC {
 38
 39static EncodedJSValue JSC_HOST_CALL symbolConstructorFor(ExecState*);
 40static EncodedJSValue JSC_HOST_CALL symbolConstructorKeyFor(ExecState*);
 41
 42}
 43
 44#include "SymbolConstructor.lut.h"
3445
3546namespace JSC {
3647

3849
3950const ClassInfo SymbolConstructor::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(SymbolConstructor) };
4051
 52/* Source for SymbolConstructor.lut.h
 53@begin symbolConstructorTable
 54 for symbolConstructorFor DontEnum|Function 1
 55 keyFor symbolConstructorKeyFor DontEnum|Function 1
 56@end
 57*/
 58
4159SymbolConstructor::SymbolConstructor(VM& vm, Structure* structure)
4260 : InternalFunction(vm, structure)
4361{

@@void SymbolConstructor::finishCreation(VM& vm, SymbolPrototype* prototype)
5573 JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_WELL_KNOWN_SYMBOLS)
5674}
5775
 76bool SymbolConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot)
 77{
 78 return getStaticFunctionSlot<Base>(exec, symbolConstructorTable, jsCast<SymbolConstructor*>(object), propertyName, slot);
 79}
 80
 81// ------------------------------ Functions ---------------------------
 82
5883static EncodedJSValue JSC_HOST_CALL callSymbol(ExecState* exec)
5984{
6085 JSValue description = exec->argument(0);

@@CallType SymbolConstructor::getCallData(JSCell*, CallData& callData)
7499 return CallTypeHost;
75100}
76101
 102EncodedJSValue JSC_HOST_CALL symbolConstructorFor(ExecState* exec)
 103{
 104 JSString* stringKey = exec->argument(0).toString(exec);
 105 if (exec->hadException())
 106 return JSValue::encode(jsUndefined());
 107 String string = stringKey->value(exec);
 108 if (exec->hadException())
 109 return JSValue::encode(jsUndefined());
 110
 111 Ref<StringImpl> uid = exec->vm().symbolRegistry().symbolForKey(string);
 112 return JSValue::encode(Symbol::create(exec->vm(), static_cast<AtomicStringImpl*>(&uid.get())));
 113}
 114
 115EncodedJSValue JSC_HOST_CALL symbolConstructorKeyFor(ExecState* exec)
 116{
 117 JSValue symbolValue = exec->argument(0);
 118 if (!symbolValue.isSymbol())
 119 return JSValue::encode(throwTypeError(exec));
 120
 121 AtomicStringImpl* uid = asSymbol(symbolValue)->privateName().uid();
 122 ASSERT(uid->isSymbol());
 123 if (!uid->symbolRegistry())
 124 return JSValue::encode(jsUndefined());
 125
 126 ASSERT(uid->symbolRegistry() == &exec->vm().symbolRegistry());
 127 return JSValue::encode(jsString(exec, exec->vm().symbolRegistry().keyForSymbol(uid)));
 128}
 129
77130} // namespace JSC

Source/JavaScriptCore/runtime/SymbolConstructor.h

@@class SymbolConstructor : public InternalFunction {
5555protected:
5656 void finishCreation(VM&, SymbolPrototype*);
5757
 58 static const unsigned StructureFlags = OverridesGetOwnPropertySlot | Base::StructureFlags;
 59
5860private:
5961 SymbolConstructor(VM&, Structure*);
6062 static ConstructType getConstructData(JSCell*, ConstructData&);
6163 static CallType getCallData(JSCell*, CallData&);
 64 static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
6265};
6366
6467} // namespace JSC

Source/JavaScriptCore/runtime/VM.cpp

8787#include "UnlinkedCodeBlock.h"
8888#include "WeakGCMapInlines.h"
8989#include "WeakMapData.h"
 90#include <wtf/CurrentTime.h>
9091#include <wtf/ProcessID.h>
9192#include <wtf/RetainPtr.h>
9293#include <wtf/StringPrintStream.h>
9394#include <wtf/Threading.h>
9495#include <wtf/WTFThreadData.h>
9596#include <wtf/text/AtomicStringTable.h>
96 #include <wtf/CurrentTime.h>
 97#include <wtf/text/SymbolRegistry.h>
9798
9899#if ENABLE(DFG_JIT)
99100#include "ConservativeRoots.h"

Source/JavaScriptCore/runtime/VM.h

6464#include <wtf/ThreadSafeRefCounted.h>
6565#include <wtf/ThreadSpecific.h>
6666#include <wtf/WTFThreadData.h>
 67#include <wtf/text/SymbolRegistry.h>
6768#include <wtf/text/WTFString.h>
6869#if ENABLE(REGEXP_TRACING)
6970#include <wtf/ListHashSet.h>

@@class VM : public ThreadSafeRefCounted<VM> {
278279 Strong<JSCell> emptyPropertyNameEnumerator;
279280
280281 AtomicStringTable* m_atomicStringTable;
 282 WTF::SymbolRegistry m_symbolRegistry;
281283 CommonIdentifiers* propertyNames;
282284 const MarkedArgumentBuffer* emptyList; // Lists are supposed to be allocated on the stack to have their elements properly marked, which is not the case here - but this list has nothing to mark.
283285 SmallStrings smallStrings;

@@class VM : public ThreadSafeRefCounted<VM> {
288290 Strong<JSString> lastCachedString;
289291
290292 AtomicStringTable* atomicStringTable() const { return m_atomicStringTable; }
 293 WTF::SymbolRegistry& symbolRegistry() { return m_symbolRegistry; }
291294
292295 void setInDefineOwnProperty(bool inDefineOwnProperty)
293296 {

Source/JavaScriptCore/tests/stress/symbol-registry.js

 1function test(actual, expected) {
 2 if (actual !== expected)
 3 throw new Error("bad value: " + actual);
 4}
 5
 6(function () {
 7 var hello = Symbol("Hello");
 8 var proto = Symbol("__proto__");
 9
 10 for (var sym of [ hello, proto, Symbol.iterator ]) {
 11 var key = Symbol.keyFor(sym);
 12 test(key, undefined);
 13 // twice
 14 var key = Symbol.keyFor(sym);
 15 test(key, undefined);
 16 }
 17}());
 18
 19(function () {
 20 var keys = [
 21 "Hello",
 22 "__proto__",
 23 "Symbol.iterator",
 24 '',
 25 null,
 26 undefined,
 27 42,
 28 20.5,
 29 -42,
 30 -20.5,
 31 true,
 32 false,
 33 {},
 34 function () {},
 35 [],
 36 ];
 37 for (var key of keys) {
 38 var sym = Symbol.for(key);
 39 test(typeof sym, "symbol");
 40 test(sym.toString(), "Symbol(" + String(key) + ")");
 41
 42 var sym2 = Symbol.for(key);
 43 test(sym === sym2, true);
 44
 45 var key = Symbol.keyFor(sym);
 46 test(key, key);
 47 var key = Symbol.keyFor(sym2);
 48 test(key, key);
 49 }
 50}());
 51
 52(function () {
 53 var error = null;
 54 try {
 55 var key = {
 56 toString() {
 57 throw new Error('toString');
 58 }
 59 };
 60 Symbol.for(key);
 61 } catch (e) {
 62 error = e;
 63 }
 64 if (!error)
 65 throw new Error('not thrown');
 66 if (String(error) !== 'Error: toString')
 67 throw new Error('bad error: ' + String(error));
 68}());
 69
 70(function () {
 71 var elements = [
 72 null,
 73 undefined,
 74 42,
 75 20.5,
 76 true,
 77 false,
 78 'string',
 79 {},
 80 function () {},
 81 [],
 82 ];
 83 for (var item of elements) {
 84 var error = null;
 85 try {
 86 Symbol.keyFor(item);
 87 } catch (e) {
 88 error = e;
 89 }
 90 if (!error)
 91 throw new Error('not thrown');
 92 if (String(error) !== 'TypeError: Type error')
 93 throw new Error('bad error: ' + String(error));
 94 }
 95}());
 96
 97(function () {
 98 for (var i = 0; i < 10000; ++i)
 99 Symbol.for(i);
 100 gc();
 101}());
 102
 103(function () {
 104 for (var i = 0; i < 100; ++i) {
 105 var symbol = Symbol.for(i);
 106 test(String(symbol), "Symbol(" + i + ")");
 107 test(symbol, Symbol.for(i));
 108 gc();
 109 }
 110 gc();
 111}());
 112
 113(function () {
 114 var symbols = [];
 115 for (var i = 0; i < 100; ++i) {
 116 var symbol = Symbol.for(i);
 117 symbols.push(symbol);
 118 }
 119
 120 for (var i = 0; i < 100; ++i)
 121 test(Symbol.for(i), symbols[i]);
 122
 123 for (var i = 0; i < 100; ++i)
 124 test(Symbol.keyFor(Symbol(i)), undefined);
 125}());

Source/WTF/WTF.vcxproj/WTF.vcxproj

134134 <ClCompile Include="..\wtf\text\StringImpl.cpp" />
135135 <ClCompile Include="..\wtf\text\StringStatics.cpp" />
136136 <ClCompile Include="..\wtf\text\StringView.cpp" />
 137 <ClCompile Include="..\wtf\text\SymbolRegistry.cpp" />
137138 <ClCompile Include="..\wtf\text\WTFString.cpp" />
138139 <ClCompile Include="..\wtf\text\cf\AtomicStringCF.cpp" />
139140 <ClCompile Include="..\wtf\text\cf\StringCF.cpp" />

295296 <ClInclude Include="..\wtf\text\StringImpl.h" />
296297 <ClInclude Include="..\wtf\text\StringOperators.h" />
297298 <ClInclude Include="..\wtf\text\StringView.h" />
 299 <ClInclude Include="..\wtf\text\SymbolRegistry.h" />
298300 <ClInclude Include="..\wtf\text\WTFString.h" />
299301 <ClInclude Include="..\wtf\Threading.h" />
300302 <ClInclude Include="..\wtf\ThreadingPrimitives.h" />

Source/WTF/WTF.vcxproj/WTF.vcxproj.filters

7878 <ClCompile Include="..\wtf\text\StringImpl.cpp">
7979 <Filter>text</Filter>
8080 </ClCompile>
 81 <ClCompile Include="..\wtf\text\SymbolRegistry.cpp">
 82 <Filter>text</Filter>
 83 </ClCompile>
8184 <ClCompile Include="..\wtf\text\WTFString.cpp">
8285 <Filter>text</Filter>
8386 </ClCompile>

342345 <ClInclude Include="..\wtf\text\StringView.h">
343346 <Filter>wtf</Filter>
344347 </ClInclude>
 348 <ClInclude Include="..\wtf\text\SymbolRegistry.h">
 349 <Filter>text</Filter>
 350 </ClInclude>
345351 <ClInclude Include="..\wtf\text\WTFString.h">
346352 <Filter>text</Filter>
347353 </ClInclude>

Source/WTF/WTF.xcodeproj/project.pbxproj

7474 2CDED0F318115C85004DBA70 /* RunLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2CDED0F118115C85004DBA70 /* RunLoop.cpp */; };
7575 2CDED0F418115C85004DBA70 /* RunLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = 2CDED0F218115C85004DBA70 /* RunLoop.h */; };
7676 430B47891AAAAC1A001223DA /* StringCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 430B47871AAAAC1A001223DA /* StringCommon.h */; };
 77 70A993FE1AD7151300FA615B /* SymbolRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70A993FC1AD7151300FA615B /* SymbolRegistry.cpp */; };
 78 70A993FF1AD7151300FA615B /* SymbolRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 70A993FD1AD7151300FA615B /* SymbolRegistry.h */; };
7779 7CBBA07419BB7FDC00BBF025 /* OSObjectPtr.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CBBA07319BB7FDC00BBF025 /* OSObjectPtr.h */; };
7880 7CDD7FF8186D291E007433CD /* IteratorAdaptors.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CDD7FF7186D291E007433CD /* IteratorAdaptors.h */; };
7981 7CDD7FFA186D2A54007433CD /* IteratorRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CDD7FF9186D2A54007433CD /* IteratorRange.h */; };

362364 5D247B7014689C4700E78B76 /* DebugRelease.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DebugRelease.xcconfig; sourceTree = "<group>"; };
363365 5D247B7314689C4700E78B76 /* WTF.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = WTF.xcconfig; sourceTree = "<group>"; };
364366 6541CAF41630DB26006D0DEC /* CopyWTFHeaders.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = CopyWTFHeaders.xcconfig; sourceTree = "<group>"; };
 367 70A993FC1AD7151300FA615B /* SymbolRegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolRegistry.cpp; sourceTree = "<group>"; };
 368 70A993FD1AD7151300FA615B /* SymbolRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolRegistry.h; sourceTree = "<group>"; };
365369 7CBBA07319BB7FDC00BBF025 /* OSObjectPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSObjectPtr.h; sourceTree = "<group>"; };
366370 7CDD7FF7186D291E007433CD /* IteratorAdaptors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IteratorAdaptors.h; sourceTree = "<group>"; };
367371 7CDD7FF9186D2A54007433CD /* IteratorRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IteratorRange.h; sourceTree = "<group>"; };

954958 93F1993D19D7958D00C2390B /* StringView.cpp */,
955959 A8A4732A151A825B004123FF /* StringOperators.h */,
956960 A8A4732B151A825B004123FF /* StringStatics.cpp */,
 961 70A993FC1AD7151300FA615B /* SymbolRegistry.cpp */,
 962 70A993FD1AD7151300FA615B /* SymbolRegistry.h */,
957963 A8A4732C151A825B004123FF /* TextPosition.h */,
958964 A8A4732D151A825B004123FF /* WTFString.cpp */,
959965 A8A4732E151A825B004123FF /* WTFString.h */,

11271133 A8A473EE151A825B004123FF /* MetaAllocatorHandle.h in Headers */,
11281134 CE46516E19DB1FB4003ECA05 /* NSMapTableSPI.h in Headers */,
11291135 0F0D85B417234CC100338210 /* NoLock.h in Headers */,
 1136 70A993FF1AD7151300FA615B /* SymbolRegistry.h in Headers */,
11301137 A8A473EF151A825B004123FF /* Noncopyable.h in Headers */,
11311138 A8A473F5151A825B004123FF /* NumberOfCores.h in Headers */,
11321139 7E29C33E15FFD79B00516D61 /* ObjcRuntimeExtras.h in Headers */,

13781385 A5BA15FC182435A600A82E69 /* StringImplCF.cpp in Sources */,
13791386 A5BA15F51824348000A82E69 /* StringImplMac.mm in Sources */,
13801387 A5BA15F3182433A900A82E69 /* StringMac.mm in Sources */,
 1388 70A993FE1AD7151300FA615B /* SymbolRegistry.cpp in Sources */,
13811389 0FDDBFA71666DFA300C55FEF /* StringPrintStream.cpp in Sources */,
13821390 A8A47443151A825B004123FF /* StringStatics.cpp in Sources */,
13831391 93934BD518A1F16900D0D6A1 /* StringViewCF.cpp in Sources */,

Source/WTF/wtf/CMakeLists.txt

@@set(WTF_HEADERS
132132 text/StringHash.h
133133 text/StringImpl.h
134134 text/StringView.h
 135 text/SymbolRegistry.h
135136 text/WTFString.h
136137
137138 threads/BinarySemaphore.h

@@set(WTF_SOURCES
199200 text/StringImpl.cpp
200201 text/StringStatics.cpp
201202 text/StringView.cpp
 203 text/SymbolRegistry.cpp
202204 text/WTFString.cpp
203205
204206 threads/BinarySemaphore.cpp

Source/WTF/wtf/text/AtomicString.cpp

@@struct CharBufferFromLiteralDataTranslator {
403403 if (!string.length())
404404 return *StringImpl::empty();
405405
406  if (string.isSymbol())
407  return add(string.extractFoldedStringInSymbol());
 406 if (string.isSymbol()) {
 407 if (string.is8Bit())
 408 return *add(string.characters8(), string.length());
 409 return *add(string.characters16(), string.length());
 410 }
408411
409412 ASSERT_WITH_MESSAGE(!string.isAtomic(), "AtomicString should not hit the slow case if the string is already atomic.");
410413

@@struct CharBufferFromLiteralDataTranslator {
424427 if (!string.length())
425428 return *StringImpl::empty();
426429
427  if (string.isSymbol())
428  return add(stringTable, string.extractFoldedStringInSymbol());
 430 if (string.isSymbol()) {
 431 if (string.is8Bit())
 432 return *add(string.characters8(), string.length());
 433 return *add(string.characters16(), string.length());
 434 }
429435
430436 ASSERT_WITH_MESSAGE(!string.isAtomic(), "AtomicString should not hit the slow case if the string is already atomic.");
431437

@@AtomicStringImpl* AtomicString::findSlowCase(StringImpl& string)
512518 if (!string.length())
513519 return static_cast<AtomicStringImpl*>(StringImpl::empty());
514520
515  if (string.isSymbol())
516  return find(&string.extractFoldedStringInSymbol());
 521 if (string.isSymbol()) {
 522 if (string.is8Bit())
 523 return findInternal(string.characters8(), string.length());
 524 return findInternal(string.characters16(), string.length());
 525 }
517526
518527 AtomicStringTableLocker locker;
519528 HashSet<StringImpl*>& atomicStringTable = stringTable();

@@AtomicString AtomicString::number(double number)
553562 return String(numberToFixedPrecisionString(number, 6, buffer, true));
554563}
555564
556 AtomicStringImpl* AtomicString::find(LChar* characters, unsigned length)
 565AtomicStringImpl* AtomicString::findInternal(const LChar* characters, unsigned length)
557566{
558567 AtomicStringTableLocker locker;
559568 auto& table = stringTable();

@@AtomicStringImpl* AtomicString::find(LChar* characters, unsigned length)
565574 return nullptr;
566575}
567576
568 AtomicStringImpl* AtomicString::find(UChar* characters, unsigned length)
 577AtomicStringImpl* AtomicString::findInternal(const UChar* characters, unsigned length)
569578{
570579 AtomicStringTableLocker locker;
571580 auto& table = stringTable();

Source/WTF/wtf/text/AtomicString.h

@@class AtomicString {
8686 AtomicString(WTF::HashTableDeletedValueType) : m_string(WTF::HashTableDeletedValue) { }
8787 bool isHashTableDeletedValue() const { return m_string.isHashTableDeletedValue(); }
8888
89  WTF_EXPORT_STRING_API static AtomicStringImpl* find(LChar*, unsigned length);
90  WTF_EXPORT_STRING_API static AtomicStringImpl* find(UChar*, unsigned length);
 89 static AtomicStringImpl* find(LChar* characters, unsigned length)
 90 {
 91 return findInternal(characters, length);
 92 }
 93 static AtomicStringImpl* find(UChar* characters, unsigned length)
 94 {
 95 return findInternal(characters, length);
 96 }
9197 static AtomicStringImpl* find(StringImpl* string)
9298 {
9399 if (!string || string->isAtomic())

@@class AtomicString {
249255
250256 WTF_EXPORT_STRING_API static AtomicStringImpl* findSlowCase(StringImpl&);
251257 WTF_EXPORT_STRING_API static AtomicString fromUTF8Internal(const char*, const char*);
 258
 259 WTF_EXPORT_STRING_API static AtomicStringImpl* findInternal(const LChar*, unsigned length);
 260 WTF_EXPORT_STRING_API static AtomicStringImpl* findInternal(const UChar*, unsigned length);
252261};
253262
254263inline bool operator==(const AtomicString& a, const AtomicString& b) { return a.impl() == b.impl(); }

Source/WTF/wtf/text/StringImpl.cpp

3333#include <wtf/WTFThreadData.h>
3434#include <wtf/text/CString.h>
3535#include <wtf/text/StringView.h>
 36#include <wtf/text/SymbolRegistry.h>
3637#include <wtf/unicode/CharacterNames.h>
3738#include <wtf/unicode/UTF8.h>
3839

@@void StringStats::printStats()
112113 if (isAtomic() && length() && !isSymbol())
113114 AtomicString::remove(this);
114115
 116 if (isSymbol() && symbolRegistry())
 117 symbolRegistry()->remove(this);
 118
115119 BufferOwnership ownership = bufferOwnership();
116120
117121 if (ownership == BufferInternal)

@@void StringImpl::destroy(StringImpl* stringImpl)
288292
289293Ref<StringImpl> StringImpl::createSymbol(PassRefPtr<StringImpl> rep)
290294{
291  unsigned length = rep->length();
292  if (!length)
293  return createSymbolEmpty();
294  Ref<StringImpl> string = createSubstringSharingImpl(rep, 0, length);
295  ASSERT(!string->isAtomic());
296  string->m_hashAndFlags = hashAndFlagsForSymbol(string->m_hashAndFlags);
297  return string;
 295 StringImpl* ownerRep = (rep->bufferOwnership() == BufferSubstring) ? rep->substringBuffer() : rep.get();
 296
 297 // We allocate a buffer that contains
 298 // 1. the StringImpl struct
 299 // 2. the pointer to the owner string
 300 // 3. the pointer to the symbol registry
 301 StringImpl* stringImpl = static_cast<StringImpl*>(fastMalloc(allocationSize<StringImpl*>(2)));
 302 if (rep->is8Bit())
 303 return adoptRef(*new (NotNull, stringImpl) StringImpl(CreateSymbol, rep->m_data8, rep->length(), ownerRep));
 304 return adoptRef(*new (NotNull, stringImpl) StringImpl(CreateSymbol, rep->m_data16, rep->length(), ownerRep));
 305}
 306
 307Ref<StringImpl> StringImpl::createSymbolEmpty()
 308{
 309 return createSymbol(empty());
298310}
299311
300312bool StringImpl::containsOnlyWhitespace()

Source/WTF/wtf/text/StringImpl.h

5757struct SubstringTranslator;
5858struct UCharBufferTranslator;
5959template<typename> class RetainPtr;
 60class SymbolRegistry;
6061
6162enum TextCaseSensitivity {
6263 TextCaseSensitive,

@@class StringImpl {
292293 STRING_STATS_ADD_16BIT_STRING2(m_length, true);
293294 }
294295
295  enum CreateSymbolEmptyTag { CreateSymbolEmpty };
296  StringImpl(CreateSymbolEmptyTag)
 296 enum CreateSymbolTag { CreateSymbol };
 297 // Used to create new symbol strings that holds existing 8-bit [[Description]] string as a substring buffer (BufferSubstring).
 298 StringImpl(CreateSymbolTag, const LChar* characters, unsigned length, PassRefPtr<StringImpl> base)
297299 : m_refCount(s_refCountIncrement)
298  , m_length(0)
299  // We expect m_length to be initialized to 0 as we use it
300  // to represent a null terminated buffer.
301  , m_data8(reinterpret_cast<const LChar*>(&m_length))
302  , m_hashAndFlags(hashAndFlagsForSymbol(s_hashFlag8BitBuffer | BufferInternal))
 300 , m_length(length)
 301 , m_data8(characters)
 302 , m_hashAndFlags(hashAndFlagsForSymbol(s_hashFlag8BitBuffer | StringSymbol | BufferSubstring))
303303 {
 304 ASSERT(is8Bit());
304305 ASSERT(m_data8);
 306 ASSERT(base->bufferOwnership() != BufferSubstring);
305307
306  STRING_STATS_ADD_8BIT_STRING(m_length);
 308 substringBuffer() = base.leakRef();
 309 symbolRegistry() = nullptr;
 310
 311 STRING_STATS_ADD_8BIT_STRING2(m_length, true);
 312 }
 313
 314 // Used to create new symbol strings that holds existing 16-bit [[Description]] string as a substring buffer (BufferSubstring).
 315 StringImpl(CreateSymbolTag, const UChar* characters, unsigned length, PassRefPtr<StringImpl> base)
 316 : m_refCount(s_refCountIncrement)
 317 , m_length(length)
 318 , m_data16(characters)
 319 , m_hashAndFlags(hashAndFlagsForSymbol(StringSymbol | BufferSubstring))
 320 {
 321 ASSERT(!is8Bit());
 322 ASSERT(m_data16);
 323 ASSERT(base->bufferOwnership() != BufferSubstring);
 324
 325 substringBuffer() = base.leakRef();
 326 symbolRegistry() = nullptr;
 327
 328 STRING_STATS_ADD_16BIT_STRING2(m_length, true);
307329 }
308330
309331 ~StringImpl();

@@class StringImpl {
397419 return constructInternal<T>(resultImpl, length);
398420 }
399421
400  ALWAYS_INLINE static Ref<StringImpl> createSymbolEmpty()
401  {
402  return adoptRef(*new StringImpl(CreateSymbolEmpty));
403  }
404 
 422 WTF_EXPORT_STRING_API static Ref<StringImpl> createSymbolEmpty();
405423 WTF_EXPORT_STRING_API static Ref<StringImpl> createSymbol(PassRefPtr<StringImpl> rep);
406424
407425 // Reallocate the StringImpl. The originalString must be only owned by the PassRefPtr,

@@class StringImpl {
741759
742760 WTF_EXPORT_STRING_API static const UChar latin1CaseFoldTable[256];
743761
744  StringImpl& extractFoldedStringInSymbol()
 762 Ref<StringImpl> extractFoldedStringInSymbol()
745763 {
746  ASSERT(length());
747764 ASSERT(isSymbol());
748765 ASSERT(bufferOwnership() == BufferSubstring);
749766 ASSERT(substringBuffer());
750767 ASSERT(!substringBuffer()->isSymbol());
751  return *substringBuffer();
 768 return createSubstringSharingImpl(this, 0, length());
 769 }
 770
 771 SymbolRegistry* const& symbolRegistry() const
 772 {
 773 ASSERT(isSymbol());
 774 return *(tailPointer<SymbolRegistry*>() + 1);
 775 }
 776
 777 SymbolRegistry*& symbolRegistry()
 778 {
 779 ASSERT(isSymbol());
 780 return *(tailPointer<SymbolRegistry*>() + 1);
752781 }
753782
754783private:

Source/WTF/wtf/text/SymbolRegistry.cpp

 1/*
 2 * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26#include "config.h"
 27#include "SymbolRegistry.h"
 28
 29#include "Threading.h"
 30
 31#if USE(WEB_THREAD)
 32#include "SpinLock.h"
 33#endif
 34
 35namespace WTF {
 36
 37#if USE(WEB_THREAD)
 38
 39class SymbolRegistryLocker : public SpinLockHolder {
 40public:
 41 SymbolRegistryLocker(SymbolRegistry& symbolRegistry)
 42 : SpinLockHolder(symbolRegistry.spinLock())
 43 {
 44 }
 45};
 46
 47#else
 48
 49class SymbolRegistryLocker {
 50 WTF_MAKE_NONCOPYABLE(SymbolRegistryLocker);
 51public:
 52 SymbolRegistryLocker(SymbolRegistry&) { }
 53};
 54
 55#endif // USE(WEB_THREAD)
 56
 57SymbolRegistry::~SymbolRegistry()
 58{
 59 for (auto& key : m_table)
 60 key.impl()->symbolRegistry() = nullptr;
 61}
 62
 63Ref<StringImpl> SymbolRegistry::symbolForKey(const String& rep)
 64{
 65 SymbolRegistryLocker locker(*this);
 66
 67 auto addResult = m_table.add(SymbolRegistryKey(rep.impl()));
 68 if (!addResult.isNewEntry)
 69 return *addResult.iterator->impl();
 70
 71 Ref<StringImpl> symbol = StringImpl::createSymbol(rep.impl());
 72 symbol->symbolRegistry() = this;
 73 *addResult.iterator = SymbolRegistryKey(&symbol.get());
 74 return symbol;
 75}
 76
 77String SymbolRegistry::keyForSymbol(StringImpl* uid)
 78{
 79 SymbolRegistryLocker locker(*this);
 80
 81 ASSERT(uid->isSymbol());
 82 ASSERT(uid->symbolRegistry() == this);
 83 return uid->extractFoldedStringInSymbol();
 84}
 85
 86void SymbolRegistry::remove(StringImpl* uid)
 87{
 88 SymbolRegistryLocker locker(*this);
 89
 90 ASSERT(uid->isSymbol());
 91 ASSERT(uid->symbolRegistry() == this);
 92 auto iterator = m_table.find(SymbolRegistryKey(uid));
 93 ASSERT_WITH_MESSAGE(iterator != m_table.end(), "The string being removed is registered in the string table of an other thread!");
 94 m_table.remove(iterator);
 95}
 96
 97}

Source/WTF/wtf/text/SymbolRegistry.h

 1/*
 2 * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26#ifndef WTF_SymbolRegistry_h
 27#define WTF_SymbolRegistry_h
 28
 29#include <wtf/HashSet.h>
 30#include <wtf/text/StringHash.h>
 31#include <wtf/text/WTFString.h>
 32
 33#if USE(WEB_THREAD)
 34#include <wtf/SpinLock.h>
 35#endif
 36
 37namespace WTF {
 38
 39// Since StringImpl* used for Symbol uid doesn't have a hash value reflecting the string content,
 40// to compare with an external string in string contents, introduce SymbolRegistryKey.
 41// SymbolRegistryKey holds a hash value reflecting the string content additionally.
 42class SymbolRegistryKey {
 43public:
 44 SymbolRegistryKey() = default;
 45 explicit SymbolRegistryKey(StringImpl* uid);
 46 SymbolRegistryKey(WTF::HashTableDeletedValueType);
 47
 48 unsigned hash() const { return m_hash; }
 49 StringImpl* impl() const { return m_impl; }
 50
 51 bool isHashTableDeletedValue() const { return m_impl == hashTableDeletedValue(); }
 52
 53private:
 54 static StringImpl* hashTableDeletedValue() { return reinterpret_cast<StringImpl*>(-1); }
 55
 56 StringImpl* m_impl { nullptr };
 57 unsigned m_hash { 0 };
 58};
 59
 60template<typename T> struct DefaultHash;
 61template<> struct DefaultHash<SymbolRegistryKey> {
 62 struct Hash : StringHash {
 63 static unsigned hash(const SymbolRegistryKey& key)
 64 {
 65 return key.hash();
 66 }
 67 static bool equal(const SymbolRegistryKey& a, const SymbolRegistryKey& b)
 68 {
 69 return StringHash::equal(a.impl(), b.impl());
 70 }
 71 };
 72};
 73
 74template<> struct HashTraits<SymbolRegistryKey> : SimpleClassHashTraits<SymbolRegistryKey> {
 75 static const bool hasIsEmptyValueFunction = true;
 76 static bool isEmptyValue(const SymbolRegistryKey& key)
 77 {
 78 return key.impl() == nullptr;
 79 }
 80};
 81
 82class SymbolRegistry {
 83 WTF_MAKE_NONCOPYABLE(SymbolRegistry);
 84public:
 85 SymbolRegistry() = default;
 86 WTF_EXPORT_PRIVATE ~SymbolRegistry();
 87
 88 WTF_EXPORT_PRIVATE Ref<StringImpl> symbolForKey(const String&);
 89 WTF_EXPORT_PRIVATE String keyForSymbol(StringImpl* uid);
 90
 91 void remove(StringImpl* uid);
 92
 93#if USE(WEB_THREAD)
 94 SpinLock& spinLock()
 95 {
 96 return m_spinLock;
 97 }
 98#endif
 99
 100private:
 101 HashSet<SymbolRegistryKey> m_table;
 102#if USE(WEB_THREAD)
 103 SpinLock m_spinLock;
 104#endif
 105};
 106
 107inline SymbolRegistryKey::SymbolRegistryKey(StringImpl* uid)
 108 : m_impl(uid)
 109{
 110 if (uid->isSymbol()) {
 111 if (uid->is8Bit())
 112 m_hash = StringHasher::computeHashAndMaskTop8Bits(uid->characters8(), uid->length());
 113 else
 114 m_hash = StringHasher::computeHashAndMaskTop8Bits(uid->characters16(), uid->length());
 115 } else
 116 m_hash = uid->hash();
 117}
 118
 119inline SymbolRegistryKey::SymbolRegistryKey(WTF::HashTableDeletedValueType)
 120 : m_impl(hashTableDeletedValue())
 121{
 122}
 123
 124}
 125
 126#endif

LayoutTests/ChangeLog

112015-04-12 Yusuke Suzuki <utatane.tea@gmail.com>
22
 3 [ES6] Implement Symbol.for and Symbol.keyFor
 4 https://bugs.webkit.org/show_bug.cgi?id=143404
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Add tests to check Symbol's identity over different realms.
 9
 10 * js/dom/cross-frame-symbols-expected.txt: Added.
 11 * js/dom/cross-frame-symbols.html: Added.
 12 * js/dom/script-tests/cross-frame-symbols.js: Added.
 13
 142015-04-12 Yusuke Suzuki <utatane.tea@gmail.com>
 15
316 [ES6] Implement Array.prototype.values
417 https://bugs.webkit.org/show_bug.cgi?id=143633
518

LayoutTests/js/dom/cross-frame-symbols-expected.txt

 1Tests Symbol and global symbol registry identity
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS frame.Symbol.iterator is Symbol.iterator
 7PASS frame.Symbol('Hello') is not Symbol('Hello')
 8PASS Symbol.keyFor(sym) is 'Hello'
 9PASS frame.Symbol.keyFor(sym) is 'Hello'
 10PASS Symbol.for('Hello') is sym
 11PASS frame.Symbol.for('Hello') is sym
 12PASS successfullyParsed is true
 13
 14TEST COMPLETE
 15

LayoutTests/js/dom/cross-frame-symbols.html

 1<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 2<html>
 3<head>
 4<script src="../../resources/js-test-pre.js"></script>
 5</head>
 6<body>
 7<iframe></iframe>
 8<script src="script-tests/cross-frame-symbols.js"></script>
 9<script src="../../resources/js-test-post.js"></script>
 10</body>
 11</html>

LayoutTests/js/dom/script-tests/cross-frame-symbols.js

 1description("Tests Symbol and global symbol registry identity");
 2
 3var frame = frames[0];
 4
 5shouldBe("frame.Symbol.iterator", "Symbol.iterator");
 6shouldNotBe("frame.Symbol('Hello')", "Symbol('Hello')");
 7
 8var sym = Symbol.for("Hello");
 9shouldBe("Symbol.keyFor(sym)", "'Hello'");
 10shouldBe("frame.Symbol.keyFor(sym)", "'Hello'");
 11shouldBe("Symbol.for('Hello')", "sym");
 12shouldBe("frame.Symbol.for('Hello')", "sym");