Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: src/elements.cc

Issue 1707743002: [key-accumulator] Starting to reimplement the key-accumulator (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: use proper type Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/elements.h ('k') | src/key-accumulator.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/elements.h" 5 #include "src/elements.h"
6 6
7 #include "src/arguments.h" 7 #include "src/arguments.h"
8 #include "src/conversions.h" 8 #include "src/conversions.h"
9 #include "src/factory.h" 9 #include "src/factory.h"
10 #include "src/messages.h" 10 #include "src/messages.h"
(...skipping 410 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 for (int i = 0; i < copy_size; i++) { 421 for (int i = 0; i < copy_size; i++) {
422 int entry = from->FindEntry(i + from_start); 422 int entry = from->FindEntry(i + from_start);
423 if (entry != SeededNumberDictionary::kNotFound) { 423 if (entry != SeededNumberDictionary::kNotFound) {
424 to->set(i + to_start, from->ValueAt(entry)->Number()); 424 to->set(i + to_start, from->ValueAt(entry)->Number());
425 } else { 425 } else {
426 to->set_the_hole(i + to_start); 426 to->set_the_hole(i + to_start);
427 } 427 }
428 } 428 }
429 } 429 }
430 430
431
432 static void TraceTopFrame(Isolate* isolate) { 431 static void TraceTopFrame(Isolate* isolate) {
433 StackFrameIterator it(isolate); 432 StackFrameIterator it(isolate);
434 if (it.done()) { 433 if (it.done()) {
435 PrintF("unknown location (no JavaScript frames present)"); 434 PrintF("unknown location (no JavaScript frames present)");
436 return; 435 return;
437 } 436 }
438 StackFrame* raw_frame = it.frame(); 437 StackFrame* raw_frame = it.frame();
439 if (raw_frame->is_internal()) { 438 if (raw_frame->is_internal()) {
440 Code* apply_builtin = 439 Code* apply_builtin =
441 isolate->builtins()->builtin(Builtins::kFunctionPrototypeApply); 440 isolate->builtins()->builtin(Builtins::kFunctionPrototypeApply);
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after
723 } else { 722 } else {
724 // Check whether the backing store should be expanded. 723 // Check whether the backing store should be expanded.
725 capacity = Max(length, JSObject::NewElementsCapacity(capacity)); 724 capacity = Max(length, JSObject::NewElementsCapacity(capacity));
726 ElementsAccessorSubclass::GrowCapacityAndConvertImpl(array, capacity); 725 ElementsAccessorSubclass::GrowCapacityAndConvertImpl(array, capacity);
727 } 726 }
728 727
729 array->set_length(Smi::FromInt(length)); 728 array->set_length(Smi::FromInt(length));
730 JSObject::ValidateElements(array); 729 JSObject::ValidateElements(array);
731 } 730 }
732 731
732 static uint32_t GetIterationLength(JSObject* receiver,
733 FixedArrayBase* elements) {
734 if (receiver->IsJSArray()) {
735 return static_cast<uint32_t>(
736 Smi::cast(JSArray::cast(receiver)->length())->value());
737 } else {
738 return ElementsAccessorSubclass::GetCapacityImpl(receiver, elements);
739 }
740 }
741
733 static Handle<FixedArrayBase> ConvertElementsWithCapacity( 742 static Handle<FixedArrayBase> ConvertElementsWithCapacity(
734 Handle<JSObject> object, Handle<FixedArrayBase> old_elements, 743 Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
735 ElementsKind from_kind, uint32_t capacity) { 744 ElementsKind from_kind, uint32_t capacity) {
736 return ConvertElementsWithCapacity( 745 return ConvertElementsWithCapacity(
737 object, old_elements, from_kind, capacity, 0, 0, 746 object, old_elements, from_kind, capacity, 0, 0,
738 ElementsAccessor::kCopyToEndAndInitializeToHole); 747 ElementsAccessor::kCopyToEndAndInitializeToHole);
739 } 748 }
740 749
741 static Handle<FixedArrayBase> ConvertElementsWithCapacity( 750 static Handle<FixedArrayBase> ConvertElementsWithCapacity(
742 Handle<JSObject> object, Handle<FixedArrayBase> old_elements, 751 Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
839 // intentionally to avoid ArrayConcat() builtin performance degradation. 848 // intentionally to avoid ArrayConcat() builtin performance degradation.
840 // 849 //
841 // Details: The idea is that allocations actually happen only in case of 850 // Details: The idea is that allocations actually happen only in case of
842 // copying from object with fast double elements to object with object 851 // copying from object with fast double elements to object with object
843 // elements. In all the other cases there are no allocations performed and 852 // elements. In all the other cases there are no allocations performed and
844 // handle creation causes noticeable performance degradation of the builtin. 853 // handle creation causes noticeable performance degradation of the builtin.
845 ElementsAccessorSubclass::CopyElementsImpl( 854 ElementsAccessorSubclass::CopyElementsImpl(
846 from, from_start, *to, from_kind, to_start, packed_size, copy_size); 855 from, from_start, *to, from_kind, to_start, packed_size, copy_size);
847 } 856 }
848 857
858 void CollectElementIndices(Handle<JSObject> object,
859 Handle<FixedArrayBase> backing_store,
860 KeyAccumulator* keys, uint32_t range,
861 PropertyFilter filter, uint32_t offset) final {
862 ElementsAccessorSubclass::CollectElementIndicesImpl(
863 object, backing_store, keys, range, filter, offset);
864 }
865
849 static void CollectElementIndicesImpl(Handle<JSObject> object, 866 static void CollectElementIndicesImpl(Handle<JSObject> object,
850 Handle<FixedArrayBase> backing_store, 867 Handle<FixedArrayBase> backing_store,
851 KeyAccumulator* keys, uint32_t range, 868 KeyAccumulator* keys, uint32_t range,
852 PropertyFilter filter, 869 PropertyFilter filter,
853 uint32_t offset) { 870 uint32_t offset) {
854 DCHECK_NE(DICTIONARY_ELEMENTS, kind()); 871 DCHECK_NE(DICTIONARY_ELEMENTS, kind());
855 if (filter & ONLY_ALL_CAN_READ) { 872 if (filter & ONLY_ALL_CAN_READ) {
856 // Non-dictionary elements can't have all-can-read accessors. 873 // Non-dictionary elements can't have all-can-read accessors.
857 return; 874 return;
858 } 875 }
859 uint32_t length = 0; 876 uint32_t length = GetIterationLength(*object, *backing_store);
860 if (object->IsJSArray()) {
861 length = Smi::cast(JSArray::cast(*object)->length())->value();
862 } else {
863 length =
864 ElementsAccessorSubclass::GetCapacityImpl(*object, *backing_store);
865 }
866 if (range < length) length = range; 877 if (range < length) length = range;
867 for (uint32_t i = offset; i < length; i++) { 878 for (uint32_t i = offset; i < length; i++) {
868 if (!ElementsAccessorSubclass::HasElementImpl(object, i, backing_store, 879 if (ElementsAccessorSubclass::HasElementImpl(object, i, backing_store,
869 filter)) { 880 filter)) {
870 continue; 881 keys->AddKey(i);
871 } 882 }
872 keys->AddKey(i);
873 } 883 }
874 } 884 }
875 885
876 void CollectElementIndices(Handle<JSObject> object, 886 static Handle<FixedArray> DirectCollectElementIndicesImpl(
877 Handle<FixedArrayBase> backing_store, 887 Isolate* isolate, Handle<JSObject> object,
878 KeyAccumulator* keys, uint32_t range, 888 Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
879 PropertyFilter filter, uint32_t offset) final { 889 PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices) {
880 ElementsAccessorSubclass::CollectElementIndicesImpl( 890 uint32_t length =
881 object, backing_store, keys, range, filter, offset); 891 ElementsAccessorSubclass::GetIterationLength(*object, *backing_store);
882 }; 892 uint32_t insertion_index = 0;
893 for (uint32_t i = 0; i < length; i++) {
894 if (ElementsAccessorSubclass::HasElementImpl(object, i, backing_store,
895 filter)) {
896 if (convert == CONVERT_TO_STRING) {
897 Handle<String> index_string = isolate->factory()->Uint32ToString(i);
898 list->set(insertion_index, *index_string);
899 } else {
900 list->set(insertion_index, Smi::FromInt(i), SKIP_WRITE_BARRIER);
901 }
902 insertion_index++;
903 }
904 }
905 *nof_indices = insertion_index;
906 return list;
907 }
908
909 Handle<FixedArray> PrependElementIndices(Handle<JSObject> object,
910 Handle<FixedArrayBase> backing_store,
911 Handle<FixedArray> keys,
912 GetKeysConversion convert,
913 PropertyFilter filter) final {
914 return ElementsAccessorSubclass::PrependElementIndicesImpl(
915 object, backing_store, keys, convert, filter);
916 }
917
918 static Handle<FixedArray> PrependElementIndicesImpl(
919 Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
920 Handle<FixedArray> keys, GetKeysConversion convert,
921 PropertyFilter filter) {
922 Isolate* isolate = object->GetIsolate();
923 uint32_t nof_property_keys = keys->length();
924 uint32_t initial_list_length =
925 ElementsAccessorSubclass::GetCapacityImpl(*object, *backing_store);
926 initial_list_length += nof_property_keys;
927
928 // Collect the element indices into a new list.
929 uint32_t nof_indices = 0;
930 Handle<FixedArray> combined_keys =
931 isolate->factory()->NewFixedArray(initial_list_length);
932 combined_keys = ElementsAccessorSubclass::DirectCollectElementIndicesImpl(
933 isolate, object, backing_store, convert, filter, combined_keys,
934 &nof_indices);
935
936 // Sort the indices list if necessary.
937 if (IsDictionaryElementsKind(kind())) {
938 struct {
939 bool operator()(Object* a, Object* b) {
940 if (!a->IsUndefined()) {
941 if (b->IsUndefined()) return true;
942 return a->Number() < b->Number();
943 }
944 return !b->IsUndefined();
945 }
946 } cmp;
947 Object** start =
948 reinterpret_cast<Object**>(combined_keys->GetFirstElementAddress());
949 std::sort(start, start + nof_indices, cmp);
950 // Indices from dictionary elements should only be converted after
951 // sorting.
952 if (convert == CONVERT_TO_STRING) {
953 for (uint32_t i = 0; i < nof_indices; i++) {
954 Handle<Object> index_string = isolate->factory()->Uint32ToString(
955 combined_keys->get(i)->Number());
956 combined_keys->set(i, *index_string);
957 }
958 }
959 }
960
961 // Copy over the passed-in property keys.
962 CopyObjectToObjectElements(*keys, FAST_ELEMENTS, 0, *combined_keys,
963 FAST_ELEMENTS, nof_indices, nof_property_keys);
964
965 if (IsHoleyElementsKind(kind())) {
966 // Shrink combined_keys to the final size.
967 int final_size = nof_indices + nof_property_keys;
968 DCHECK_LE(final_size, combined_keys->length());
969 combined_keys->Shrink(final_size);
970 }
971
972 return combined_keys;
973 }
883 974
884 void AddElementsToKeyAccumulator(Handle<JSObject> receiver, 975 void AddElementsToKeyAccumulator(Handle<JSObject> receiver,
885 KeyAccumulator* accumulator, 976 KeyAccumulator* accumulator,
886 AddKeyConversion convert) final { 977 AddKeyConversion convert) final {
887 ElementsAccessorSubclass::AddElementsToKeyAccumulatorImpl( 978 ElementsAccessorSubclass::AddElementsToKeyAccumulatorImpl(
888 receiver, accumulator, convert); 979 receiver, accumulator, convert);
889 } 980 }
890 981
891 static uint32_t GetCapacityImpl(JSObject* holder, 982 static uint32_t GetCapacityImpl(JSObject* holder,
892 FixedArrayBase* backing_store) { 983 FixedArrayBase* backing_store) {
(...skipping 12 matching lines...) Expand all
905 static uint32_t GetEntryForIndexImpl(JSObject* holder, 996 static uint32_t GetEntryForIndexImpl(JSObject* holder,
906 FixedArrayBase* backing_store, 997 FixedArrayBase* backing_store,
907 uint32_t index, PropertyFilter filter) { 998 uint32_t index, PropertyFilter filter) {
908 if (IsHoleyElementsKind(kind())) { 999 if (IsHoleyElementsKind(kind())) {
909 return index < ElementsAccessorSubclass::GetCapacityImpl(holder, 1000 return index < ElementsAccessorSubclass::GetCapacityImpl(holder,
910 backing_store) && 1001 backing_store) &&
911 !BackingStore::cast(backing_store)->is_the_hole(index) 1002 !BackingStore::cast(backing_store)->is_the_hole(index)
912 ? index 1003 ? index
913 : kMaxUInt32; 1004 : kMaxUInt32;
914 } else { 1005 } else {
915 uint32_t length = 1006 uint32_t length = GetIterationLength(holder, backing_store);
916 holder->IsJSArray()
917 ? static_cast<uint32_t>(
918 Smi::cast(JSArray::cast(holder)->length())->value())
919 : ElementsAccessorSubclass::GetCapacityImpl(holder,
920 backing_store);
921 return index < length ? index : kMaxUInt32; 1007 return index < length ? index : kMaxUInt32;
922 } 1008 }
923 } 1009 }
924 1010
925 uint32_t GetEntryForIndex(JSObject* holder, FixedArrayBase* backing_store, 1011 uint32_t GetEntryForIndex(JSObject* holder, FixedArrayBase* backing_store,
926 uint32_t index) final { 1012 uint32_t index) final {
927 return ElementsAccessorSubclass::GetEntryForIndexImpl( 1013 return ElementsAccessorSubclass::GetEntryForIndexImpl(
928 holder, backing_store, index, ALL_PROPERTIES); 1014 holder, backing_store, index, ALL_PROPERTIES);
929 } 1015 }
930 1016
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
1112 1198
1113 static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) { 1199 static PropertyDetails GetDetailsImpl(JSObject* holder, uint32_t entry) {
1114 return GetDetailsImpl(holder->elements(), entry); 1200 return GetDetailsImpl(holder->elements(), entry);
1115 } 1201 }
1116 1202
1117 static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store, 1203 static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store,
1118 uint32_t entry) { 1204 uint32_t entry) {
1119 return SeededNumberDictionary::cast(backing_store)->DetailsAt(entry); 1205 return SeededNumberDictionary::cast(backing_store)->DetailsAt(entry);
1120 } 1206 }
1121 1207
1208 static uint32_t GetKeyForEntryImpl(Handle<SeededNumberDictionary> dictionary,
1209 int entry, PropertyFilter filter) {
1210 DisallowHeapAllocation no_gc;
1211 Object* raw_key = dictionary->KeyAt(entry);
1212 if (!dictionary->IsKey(raw_key)) return kMaxUInt32;
1213 if (raw_key->FilterKey(filter)) return kMaxUInt32;
1214 if (dictionary->IsDeleted(entry)) return kMaxUInt32;
1215 DCHECK(raw_key->IsNumber());
1216 DCHECK_LE(raw_key->Number(), kMaxUInt32);
1217 uint32_t key = static_cast<uint32_t>(raw_key->Number());
1218 PropertyDetails details = dictionary->DetailsAt(entry);
1219 if (filter & ONLY_ALL_CAN_READ) {
1220 if (details.kind() != kAccessor) return kMaxUInt32;
1221 Object* accessors = dictionary->ValueAt(entry);
1222 if (!accessors->IsAccessorInfo()) return kMaxUInt32;
1223 if (!AccessorInfo::cast(accessors)->all_can_read()) return kMaxUInt32;
1224 }
1225 PropertyAttributes attr = details.attributes();
1226 if ((attr & filter) != 0) return kMaxUInt32;
1227 return key;
1228 }
1229
1122 static void CollectElementIndicesImpl(Handle<JSObject> object, 1230 static void CollectElementIndicesImpl(Handle<JSObject> object,
1123 Handle<FixedArrayBase> backing_store, 1231 Handle<FixedArrayBase> backing_store,
1124 KeyAccumulator* keys, uint32_t range, 1232 KeyAccumulator* keys, uint32_t range,
1125 PropertyFilter filter, 1233 PropertyFilter filter,
1126 uint32_t offset) { 1234 uint32_t offset) {
1127 Handle<SeededNumberDictionary> dictionary = 1235 Handle<SeededNumberDictionary> dictionary =
1128 Handle<SeededNumberDictionary>::cast(backing_store); 1236 Handle<SeededNumberDictionary>::cast(backing_store);
1129 int capacity = dictionary->Capacity(); 1237 int capacity = dictionary->Capacity();
1130 for (int i = 0; i < capacity; i++) { 1238 for (int i = 0; i < capacity; i++) {
1131 Object* k = dictionary->KeyAt(i); 1239 uint32_t key = GetKeyForEntryImpl(dictionary, i, filter);
1132 if (!dictionary->IsKey(k)) continue; 1240 if (key == kMaxUInt32) continue;
1133 if (k->FilterKey(filter)) continue; 1241 keys->AddKey(key);
1134 if (dictionary->IsDeleted(i)) continue;
1135 DCHECK(k->IsNumber());
1136 DCHECK_LE(k->Number(), kMaxUInt32);
1137 uint32_t index = static_cast<uint32_t>(k->Number());
1138 if (index < offset) continue;
1139 PropertyDetails details = dictionary->DetailsAt(i);
1140 if (filter & ONLY_ALL_CAN_READ) {
1141 if (details.kind() != kAccessor) continue;
1142 Object* accessors = dictionary->ValueAt(i);
1143 if (!accessors->IsAccessorInfo()) continue;
1144 if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
1145 }
1146 PropertyAttributes attr = details.attributes();
1147 if ((attr & filter) != 0) continue;
1148 keys->AddKey(index);
1149 } 1242 }
1150 1243
1151 keys->SortCurrentElementsList(); 1244 keys->SortCurrentElementsList();
1152 } 1245 }
1153 1246
1247 static Handle<FixedArray> DirectCollectElementIndicesImpl(
1248 Isolate* isolate, Handle<JSObject> object,
1249 Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
1250 PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices) {
1251 Handle<SeededNumberDictionary> dictionary =
1252 Handle<SeededNumberDictionary>::cast(backing_store);
1253 uint32_t capacity = dictionary->Capacity();
1254 uint32_t insertion_index = 0;
1255 for (uint32_t i = 0; i < capacity; i++) {
1256 uint32_t key = GetKeyForEntryImpl(dictionary, i, filter);
1257 if (key == kMaxUInt32) continue;
1258 Handle<Object> index = isolate->factory()->NewNumberFromUint(key);
1259 list->set(insertion_index, *index);
1260 insertion_index++;
1261 }
1262 *nof_indices = insertion_index;
1263 return list;
1264 }
1265
1154 static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver, 1266 static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
1155 KeyAccumulator* accumulator, 1267 KeyAccumulator* accumulator,
1156 AddKeyConversion convert) { 1268 AddKeyConversion convert) {
1157 SeededNumberDictionary* dictionary = 1269 SeededNumberDictionary* dictionary =
1158 SeededNumberDictionary::cast(receiver->elements()); 1270 SeededNumberDictionary::cast(receiver->elements());
1159 int capacity = dictionary->Capacity(); 1271 int capacity = dictionary->Capacity();
1160 for (int i = 0; i < capacity; i++) { 1272 for (int i = 0; i < capacity; i++) {
1161 Object* k = dictionary->KeyAt(i); 1273 Object* k = dictionary->KeyAt(i);
1162 if (!dictionary->IsKey(k)) continue; 1274 if (!dictionary->IsKey(k)) continue;
1163 if (dictionary->IsDeleted(i)) continue; 1275 if (dictionary->IsDeleted(i)) continue;
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
1308 DeleteCommon(obj, entry, handle(obj->elements())); 1420 DeleteCommon(obj, entry, handle(obj->elements()));
1309 } 1421 }
1310 1422
1311 static bool HasEntryImpl(FixedArrayBase* backing_store, uint32_t entry) { 1423 static bool HasEntryImpl(FixedArrayBase* backing_store, uint32_t entry) {
1312 return !BackingStore::cast(backing_store)->is_the_hole(entry); 1424 return !BackingStore::cast(backing_store)->is_the_hole(entry);
1313 } 1425 }
1314 1426
1315 static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver, 1427 static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
1316 KeyAccumulator* accumulator, 1428 KeyAccumulator* accumulator,
1317 AddKeyConversion convert) { 1429 AddKeyConversion convert) {
1318 uint32_t length = 0;
1319 Handle<FixedArrayBase> elements(receiver->elements(), 1430 Handle<FixedArrayBase> elements(receiver->elements(),
1320 receiver->GetIsolate()); 1431 receiver->GetIsolate());
1321 if (receiver->IsJSArray()) { 1432 uint32_t length =
1322 length = Smi::cast(JSArray::cast(*receiver)->length())->value(); 1433 FastElementsAccessorSubclass::GetIterationLength(*receiver, *elements);
1323 } else {
1324 length =
1325 FastElementsAccessorSubclass::GetCapacityImpl(*receiver, *elements);
1326 }
1327 for (uint32_t i = 0; i < length; i++) { 1434 for (uint32_t i = 0; i < length; i++) {
1328 if (IsFastPackedElementsKind(KindTraits::Kind) || 1435 if (IsFastPackedElementsKind(KindTraits::Kind) ||
1329 HasEntryImpl(*elements, i)) { 1436 HasEntryImpl(*elements, i)) {
1330 accumulator->AddKey(FastElementsAccessorSubclass::GetImpl(*elements, i), 1437 accumulator->AddKey(FastElementsAccessorSubclass::GetImpl(*elements, i),
1331 convert); 1438 convert);
1332 } 1439 }
1333 } 1440 }
1334 } 1441 }
1335 1442
1336 static void ValidateContents(Handle<JSObject> holder, int length) { 1443 static void ValidateContents(Handle<JSObject> holder, int length) {
(...skipping 1343 matching lines...) Expand 10 before | Expand all | Expand 10 after
2680 } 2787 }
2681 } 2788 }
2682 2789
2683 DCHECK(j == result_len); 2790 DCHECK(j == result_len);
2684 return result_array; 2791 return result_array;
2685 } 2792 }
2686 2793
2687 ElementsAccessor** ElementsAccessor::elements_accessors_ = NULL; 2794 ElementsAccessor** ElementsAccessor::elements_accessors_ = NULL;
2688 } // namespace internal 2795 } // namespace internal
2689 } // namespace v8 2796 } // namespace v8
OLDNEW
« no previous file with comments | « src/elements.h ('k') | src/key-accumulator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698