v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
js-struct.cc
Go to the documentation of this file.
1// Copyright 2023 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
12
13namespace v8 {
14namespace internal {
15
16namespace {
17
18void PrepareMapCommon(Tagged<Map> map) {
19 DCHECK(IsAlwaysSharedSpaceJSObjectMap(map));
21 // Shared objects have fixed layout ahead of time, so there's no slack.
22 map->SetInObjectUnusedPropertyFields(0);
23 // Shared objects are not extensible and have a null prototype.
24 map->set_is_extensible(false);
25 // Shared space objects are not optimizable as prototypes because it is
26 // not threadsafe.
27 map->set_prototype_validity_cell(Map::kPrototypeChainValidSmi, kRelaxedStore,
29}
30
31} // namespace
32
33// static
35 Tagged<Map> map) {
36 PrepareMapCommon(map);
37 map->SetEnumLength(0);
38}
39
40// static
42 Isolate* isolate, Tagged<Map> map, Tagged<DescriptorArray> descriptors) {
43 PrepareMapCommon(map);
44 map->InitializeDescriptors(isolate, *descriptors);
45 DCHECK_EQ(0, map->NumberOfEnumerableProperties());
46 map->SetEnumLength(0);
47}
48
49// static
51 Isolate* isolate, DirectHandle<Map> map,
52 DirectHandle<DescriptorArray> descriptors, int enum_length) {
53 PrepareMapCommon(*map);
54 // Shared objects with enumerable own properties need to pre-create the enum
55 // cache, as creating it lazily is racy.
56 map->InitializeDescriptors(isolate, *descriptors);
58 isolate, map, enum_length, AllocationType::kSharedOld);
59 DCHECK_EQ(enum_length, map->EnumLength());
60}
61
62// static
66 Maybe<ShouldThrow> should_throw) {
67 // Shared objects are designed to have fixed layout, i.e. their maps are
68 // effectively immutable. They are constructed seal, but the semantics of
69 // ordinary ECMAScript objects allow writable properties to be upgraded to
70 // non-writable properties. This upgrade violates the fixed layout invariant
71 // and is disallowed.
72
73 DCHECK(IsName(*key) || IsNumber(*key)); // |key| is a PropertyKey.
74 PropertyKey lookup_key(isolate, key);
75 LookupIterator it(isolate, shared_obj, lookup_key, LookupIterator::OWN);
77 MAYBE_RETURN(GetOwnPropertyDescriptor(&it, &current), Nothing<bool>());
78
79 // The only redefinition allowed is to set the value if all attributes match.
80 if (!it.IsFound() ||
83 desc->ToAttributes() != current.ToAttributes()) {
84 DCHECK(!shared_obj->map()->is_extensible());
85 RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
86 NewTypeError(MessageTemplate::kDefineDisallowedFixedLayout,
87 it.GetName()));
88 }
89 DCHECK(it.property_attributes() == desc->ToAttributes());
90 if (desc->has_value()) {
91 return Object::SetDataProperty(&it, desc->value());
92 }
93 return Just(true);
94}
95
97 Isolate* isolate, DirectHandle<JSFunction> constructor,
98 DirectHandle<Object> object) {
99 if (!constructor->has_prototype_slot() || !constructor->has_initial_map() ||
100 !IsJSReceiver(*object)) {
101 return Just(false);
102 }
103 DirectHandle<Map> constructor_map(constructor->initial_map(), isolate);
105 DirectHandle<Map> current_map;
106 while (true) {
107 current_map =
109 if (current_map.is_identical_to(constructor_map)) {
110 return Just(true);
111 }
112 if (!iter.AdvanceFollowingProxies()) return Nothing<bool>();
113 if (iter.IsAtEnd()) return Just(false);
114 }
115}
116
117namespace {
118
119// Currently there are 2, both optionally present:
120// - Registry key
121// - Elements template
122constexpr int kSpecialSlots = 2;
123
124InternalIndex GetSpecialSlotIndex(Tagged<Map> instance_map,
125 Tagged<Symbol> special_slot_name) {
126 DCHECK(IsJSSharedStructMap(instance_map));
127 DCHECK(IsPrivateSymbol(special_slot_name));
128 Tagged<DescriptorArray> descriptors = instance_map->instance_descriptors();
129 // Special slots are optional and start at descriptor number 0.
130 int end = std::min(static_cast<int>(descriptors->number_of_all_descriptors()),
131 kSpecialSlots);
132 for (int i = 0; i < end; ++i) {
133 InternalIndex idx(i);
134 if (descriptors->GetKey(idx) == special_slot_name) {
136 descriptors->GetDetails(idx).location());
137 return idx;
138 }
139 }
141}
142
143template <typename T>
144MaybeHandle<T> GetSpecialSlotValue(Isolate* isolate, Tagged<Map> instance_map,
145 Tagged<Symbol> special_slot_name) {
147 MaybeHandle<T> result;
148 InternalIndex entry = GetSpecialSlotIndex(instance_map, special_slot_name);
149 if (entry.is_found()) {
151 special_slot_name ==
152 ReadOnlyRoots(isolate).shared_struct_map_registry_key_symbol(),
153 entry.as_int() == 0);
154 result =
155 handle(Cast<T>(instance_map->instance_descriptors()->GetStrongValue(
156 isolate, entry)),
157 isolate);
158 }
159 return result;
160}
161
162} // namespace
163
164// static
166 Isolate* isolate, const base::Vector<const DirectHandle<Name>> field_names,
167 const std::set<uint32_t>& element_names,
168 MaybeDirectHandle<String> maybe_registry_key) {
169 auto* factory = isolate->factory();
170
171 int num_fields = 0;
172 int num_elements = 0;
173
174 int num_descriptors = static_cast<int>(field_names.size());
175 // If there are elements, an template NumberDictionary is created and stored
176 // as a data constant on a descriptor.
177 if (!element_names.empty()) num_descriptors++;
178 // If this is a registered map, the key is stored as a data constant on a
179 // descriptor because the registry stores the maps weakly. Storing the key in
180 // the map simplifies the weakness handling in the GC.
181 if (!maybe_registry_key.is_null()) num_descriptors++;
182
183 // Create the DescriptorArray if there are fields or elements.
185 if (num_descriptors != 0) {
186 descriptors = factory->NewDescriptorArray(num_descriptors, 0,
188
189 int special_slots = 0;
190
191 // Store the registry key if the map is registered. This must be the first
192 // slot if present. The registry depends on this for rehashing.
193 DirectHandle<String> registry_key;
194 if (maybe_registry_key.ToHandle(&registry_key)) {
196 factory->shared_struct_map_registry_key_symbol(), registry_key,
198 DCHECK_EQ(0, special_slots);
199 descriptors->Set(InternalIndex(special_slots++), &d);
200 }
201
202 // Elements in shared structs are only supported as a dictionary. Create the
203 // template NumberDictionary if needed.
204 if (!element_names.empty()) {
205 DirectHandle<NumberDictionary> elements_template;
206 num_elements = static_cast<int>(element_names.size());
207 elements_template = NumberDictionary::New(isolate, num_elements,
209 for (uint32_t index : element_names) {
212 NumberDictionary::UncheckedAdd<Isolate, DirectHandle,
214 isolate, elements_template, index,
215 isolate->factory()->undefined_value(), details);
216 }
217 elements_template->SetInitialNumberOfElements(num_elements);
218 DCHECK(HeapLayout::InAnySharedSpace(*elements_template));
219
221 factory->shared_struct_map_elements_template_symbol(),
222 elements_template, ALL_ATTRIBUTES_MASK);
223 descriptors->Set(InternalIndex(special_slots++), &d);
224 }
225
226 DCHECK_LE(special_slots, kSpecialSlots);
227
228 for (DirectHandle<Name> field_name : field_names) {
229 // Shared structs' fields need to be aligned, so make it all tagged.
230 PropertyDetails details(
233 descriptors->Set(InternalIndex(special_slots + num_fields), *field_name,
234 FieldType::Any(), details);
235 num_fields++;
236 }
237
238 descriptors->Sort();
239 }
240
241 // Calculate the size for instances and create the map.
242 int instance_size;
243 int in_object_properties;
244 JSFunction::CalculateInstanceSizeHelper(JS_SHARED_STRUCT_TYPE, false, 0,
245 num_fields, &instance_size,
246 &in_object_properties);
247 DirectHandle<Map> instance_map = factory->NewContextlessMap(
248 JS_SHARED_STRUCT_TYPE, instance_size, DICTIONARY_ELEMENTS,
249 in_object_properties, AllocationType::kSharedMap);
250
251 // Prepare the enum cache if necessary.
252 if (num_descriptors == 0) {
253 DCHECK_EQ(0, num_fields);
254 // No properties at all.
256 } else if (num_fields == 0) {
257 // Have descriptors, but no enumerable fields.
259 isolate, *instance_map, *descriptors);
260 } else {
261 // Have enumerable fields.
263 isolate, instance_map, descriptors, num_fields);
264 }
265
266 // Structs have fixed layout ahead of time, so there's no slack.
267 int out_of_object_properties = num_fields - in_object_properties;
268 if (out_of_object_properties != 0) {
269 instance_map->SetOutOfObjectUnusedPropertyFields(0);
270 }
271
272 return instance_map;
273}
274
275// static
277 Tagged<Map> instance_map) {
278 return GetSpecialSlotValue<String>(
279 isolate, *instance_map,
280 ReadOnlyRoots(isolate).shared_struct_map_registry_key_symbol());
281}
282
283// static
285 Tagged<Map> instance_map,
287 DCHECK(IsJSSharedStructMap(instance_map));
288 return instance_map->instance_descriptors(isolate)->GetKey(i) ==
289 ReadOnlyRoots(isolate).shared_struct_map_registry_key_symbol();
290}
291
292// static
294 Isolate* isolate, Tagged<Map> instance_map) {
295 return GetSpecialSlotValue<NumberDictionary>(
296 isolate, instance_map,
297 ReadOnlyRoots(isolate).shared_struct_map_elements_template_symbol());
298}
299
300// static
302 Tagged<Map> instance_map,
304 DCHECK(IsJSSharedStructMap(instance_map));
305 return instance_map->instance_descriptors(isolate)->GetKey(i) ==
306 ReadOnlyRoots(isolate).shared_struct_map_elements_template_symbol();
307}
308
309// Hash table mapping string keys to shared struct maps.
311 public:
312 static constexpr int kEntrySize = 1;
313 static constexpr int kMaxEmptyFactor = 4;
314 static constexpr int kMinCapacity = 4;
315
316 explicit Data(int capacity) : OffHeapHashTableBase<Data>(capacity) {}
317
318 static uint32_t Hash(PtrComprCageBase cage_base, Tagged<Object> key) {
319 // Registry keys, if present, store them at the first descriptor. All maps
320 // in the registry have registry keys.
321 return Cast<String>(
322 Cast<Map>(key)->instance_descriptors(cage_base)->GetStrongValue(
323 InternalIndex(0)))
324 ->hash();
325 }
326
327 template <typename IsolateT>
328 static bool KeyIsMatch(IsolateT* isolate, DirectHandle<String> key,
329 Tagged<Object> obj) {
330 DirectHandle<String> existing =
332 .ToHandleChecked();
333 DCHECK(IsInternalizedString(*key));
334 DCHECK(IsInternalizedString(*existing));
335 return *key == *existing;
336 }
337
339 return slot(index).load(cage_base);
340 }
341
343 DCHECK(IsMap(key));
344 slot(index).store(key);
345 }
346 void Set(InternalIndex index, Tagged<Map> map) { SetKey(index, map); }
347
349 InternalIndex from_index, Data* to,
350 InternalIndex to_index) {
351 // Do nothing, since kEntrySize is 1.
352 }
353
354 static std::unique_ptr<Data> New(int capacity) {
355 return std::unique_ptr<Data>(new (capacity) Data(capacity));
356 }
357
358 void* operator new(size_t size, int capacity) {
359 DCHECK_GE(capacity, kMinCapacity);
360 DCHECK_EQ(size, sizeof(Data));
362 offsetof(Data, elements_)>(
363 capacity);
364 }
365 void* operator new(size_t size) = delete;
366
367 void operator delete(void* table) { OffHeapHashTableBase<Data>::Free(table); }
368};
369
374
376
379 const base::Vector<const DirectHandle<Name>> field_names,
380 const std::set<uint32_t>& element_names) {
381 Tagged<Map> existing_map = Cast<Map>(data_->GetKey(isolate, entry));
382
383 // A map is considered a match iff all of the following hold:
384 // - field names are the same element-wise (in order)
385 // - element indices are the same
386
387 // Registered types always have the key as the first descriptor.
388 DCHECK_EQ(
389 *JSSharedStruct::GetRegistryKey(isolate, existing_map).ToHandleChecked(),
390 *key);
391
392 int num_descriptors = static_cast<int>(field_names.size()) + 1;
393 if (!element_names.empty()) {
394 if (JSSharedStruct::GetElementsTemplate(isolate, existing_map).is_null()) {
395 return MaybeDirectHandle<Map>();
396 }
397 num_descriptors++;
398 }
399
400 if (num_descriptors != existing_map->NumberOfOwnDescriptors()) {
401 return MaybeDirectHandle<Map>();
402 }
403
404 Tagged<DescriptorArray> existing_descriptors =
405 existing_map->instance_descriptors(isolate);
406 auto field_names_iter = field_names.begin();
407 for (InternalIndex i : existing_map->IterateOwnDescriptors()) {
408 if (JSSharedStruct::IsElementsTemplateDescriptor(isolate, existing_map,
409 i)) {
410 DirectHandle<NumberDictionary> elements_template(
412 existing_map->instance_descriptors()->GetStrongValue(isolate, i)),
413 isolate);
414 if (static_cast<int>(element_names.size()) !=
415 elements_template->NumberOfElements()) {
416 return MaybeDirectHandle<Map>();
417 }
418 for (int element : element_names) {
419 if (elements_template->FindEntry(isolate, element).is_not_found()) {
420 return MaybeDirectHandle<Map>();
421 }
422 }
423
424 continue;
425 }
426
427 if (JSSharedStruct::IsRegistryKeyDescriptor(isolate, existing_map, i)) {
428 continue;
429 }
430
431 Tagged<Name> existing_name = existing_descriptors->GetKey(i);
432 DCHECK(IsUniqueName(existing_name));
433 Tagged<Name> name = **field_names_iter;
434 DCHECK(IsUniqueName(name));
435 if (name != existing_name) return MaybeDirectHandle<Map>();
436 ++field_names_iter;
437 }
438
439 return direct_handle(existing_map, isolate);
440}
441
443 Isolate* isolate, Handle<String> key,
444 const base::Vector<const DirectHandle<Name>> field_names,
445 const std::set<uint32_t>& element_names) {
446 key = isolate->factory()->InternalizeString(key);
447
448 // To avoid deadlock with iteration during GC and modifying the table, no GC
449 // must occur under lock.
450
451 {
453 InternalIndex entry = data_->FindEntry(isolate, key, key->hash());
454 if (entry.is_found()) {
455 return CheckIfEntryMatches(isolate, entry, key, field_names,
456 element_names);
457 }
458 }
459
460 // We have a likely miss. Create a new instance map outside of the lock.
462 isolate, field_names, element_names, key);
463
464 // Relookup to see if it's in fact a miss.
466
467 EnsureCapacity(isolate, 1);
468 InternalIndex entry =
469 data_->FindEntryOrInsertionEntry(isolate, key, key->hash());
470 Tagged<Object> existing_key = data_->GetKey(isolate, entry);
471 if (existing_key == Data::empty_element()) {
472 data_->AddAt(isolate, entry, *map);
473 return map;
474 } else if (existing_key == Data::deleted_element()) {
475 data_->OverwriteDeletedAt(isolate, entry, *map);
476 return map;
477 } else {
478 // An entry with the same key was inserted between the two locks.
479 return CheckIfEntryMatches(isolate, entry, key, field_names, element_names);
480 }
481}
482
484 Isolate* isolate, Handle<String> key,
485 const base::Vector<const DirectHandle<Name>> field_names,
486 const std::set<uint32_t>& element_names) {
487 MaybeDirectHandle<Map> canonical_map =
488 RegisterNoThrow(isolate, key, field_names, element_names);
489 if (canonical_map.is_null()) {
491 isolate,
492 NewTypeError(MessageTemplate::kSharedStructTypeRegistryMismatch, key));
493 }
494 return canonical_map;
495}
496
498 RootVisitor* visitor) {
499 // Ideally this should only happen during a global safepoint, when all
500 // workers and background threads are paused, so there would be no need to
501 // take the data mutex. However, the array left trimming has a verifier
502 // visitor that visits all roots (including weak ones), thus we take the
503 // mutex.
504 //
505 // TODO(v8:12547): Figure out how to do
506 // isolate->global_safepoint()->AssertActive() instead.
507 base::MutexGuard data_guard(&data_mutex_);
508 data_->IterateElements(Root::kSharedStructTypeRegistry, visitor);
509}
510
512 data_->ElementsRemoved(count);
513}
514
516 int additional_elements) {
518
519 int new_capacity;
520 if (data_->ShouldResizeToAdd(additional_elements, &new_capacity)) {
521 std::unique_ptr<Data> new_data(Data::New(new_capacity));
522 data_->RehashInto(cage_base, new_data.get());
523 data_ = std::move(new_data);
524 }
525}
526
527} // namespace internal
528} // namespace v8
uint8_t data_[MAX_STACK_LENGTH]
V8_INLINE void AssertHeld() const
Definition mutex.h:58
static void PrepareMapNoEnumerableProperties(Tagged< Map > map)
Definition js-struct.cc:34
static void PrepareMapWithEnumerableProperties(Isolate *isolate, DirectHandle< Map > map, DirectHandle< DescriptorArray > descriptors, int enum_length)
Definition js-struct.cc:50
static V8_WARN_UNUSED_RESULT Maybe< bool > DefineOwnProperty(Isolate *isolate, DirectHandle< AlwaysSharedSpaceJSObject > shared_obj, DirectHandle< Object > key, PropertyDescriptor *desc, Maybe< ShouldThrow > should_throw)
Definition js-struct.cc:63
static Maybe< bool > HasInstance(Isolate *isolate, DirectHandle< JSFunction > constructor, DirectHandle< Object > object)
Definition js-struct.cc:96
static Descriptor DataConstant(DirectHandle< Name > key, DirectHandle< Object > value, PropertyAttributes attributes)
Definition property.cc:100
V8_INLINE bool is_identical_to(Handle< S > other) const
Definition handles.h:716
static Handle< FixedArray > InitializeFastPropertyEnumCache(Isolate *isolate, DirectHandle< Map > map, int enum_length, AllocationType allocation=AllocationType::kOld)
Definition keys.cc:513
static V8_EXPORT_PRIVATE Tagged< FieldType > Any()
Definition field-type.cc:22
Tagged< Object > load() const
Definition slots-inl.h:48
void store(Tagged< Object > value) const
Definition slots-inl.h:54
static V8_INLINE bool InAnySharedSpace(Tagged< HeapObject > object)
static InternalIndex NotFound()
static void CalculateInstanceSizeHelper(InstanceType instance_type, bool has_prototype_slot, int requested_embedder_fields, int requested_in_object_properties, int *instance_size, int *in_object_properties)
static bool IsRegistryKeyDescriptor(Isolate *isolate, Tagged< Map > instance_map, InternalIndex i)
Definition js-struct.cc:284
static MaybeHandle< String > GetRegistryKey(Isolate *isolate, Tagged< Map > instance_map)
Definition js-struct.cc:276
static MaybeDirectHandle< NumberDictionary > GetElementsTemplate(Isolate *isolate, Tagged< Map > instance_map)
Definition js-struct.cc:293
static DirectHandle< Map > CreateInstanceMap(Isolate *isolate, const base::Vector< const DirectHandle< Name > > field_names, const std::set< uint32_t > &element_names, MaybeDirectHandle< String > maybe_registry_key)
Definition js-struct.cc:165
static bool IsElementsTemplateDescriptor(Isolate *isolate, Tagged< Map > instance_map, InternalIndex i)
Definition js-struct.cc:301
static constexpr Tagged< Smi > kPrototypeChainValidSmi
Definition map.h:519
V8_WARN_UNUSED_RESULT V8_INLINE bool ToHandle(DirectHandle< S > *out) const
V8_INLINE bool is_null() const
static V8_WARN_UNUSED_RESULT Maybe< bool > SetDataProperty(LookupIterator *it, DirectHandle< Object > value)
Definition objects.cc:2604
static constexpr Tagged< Smi > deleted_element()
OffHeapObjectSlot slot(InternalIndex index, int offset=0) const
static constexpr Tagged< Smi > empty_element()
static bool IsDataDescriptor(PropertyDescriptor *desc)
V8_WARN_UNUSED_RESULT bool AdvanceFollowingProxies()
Tagged< T > GetCurrent() const
Definition prototype.h:52
static constexpr Representation Tagged()
Tagged< Object > GetKey(PtrComprCageBase cage_base, InternalIndex index) const
Definition js-struct.cc:338
void CopyEntryExcludingKeyInto(PtrComprCageBase cage_base, InternalIndex from_index, Data *to, InternalIndex to_index)
Definition js-struct.cc:348
void SetKey(InternalIndex index, Tagged< Object > key)
Definition js-struct.cc:342
static std::unique_ptr< Data > New(int capacity)
Definition js-struct.cc:354
static uint32_t Hash(PtrComprCageBase cage_base, Tagged< Object > key)
Definition js-struct.cc:318
static bool KeyIsMatch(IsolateT *isolate, DirectHandle< String > key, Tagged< Object > obj)
Definition js-struct.cc:328
void Set(InternalIndex index, Tagged< Map > map)
Definition js-struct.cc:346
MaybeDirectHandle< Map > RegisterNoThrow(Isolate *isolate, Handle< String > key, const base::Vector< const DirectHandle< Name > > field_names, const std::set< uint32_t > &element_names)
Definition js-struct.cc:442
std::unique_ptr< Data > data_
Definition js-struct.h:109
void IterateElements(Isolate *isolate, RootVisitor *visitor)
Definition js-struct.cc:497
MaybeDirectHandle< Map > Register(Isolate *isolate, Handle< String > key, const base::Vector< const DirectHandle< Name > > field_names, const std::set< uint32_t > &element_names)
Definition js-struct.cc:483
void EnsureCapacity(PtrComprCageBase cage_base, int additional_elements)
Definition js-struct.cc:515
MaybeDirectHandle< Map > CheckIfEntryMatches(Isolate *isolate, InternalIndex entry, DirectHandle< String > key, const base::Vector< const DirectHandle< Name > > field_names, const std::set< uint32_t > &element_names)
Definition js-struct.cc:377
static constexpr Tagged< Smi > deleted_element()
Definition js-struct.h:81
int end
LineAndColumn current
#define RETURN_FAILURE(isolate, should_throw, call)
Definition isolate.h:398
#define THROW_NEW_ERROR(isolate, call)
Definition isolate.h:307
#define MAYBE_RETURN(call, value)
Definition isolate.h:408
Isolate * isolate
std::map< const std::string, const std::string > map
ZoneVector< RpoNumber > & result
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
@ SKIP_WRITE_BARRIER
Definition objects.h:52
bool IsNumber(Tagged< Object > obj)
Tagged(T object) -> Tagged< T >
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
return Cast< NumberDictionary >(elements(cage_base))
instance_descriptors
Definition map-inl.h:52
ShouldThrow GetShouldThrow(Isolate *isolate, Maybe< ShouldThrow > should_throw)
Definition objects.cc:140
bool IsUniqueName(Tagged< Name > obj)
bool IsPrivateSymbol(Tagged< Object > obj)
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Maybe< T > Nothing()
Definition v8-maybe.h:112
static constexpr RelaxedStoreTag kRelaxedStore
Definition globals.h:2911
Maybe< T > Just(const T &t)
Definition v8-maybe.h:117
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485