1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/symbols.h"
6
7#include "platform/unicode.h"
8#include "vm/handles.h"
9#include "vm/hash_table.h"
10#include "vm/heap/safepoint.h"
11#include "vm/isolate.h"
12#include "vm/object.h"
13#include "vm/object_store.h"
14#include "vm/raw_object.h"
15#include "vm/reusable_handles.h"
16#include "vm/snapshot_ids.h"
17#include "vm/type_table.h"
18#include "vm/visitor.h"
19
20namespace dart {
21
22StringPtr Symbols::predefined_[Symbols::kNumberOfOneCharCodeSymbols];
23String* Symbols::symbol_handles_[Symbols::kMaxPredefinedId];
24
25static const char* names[] = {
26 // clang-format off
27 NULL,
28#define DEFINE_SYMBOL_LITERAL(symbol, literal) literal,
29 PREDEFINED_SYMBOLS_LIST(DEFINE_SYMBOL_LITERAL)
30#undef DEFINE_SYMBOL_LITERAL
31 "", // matches kTokenTableStart.
32#define DEFINE_TOKEN_SYMBOL_INDEX(t, s, p, a) s,
33 DART_TOKEN_LIST(DEFINE_TOKEN_SYMBOL_INDEX)
34 DART_KEYWORD_LIST(DEFINE_TOKEN_SYMBOL_INDEX)
35#undef DEFINE_TOKEN_SYMBOL_INDEX
36 // clang-format on
37};
38
39StringPtr StringFrom(const uint8_t* data, intptr_t len, Heap::Space space) {
40 return String::FromLatin1(data, len, space);
41}
42
43StringPtr StringFrom(const uint16_t* data, intptr_t len, Heap::Space space) {
44 return String::FromUTF16(data, len, space);
45}
46
47StringPtr StringFrom(const int32_t* data, intptr_t len, Heap::Space space) {
48 return String::FromUTF32(data, len, space);
49}
50
51template <typename CharType>
52class CharArray {
53 public:
54 CharArray(const CharType* data, intptr_t len) : data_(data), len_(len) {
55 hash_ = String::Hash(data, len);
56 }
57 StringPtr ToSymbol() const {
58 String& result = String::Handle(StringFrom(data_, len_, Heap::kOld));
59 result.SetCanonical();
60 result.SetHash(hash_);
61 return result.raw();
62 }
63 bool Equals(const String& other) const {
64 ASSERT(other.HasHash());
65 if (other.Hash() != hash_) {
66 return false;
67 }
68 return other.Equals(data_, len_);
69 }
70 intptr_t Hash() const { return hash_; }
71
72 private:
73 const CharType* data_;
74 intptr_t len_;
75 intptr_t hash_;
76};
77typedef CharArray<uint8_t> Latin1Array;
78typedef CharArray<uint16_t> UTF16Array;
79typedef CharArray<int32_t> UTF32Array;
80
81class StringSlice {
82 public:
83 StringSlice(const String& str, intptr_t begin_index, intptr_t length)
84 : str_(str), begin_index_(begin_index), len_(length) {
85 hash_ = is_all() ? str.Hash() : String::Hash(str, begin_index, length);
86 }
87 StringPtr ToSymbol() const;
88 bool Equals(const String& other) const {
89 ASSERT(other.HasHash());
90 if (other.Hash() != hash_) {
91 return false;
92 }
93 return other.Equals(str_, begin_index_, len_);
94 }
95 intptr_t Hash() const { return hash_; }
96
97 private:
98 bool is_all() const { return begin_index_ == 0 && len_ == str_.Length(); }
99 const String& str_;
100 intptr_t begin_index_;
101 intptr_t len_;
102 intptr_t hash_;
103};
104
105StringPtr StringSlice::ToSymbol() const {
106 if (is_all() && str_.IsOld()) {
107 str_.SetCanonical();
108 return str_.raw();
109 } else {
110 String& result =
111 String::Handle(String::SubString(str_, begin_index_, len_, Heap::kOld));
112 result.SetCanonical();
113 result.SetHash(hash_);
114 return result.raw();
115 }
116}
117
118class ConcatString {
119 public:
120 ConcatString(const String& str1, const String& str2)
121 : str1_(str1), str2_(str2), hash_(String::HashConcat(str1, str2)) {}
122 StringPtr ToSymbol() const;
123 bool Equals(const String& other) const {
124 ASSERT(other.HasHash());
125 if (other.Hash() != hash_) {
126 return false;
127 }
128 return other.EqualsConcat(str1_, str2_);
129 }
130 intptr_t Hash() const { return hash_; }
131
132 private:
133 const String& str1_;
134 const String& str2_;
135 intptr_t hash_;
136};
137
138StringPtr ConcatString::ToSymbol() const {
139 String& result = String::Handle(String::Concat(str1_, str2_, Heap::kOld));
140 result.SetCanonical();
141 result.SetHash(hash_);
142 return result.raw();
143}
144
145class SymbolTraits {
146 public:
147 static const char* Name() { return "SymbolTraits"; }
148 static bool ReportStats() { return false; }
149
150 static bool IsMatch(const Object& a, const Object& b) {
151 const String& a_str = String::Cast(a);
152 const String& b_str = String::Cast(b);
153 ASSERT(a_str.HasHash());
154 ASSERT(b_str.HasHash());
155 if (a_str.Hash() != b_str.Hash()) {
156 return false;
157 }
158 intptr_t a_len = a_str.Length();
159 if (a_len != b_str.Length()) {
160 return false;
161 }
162 // Use a comparison which does not consider the state of the canonical bit.
163 return a_str.Equals(b_str, 0, a_len);
164 }
165 template <typename CharType>
166 static bool IsMatch(const CharArray<CharType>& array, const Object& obj) {
167 return array.Equals(String::Cast(obj));
168 }
169 static bool IsMatch(const StringSlice& slice, const Object& obj) {
170 return slice.Equals(String::Cast(obj));
171 }
172 static bool IsMatch(const ConcatString& concat, const Object& obj) {
173 return concat.Equals(String::Cast(obj));
174 }
175 static uword Hash(const Object& key) { return String::Cast(key).Hash(); }
176 template <typename CharType>
177 static uword Hash(const CharArray<CharType>& array) {
178 return array.Hash();
179 }
180 static uword Hash(const StringSlice& slice) { return slice.Hash(); }
181 static uword Hash(const ConcatString& concat) { return concat.Hash(); }
182 template <typename CharType>
183 static ObjectPtr NewKey(const CharArray<CharType>& array) {
184 return array.ToSymbol();
185 }
186 static ObjectPtr NewKey(const StringSlice& slice) { return slice.ToSymbol(); }
187 static ObjectPtr NewKey(const ConcatString& concat) {
188 return concat.ToSymbol();
189 }
190};
191typedef UnorderedHashSet<SymbolTraits> SymbolTable;
192
193const char* Symbols::Name(SymbolId symbol) {
194 ASSERT((symbol > kIllegal) && (symbol < kNullCharId));
195 return names[symbol];
196}
197
198const String& Symbols::Token(Token::Kind token) {
199 const int tok_index = token;
200 ASSERT((0 <= tok_index) && (tok_index < Token::kNumTokens));
201 // First keyword symbol is in symbol_handles_[kTokenTableStart + 1].
202 const intptr_t token_id = Symbols::kTokenTableStart + 1 + tok_index;
203 ASSERT(symbol_handles_[token_id] != NULL);
204 return *symbol_handles_[token_id];
205}
206
207void Symbols::Init(Isolate* vm_isolate) {
208 // Should only be run by the vm isolate.
209 ASSERT(Isolate::Current() == Dart::vm_isolate());
210 ASSERT(vm_isolate == Dart::vm_isolate());
211 Zone* zone = Thread::Current()->zone();
212
213 // Create and setup a symbol table in the vm isolate.
214 SetupSymbolTable(vm_isolate);
215
216 // Create all predefined symbols.
217 ASSERT((sizeof(names) / sizeof(const char*)) == Symbols::kNullCharId);
218
219 SymbolTable table(zone, vm_isolate->object_store()->symbol_table());
220
221 // First set up all the predefined string symbols.
222 // Create symbols for language keywords. Some keywords are equal to
223 // symbols we already created, so use New() instead of Add() to ensure
224 // that the symbols are canonicalized.
225 for (intptr_t i = 1; i < Symbols::kNullCharId; i++) {
226 String* str = String::ReadOnlyHandle();
227 *str = OneByteString::New(names[i], Heap::kOld);
228 str->Hash();
229 *str ^= table.InsertOrGet(*str);
230 str->SetCanonical(); // Make canonical once entered.
231 symbol_handles_[i] = str;
232 }
233
234 // Add Latin1 characters as Symbols, so that Symbols::FromCharCode is fast.
235 for (intptr_t c = 0; c < kNumberOfOneCharCodeSymbols; c++) {
236 intptr_t idx = (kNullCharId + c);
237 ASSERT(idx < kMaxPredefinedId);
238 ASSERT(Utf::IsLatin1(c));
239 uint8_t ch = static_cast<uint8_t>(c);
240 String* str = String::ReadOnlyHandle();
241 *str = OneByteString::New(&ch, 1, Heap::kOld);
242 str->Hash();
243 *str ^= table.InsertOrGet(*str);
244 ASSERT(predefined_[c] == nullptr);
245 str->SetCanonical(); // Make canonical once entered.
246 predefined_[c] = str->raw();
247 symbol_handles_[idx] = str;
248 }
249
250 vm_isolate->object_store()->set_symbol_table(table.Release());
251}
252
253void Symbols::InitFromSnapshot(Isolate* vm_isolate) {
254 // Should only be run by the vm isolate.
255 ASSERT(Isolate::Current() == Dart::vm_isolate());
256 ASSERT(vm_isolate == Dart::vm_isolate());
257 Zone* zone = Thread::Current()->zone();
258
259 SymbolTable table(zone, vm_isolate->object_store()->symbol_table());
260
261 // Lookup all the predefined string symbols and language keyword symbols
262 // and cache them in the read only handles for fast access.
263 for (intptr_t i = 1; i < Symbols::kNullCharId; i++) {
264 String* str = String::ReadOnlyHandle();
265 const unsigned char* name =
266 reinterpret_cast<const unsigned char*>(names[i]);
267 *str ^= table.GetOrNull(Latin1Array(name, strlen(names[i])));
268 ASSERT(!str->IsNull());
269 ASSERT(str->HasHash());
270 ASSERT(str->IsCanonical());
271 symbol_handles_[i] = str;
272 }
273
274 // Lookup Latin1 character Symbols and cache them in read only handles,
275 // so that Symbols::FromCharCode is fast.
276 for (intptr_t c = 0; c < kNumberOfOneCharCodeSymbols; c++) {
277 intptr_t idx = (kNullCharId + c);
278 ASSERT(idx < kMaxPredefinedId);
279 ASSERT(Utf::IsLatin1(c));
280 uint8_t ch = static_cast<uint8_t>(c);
281 String* str = String::ReadOnlyHandle();
282 *str ^= table.GetOrNull(Latin1Array(&ch, 1));
283 ASSERT(!str->IsNull());
284 ASSERT(str->HasHash());
285 ASSERT(str->IsCanonical());
286 predefined_[c] = str->raw();
287 symbol_handles_[idx] = str;
288 }
289
290 vm_isolate->object_store()->set_symbol_table(table.Release());
291}
292
293void Symbols::SetupSymbolTable(Isolate* isolate) {
294 ASSERT(isolate != NULL);
295
296 // Setup the symbol table used within the String class.
297 const intptr_t initial_size = (isolate == Dart::vm_isolate())
298 ? kInitialVMIsolateSymtabSize
299 : kInitialSymtabSize;
300 Array& array =
301 Array::Handle(HashTables::New<SymbolTable>(initial_size, Heap::kOld));
302 isolate->object_store()->set_symbol_table(array);
303}
304
305void Symbols::Compact() {
306 Thread* thread = Thread::Current();
307 ASSERT(thread->isolate() != Dart::vm_isolate());
308 HANDLESCOPE(thread);
309 Zone* zone = thread->zone();
310 ObjectStore* object_store = thread->isolate()->object_store();
311
312 // 1. Drop the tables and do a full garbage collection.
313 object_store->set_symbol_table(Object::empty_array());
314 object_store->set_canonical_types(Object::empty_array());
315 object_store->set_canonical_type_parameters(Object::empty_array());
316 object_store->set_canonical_type_arguments(Object::empty_array());
317 thread->heap()->CollectAllGarbage();
318
319 // 2. Walk the heap to find surviving canonical objects.
320 GrowableArray<String*> symbols;
321 GrowableArray<class Type*> types;
322 GrowableArray<class TypeParameter*> type_params;
323 GrowableArray<class TypeArguments*> type_args;
324 class SymbolCollector : public ObjectVisitor {
325 public:
326 SymbolCollector(Thread* thread,
327 GrowableArray<String*>* symbols,
328 GrowableArray<class Type*>* types,
329 GrowableArray<class TypeParameter*>* type_params,
330 GrowableArray<class TypeArguments*>* type_args)
331 : symbols_(symbols),
332 types_(types),
333 type_params_(type_params),
334 type_args_(type_args),
335 zone_(thread->zone()) {}
336
337 void VisitObject(ObjectPtr obj) {
338 if (obj->ptr()->IsCanonical()) {
339 if (obj->IsStringInstance()) {
340 symbols_->Add(&String::Handle(zone_, String::RawCast(obj)));
341 } else if (obj->IsType()) {
342 types_->Add(&Type::Handle(zone_, Type::RawCast(obj)));
343 } else if (obj->IsTypeParameter()) {
344 type_params_->Add(
345 &TypeParameter::Handle(zone_, TypeParameter::RawCast(obj)));
346 } else if (obj->IsTypeArguments()) {
347 type_args_->Add(
348 &TypeArguments::Handle(zone_, TypeArguments::RawCast(obj)));
349 }
350 }
351 }
352
353 private:
354 GrowableArray<String*>* symbols_;
355 GrowableArray<class Type*>* types_;
356 GrowableArray<class TypeParameter*>* type_params_;
357 GrowableArray<class TypeArguments*>* type_args_;
358 Zone* zone_;
359 };
360
361 {
362 HeapIterationScope iteration(thread);
363 SymbolCollector visitor(thread, &symbols, &types, &type_params, &type_args);
364 iteration.IterateObjects(&visitor);
365 }
366
367 // 3. Build new tables from the surviving canonical objects.
368 {
369 Array& array = Array::Handle(
370 zone,
371 HashTables::New<SymbolTable>(symbols.length() * 4 / 3, Heap::kOld));
372 SymbolTable table(zone, array.raw());
373 for (intptr_t i = 0; i < symbols.length(); i++) {
374 String& symbol = *symbols[i];
375 ASSERT(symbol.IsString());
376 ASSERT(symbol.IsCanonical());
377 bool present = table.Insert(symbol);
378 ASSERT(!present);
379 }
380 object_store->set_symbol_table(table.Release());
381 }
382
383 {
384 Array& array = Array::Handle(zone, HashTables::New<CanonicalTypeSet>(
385 types.length() * 4 / 3, Heap::kOld));
386 CanonicalTypeSet table(zone, array.raw());
387 for (intptr_t i = 0; i < types.length(); i++) {
388 class Type& type = *types[i];
389 ASSERT(type.IsType());
390 ASSERT(type.IsCanonical());
391 bool present = table.Insert(type);
392 // Two recursive types with different topology (and hashes) may be equal.
393 ASSERT(!present || type.IsRecursive());
394 }
395 object_store->set_canonical_types(table.Release());
396 }
397
398 {
399 Array& array =
400 Array::Handle(zone, HashTables::New<CanonicalTypeParameterSet>(
401 type_params.length() * 4 / 3, Heap::kOld));
402 CanonicalTypeParameterSet table(zone, array.raw());
403 for (intptr_t i = 0; i < type_params.length(); i++) {
404 class TypeParameter& type_param = *type_params[i];
405 ASSERT(type_param.IsTypeParameter());
406 ASSERT(type_param.IsCanonical());
407 if (type_param.IsDeclaration()) continue;
408 bool present = table.Insert(type_param);
409 ASSERT(!present);
410 }
411 object_store->set_canonical_type_parameters(table.Release());
412 }
413
414 {
415 Array& array =
416 Array::Handle(zone, HashTables::New<CanonicalTypeArgumentsSet>(
417 type_args.length() * 4 / 3, Heap::kOld));
418 CanonicalTypeArgumentsSet table(zone, array.raw());
419 for (intptr_t i = 0; i < type_args.length(); i++) {
420 class TypeArguments& type_arg = *type_args[i];
421 ASSERT(type_arg.IsTypeArguments());
422 ASSERT(type_arg.IsCanonical());
423 bool present = table.Insert(type_arg);
424 // Two recursive types with different topology (and hashes) may be equal.
425 ASSERT(!present || type_arg.IsRecursive());
426 }
427 object_store->set_canonical_type_arguments(table.Release());
428 }
429}
430
431void Symbols::GetStats(Isolate* isolate, intptr_t* size, intptr_t* capacity) {
432 ASSERT(isolate != NULL);
433 SymbolTable table(isolate->object_store()->symbol_table());
434 *size = table.NumOccupied();
435 *capacity = table.NumEntries();
436 table.Release();
437}
438
439StringPtr Symbols::New(Thread* thread, const char* cstr, intptr_t len) {
440 ASSERT((cstr != NULL) && (len >= 0));
441 const uint8_t* utf8_array = reinterpret_cast<const uint8_t*>(cstr);
442 return Symbols::FromUTF8(thread, utf8_array, len);
443}
444
445StringPtr Symbols::FromUTF8(Thread* thread,
446 const uint8_t* utf8_array,
447 intptr_t array_len) {
448 if (array_len == 0 || utf8_array == NULL) {
449 return FromLatin1(thread, reinterpret_cast<uint8_t*>(NULL), 0);
450 }
451 Utf8::Type type;
452 intptr_t len = Utf8::CodeUnitCount(utf8_array, array_len, &type);
453 ASSERT(len != 0);
454 Zone* zone = thread->zone();
455 if (type == Utf8::kLatin1) {
456 uint8_t* characters = zone->Alloc<uint8_t>(len);
457 if (!Utf8::DecodeToLatin1(utf8_array, array_len, characters, len)) {
458 Utf8::ReportInvalidByte(utf8_array, array_len, len);
459 return String::null();
460 }
461 return FromLatin1(thread, characters, len);
462 }
463 ASSERT((type == Utf8::kBMP) || (type == Utf8::kSupplementary));
464 uint16_t* characters = zone->Alloc<uint16_t>(len);
465 if (!Utf8::DecodeToUTF16(utf8_array, array_len, characters, len)) {
466 Utf8::ReportInvalidByte(utf8_array, array_len, len);
467 return String::null();
468 }
469 return FromUTF16(thread, characters, len);
470}
471
472StringPtr Symbols::FromLatin1(Thread* thread,
473 const uint8_t* latin1_array,
474 intptr_t len) {
475 return NewSymbol(thread, Latin1Array(latin1_array, len));
476}
477
478StringPtr Symbols::FromUTF16(Thread* thread,
479 const uint16_t* utf16_array,
480 intptr_t len) {
481 return NewSymbol(thread, UTF16Array(utf16_array, len));
482}
483
484StringPtr Symbols::FromUTF32(Thread* thread,
485 const int32_t* utf32_array,
486 intptr_t len) {
487 return NewSymbol(thread, UTF32Array(utf32_array, len));
488}
489
490StringPtr Symbols::FromConcat(Thread* thread,
491 const String& str1,
492 const String& str2) {
493 if (str1.Length() == 0) {
494 return New(thread, str2);
495 } else if (str2.Length() == 0) {
496 return New(thread, str1);
497 } else {
498 return NewSymbol(thread, ConcatString(str1, str2));
499 }
500}
501
502StringPtr Symbols::FromGet(Thread* thread, const String& str) {
503 return FromConcat(thread, GetterPrefix(), str);
504}
505
506StringPtr Symbols::FromSet(Thread* thread, const String& str) {
507 return FromConcat(thread, SetterPrefix(), str);
508}
509
510StringPtr Symbols::FromDot(Thread* thread, const String& str) {
511 return FromConcat(thread, str, Dot());
512}
513
514// TODO(srdjan): If this becomes performance critical code, consider looking
515// up symbol from hash of pieces instead of concatenating them first into
516// a string.
517StringPtr Symbols::FromConcatAll(
518 Thread* thread,
519 const GrowableHandlePtrArray<const String>& strs) {
520 const intptr_t strs_length = strs.length();
521 GrowableArray<intptr_t> lengths(strs_length);
522
523 intptr_t len_sum = 0;
524 const intptr_t kOneByteChar = 1;
525 intptr_t char_size = kOneByteChar;
526
527 for (intptr_t i = 0; i < strs_length; i++) {
528 const String& str = strs[i];
529 const intptr_t str_len = str.Length();
530 if ((String::kMaxElements - len_sum) < str_len) {
531 Exceptions::ThrowOOM();
532 UNREACHABLE();
533 }
534 len_sum += str_len;
535 lengths.Add(str_len);
536 char_size = Utils::Maximum(char_size, str.CharSize());
537 }
538 const bool is_one_byte_string = char_size == kOneByteChar;
539
540 Zone* zone = thread->zone();
541 if (is_one_byte_string) {
542 uint8_t* buffer = zone->Alloc<uint8_t>(len_sum);
543 const uint8_t* const orig_buffer = buffer;
544 for (intptr_t i = 0; i < strs_length; i++) {
545 NoSafepointScope no_safepoint;
546 intptr_t str_len = lengths[i];
547 if (str_len > 0) {
548 const String& str = strs[i];
549 ASSERT(str.IsOneByteString() || str.IsExternalOneByteString());
550 const uint8_t* src_p = str.IsOneByteString()
551 ? OneByteString::DataStart(str)
552 : ExternalOneByteString::DataStart(str);
553 memmove(buffer, src_p, str_len);
554 buffer += str_len;
555 }
556 }
557 ASSERT(len_sum == buffer - orig_buffer);
558 return Symbols::FromLatin1(thread, orig_buffer, len_sum);
559 } else {
560 uint16_t* buffer = zone->Alloc<uint16_t>(len_sum);
561 const uint16_t* const orig_buffer = buffer;
562 for (intptr_t i = 0; i < strs_length; i++) {
563 NoSafepointScope no_safepoint;
564 intptr_t str_len = lengths[i];
565 if (str_len > 0) {
566 const String& str = strs[i];
567 if (str.IsTwoByteString()) {
568 memmove(buffer, TwoByteString::DataStart(str), str_len * 2);
569 } else if (str.IsExternalTwoByteString()) {
570 memmove(buffer, ExternalTwoByteString::DataStart(str), str_len * 2);
571 } else {
572 // One-byte to two-byte string copy.
573 ASSERT(str.IsOneByteString() || str.IsExternalOneByteString());
574 const uint8_t* src_p = str.IsOneByteString()
575 ? OneByteString::DataStart(str)
576 : ExternalOneByteString::DataStart(str);
577 for (int n = 0; n < str_len; n++) {
578 buffer[n] = src_p[n];
579 }
580 }
581 buffer += str_len;
582 }
583 }
584 ASSERT(len_sum == buffer - orig_buffer);
585 return Symbols::FromUTF16(thread, orig_buffer, len_sum);
586 }
587}
588
589// StringType can be StringSlice, ConcatString, or {Latin1,UTF16,UTF32}Array.
590template <typename StringType>
591StringPtr Symbols::NewSymbol(Thread* thread, const StringType& str) {
592 REUSABLE_OBJECT_HANDLESCOPE(thread);
593 REUSABLE_SMI_HANDLESCOPE(thread);
594 REUSABLE_ARRAY_HANDLESCOPE(thread);
595 String& symbol = String::Handle(thread->zone());
596 dart::Object& key = thread->ObjectHandle();
597 Smi& value = thread->SmiHandle();
598 Array& data = thread->ArrayHandle();
599 {
600 Isolate* vm_isolate = Dart::vm_isolate();
601 data = vm_isolate->object_store()->symbol_table();
602 SymbolTable table(&key, &value, &data);
603 symbol ^= table.GetOrNull(str);
604 table.Release();
605 }
606 if (symbol.IsNull()) {
607 IsolateGroup* group = thread->isolate_group();
608 Isolate* isolate = thread->isolate();
609 // In JIT object_store lives on isolate, not on isolate group.
610 ObjectStore* object_store = group->object_store() == nullptr
611 ? isolate->object_store()
612 : group->object_store();
613 if (thread->IsAtSafepoint()) {
614 // There are two cases where we can cause symbol allocation while holding
615 // a safepoint:
616 // - FLAG_enable_isolate_groups in AOT due to the usage of
617 // `RunWithStoppedMutators` in SwitchableCall runtime entry.
618 // - non-PRODUCT mode where the vm-service uses a HeapIterationScope
619 // while building instances
620 // Ideally we should get rid of both cases to avoid this unsafe usage of
621 // the symbol table (we are assuming here that no other thread holds the
622 // symbols_lock).
623 // TODO(https://dartbug.com/41943): Get rid of the symbol table accesses
624 // within safepoint operation scope.
625 RELEASE_ASSERT(group->safepoint_handler()->IsOwnedByTheThread(thread));
626 RELEASE_ASSERT(FLAG_enable_isolate_groups || !USING_PRODUCT);
627
628 // Uncommon case: We are at a safepoint, all mutators are stopped and we
629 // have therefore exclusive access to the symbol table.
630 data = object_store->symbol_table();
631 SymbolTable table(&key, &value, &data);
632 symbol ^= table.InsertNewOrGet(str);
633 object_store->set_symbol_table(table.Release());
634 } else {
635 // Most common case: We are not at a safepoint and the symbol is available
636 // in the symbol table: We require only read access.
637 {
638 SafepointReadRwLocker sl(thread, group->symbols_lock());
639 data = object_store->symbol_table();
640 SymbolTable table(&key, &value, &data);
641 symbol ^= table.GetOrNull(str);
642 table.Release();
643 }
644 // Second common case: We are not at a safepoint and the symbol is not
645 // available in the symbol table: We require only exclusive access.
646 if (symbol.IsNull()) {
647 auto insert_or_get = [&]() {
648 data = object_store->symbol_table();
649 SymbolTable table(&key, &value, &data);
650 symbol ^= table.InsertNewOrGet(str);
651 object_store->set_symbol_table(table.Release());
652 };
653
654 SafepointWriteRwLocker sl(thread, group->symbols_lock());
655 if (FLAG_enable_isolate_groups || !USING_PRODUCT) {
656 // NOTE: Strictly speaking we should use a safepoint operation scope
657 // here to ensure the lock-free usage inside safepoint operations (see
658 // above) is safe. Though this would really kill the performance.
659 // TODO(https://dartbug.com/41943): Get rid of the symbol table
660 // accesses within safepoint operation scope.
661 group->RunWithStoppedMutators(insert_or_get,
662 /*force_heap_growth=*/true);
663 } else {
664 insert_or_get();
665 }
666 }
667 }
668 }
669 ASSERT(symbol.IsSymbol());
670 ASSERT(symbol.HasHash());
671 return symbol.raw();
672}
673
674template <typename StringType>
675StringPtr Symbols::Lookup(Thread* thread, const StringType& str) {
676 REUSABLE_OBJECT_HANDLESCOPE(thread);
677 REUSABLE_SMI_HANDLESCOPE(thread);
678 REUSABLE_ARRAY_HANDLESCOPE(thread);
679 String& symbol = String::Handle(thread->zone());
680 dart::Object& key = thread->ObjectHandle();
681 Smi& value = thread->SmiHandle();
682 Array& data = thread->ArrayHandle();
683 {
684 Isolate* vm_isolate = Dart::vm_isolate();
685 data = vm_isolate->object_store()->symbol_table();
686 SymbolTable table(&key, &value, &data);
687 symbol ^= table.GetOrNull(str);
688 table.Release();
689 }
690 if (symbol.IsNull()) {
691 IsolateGroup* group = thread->isolate_group();
692 Isolate* isolate = thread->isolate();
693 // In JIT object_store lives on isolate, not on isolate group.
694 ObjectStore* object_store = group->object_store() == nullptr
695 ? isolate->object_store()
696 : group->object_store();
697 // See `Symbols::NewSymbol` for more information why we separate the two
698 // cases.
699 if (thread->IsAtSafepoint()) {
700 RELEASE_ASSERT(group->safepoint_handler()->IsOwnedByTheThread(thread));
701 // In DEBUG mode the snapshot writer also calls this method inside a
702 // safepoint.
703#if !defined(DEBUG)
704 RELEASE_ASSERT(FLAG_enable_isolate_groups || !USING_PRODUCT);
705#endif
706 data = object_store->symbol_table();
707 SymbolTable table(&key, &value, &data);
708 symbol ^= table.GetOrNull(str);
709 table.Release();
710 } else {
711 SafepointReadRwLocker sl(thread, group->symbols_lock());
712 data = object_store->symbol_table();
713 SymbolTable table(&key, &value, &data);
714 symbol ^= table.GetOrNull(str);
715 table.Release();
716 }
717 }
718 ASSERT(symbol.IsNull() || symbol.IsSymbol());
719 ASSERT(symbol.IsNull() || symbol.HasHash());
720 return symbol.raw();
721}
722
723StringPtr Symbols::LookupFromConcat(Thread* thread,
724 const String& str1,
725 const String& str2) {
726 if (str1.Length() == 0) {
727 return Lookup(thread, str2);
728 } else if (str2.Length() == 0) {
729 return Lookup(thread, str1);
730 } else {
731 return Lookup(thread, ConcatString(str1, str2));
732 }
733}
734
735StringPtr Symbols::LookupFromGet(Thread* thread, const String& str) {
736 return LookupFromConcat(thread, GetterPrefix(), str);
737}
738
739StringPtr Symbols::LookupFromSet(Thread* thread, const String& str) {
740 return LookupFromConcat(thread, SetterPrefix(), str);
741}
742
743StringPtr Symbols::LookupFromDot(Thread* thread, const String& str) {
744 return LookupFromConcat(thread, str, Dot());
745}
746
747StringPtr Symbols::New(Thread* thread, const String& str) {
748 if (str.IsSymbol()) {
749 return str.raw();
750 }
751 return New(thread, str, 0, str.Length());
752}
753
754StringPtr Symbols::New(Thread* thread,
755 const String& str,
756 intptr_t begin_index,
757 intptr_t len) {
758 return NewSymbol(thread, StringSlice(str, begin_index, len));
759}
760
761StringPtr Symbols::NewFormatted(Thread* thread, const char* format, ...) {
762 va_list args;
763 va_start(args, format);
764 StringPtr result = NewFormattedV(thread, format, args);
765 NoSafepointScope no_safepoint;
766 va_end(args);
767 return result;
768}
769
770StringPtr Symbols::NewFormattedV(Thread* thread,
771 const char* format,
772 va_list args) {
773 va_list args_copy;
774 va_copy(args_copy, args);
775 intptr_t len = Utils::VSNPrint(NULL, 0, format, args_copy);
776 va_end(args_copy);
777
778 Zone* zone = Thread::Current()->zone();
779 char* buffer = zone->Alloc<char>(len + 1);
780 Utils::VSNPrint(buffer, (len + 1), format, args);
781
782 return Symbols::New(thread, buffer);
783}
784
785StringPtr Symbols::FromCharCode(Thread* thread, int32_t char_code) {
786 if (char_code > kMaxOneCharCodeSymbol) {
787 return FromUTF32(thread, &char_code, 1);
788 }
789 return predefined_[char_code];
790}
791
792void Symbols::DumpStats(Isolate* isolate) {
793 intptr_t size = -1;
794 intptr_t capacity = -1;
795 // First dump VM symbol table stats.
796 GetStats(Dart::vm_isolate(), &size, &capacity);
797 OS::PrintErr("VM Isolate: Number of symbols : %" Pd "\n", size);
798 OS::PrintErr("VM Isolate: Symbol table capacity : %" Pd "\n", capacity);
799 // Now dump regular isolate symbol table stats.
800 GetStats(isolate, &size, &capacity);
801 OS::PrintErr("Isolate: Number of symbols : %" Pd "\n", size);
802 OS::PrintErr("Isolate: Symbol table capacity : %" Pd "\n", capacity);
803 // TODO(koda): Consider recording growth and collision stats in HashTable,
804 // in DEBUG mode.
805}
806
807void Symbols::DumpTable(Isolate* isolate) {
808 OS::PrintErr("symbols:\n");
809 SymbolTable table(isolate->object_store()->symbol_table());
810 table.Dump();
811 table.Release();
812}
813
814intptr_t Symbols::LookupPredefinedSymbol(ObjectPtr obj) {
815 for (intptr_t i = 1; i < Symbols::kMaxPredefinedId; i++) {
816 if (symbol_handles_[i]->raw() == obj) {
817 return (i + kMaxPredefinedObjectIds);
818 }
819 }
820 return kInvalidIndex;
821}
822
823ObjectPtr Symbols::GetPredefinedSymbol(intptr_t object_id) {
824 ASSERT(IsPredefinedSymbolId(object_id));
825 intptr_t i = (object_id - kMaxPredefinedObjectIds);
826 if ((i > kIllegal) && (i < Symbols::kMaxPredefinedId)) {
827 return symbol_handles_[i]->raw();
828 }
829 return Object::null();
830}
831
832} // namespace dart
833