1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
|
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_OBJECTS_DESCRIPTOR_ARRAY_H_
#define V8_OBJECTS_DESCRIPTOR_ARRAY_H_
#include "src/common/globals.h"
#include "src/objects/fixed-array.h"
// TODO(jkummerow): Consider forward-declaring instead.
#include "src/base/bit-field.h"
#include "src/objects/internal-index.h"
#include "src/objects/objects.h"
#include "src/objects/struct.h"
#include "src/utils/utils.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
class StructBodyDescriptor;
#include "torque-generated/src/objects/descriptor-array-tq.inc"
// An EnumCache is a pair used to hold keys and indices caches.
class EnumCache : public TorqueGeneratedEnumCache<EnumCache, Struct> {
public:
DECL_VERIFIER(EnumCache)
using BodyDescriptor = StructBodyDescriptor;
TQ_OBJECT_CONSTRUCTORS(EnumCache)
};
// A DescriptorArray is a custom array that holds instance descriptors.
// It has the following layout:
// Header:
// [16:0 bits]: number_of_all_descriptors (including slack)
// [32:16 bits]: number_of_descriptors
// [64:32 bits]: raw_gc_state (used by GC)
// [kEnumCacheOffset]: enum cache
// Elements:
// [kHeaderSize + 0]: first key (and internalized String)
// [kHeaderSize + 1]: first descriptor details (see PropertyDetails)
// [kHeaderSize + 2]: first value for constants / Smi(1) when not used
// Slack:
// [kHeaderSize + number of descriptors * 3]: start of slack
// The "value" fields store either values or field types. A field type is either
// FieldType::None(), FieldType::Any() or a weak reference to a Map. All other
// references are strong.
class DescriptorArray
: public TorqueGeneratedDescriptorArray<DescriptorArray, HeapObject> {
public:
DECL_INT16_ACCESSORS(number_of_all_descriptors)
DECL_INT16_ACCESSORS(number_of_descriptors)
inline int16_t number_of_slack_descriptors() const;
inline int number_of_entries() const;
void ClearEnumCache();
inline void CopyEnumCacheFrom(Tagged<DescriptorArray> array);
static void InitializeOrChangeEnumCache(
Handle<DescriptorArray> descriptors, Isolate* isolate,
Handle<FixedArray> keys, Handle<FixedArray> indices,
AllocationType allocation_if_initialize);
// Accessors for fetching instance descriptor at descriptor number.
inline Tagged<Name> GetKey(InternalIndex descriptor_number) const;
inline Tagged<Name> GetKey(PtrComprCageBase cage_base,
InternalIndex descriptor_number) const;
inline Tagged<Object> GetStrongValue(InternalIndex descriptor_number);
inline Tagged<Object> GetStrongValue(PtrComprCageBase cage_base,
InternalIndex descriptor_number);
inline MaybeObject GetValue(InternalIndex descriptor_number);
inline MaybeObject GetValue(PtrComprCageBase cage_base,
InternalIndex descriptor_number);
inline PropertyDetails GetDetails(InternalIndex descriptor_number);
inline int GetFieldIndex(InternalIndex descriptor_number);
inline Tagged<FieldType> GetFieldType(InternalIndex descriptor_number);
inline Tagged<FieldType> GetFieldType(PtrComprCageBase cage_base,
InternalIndex descriptor_number);
inline Tagged<Name> GetSortedKey(int descriptor_number);
inline Tagged<Name> GetSortedKey(PtrComprCageBase cage_base,
int descriptor_number);
inline int GetSortedKeyIndex(int descriptor_number);
// Accessor for complete descriptor.
inline void Set(InternalIndex descriptor_number, Descriptor* desc);
inline void Set(InternalIndex descriptor_number, Tagged<Name> key,
MaybeObject value, PropertyDetails details);
void Replace(InternalIndex descriptor_number, Descriptor* descriptor);
// Generalizes constness, representation and field type of all field
// descriptors.
void GeneralizeAllFields();
// Append automatically sets the enumeration index. This should only be used
// to add descriptors in bulk at the end, followed by sorting the descriptor
// array.
inline void Append(Descriptor* desc);
static Handle<DescriptorArray> CopyUpTo(Isolate* isolate,
Handle<DescriptorArray> desc,
int enumeration_index, int slack = 0);
static Handle<DescriptorArray> CopyUpToAddAttributes(
Isolate* isolate, Handle<DescriptorArray> desc, int enumeration_index,
PropertyAttributes attributes, int slack = 0);
// Sort the instance descriptors by the hash codes of their keys.
V8_EXPORT_PRIVATE void Sort();
// Iterate through Name hash collisions in the descriptor array starting from
// insertion index checking for Name collisions. Note: If we ever add binary
// insertion for large DescriptorArrays it would need to be hardened in a
// similar way. This function only expects to be called on Sorted
// DescriptorArrays.
V8_EXPORT_PRIVATE void CheckNameCollisionDuringInsertion(
Descriptor* desc, uint32_t descriptor_hash, int insertion_index);
// Search the instance descriptors for given name. {concurrent_search} signals
// if we are doing the search on a background thread. If so, we will sacrifice
// speed for thread-safety.
V8_INLINE InternalIndex Search(Tagged<Name> name,
int number_of_own_descriptors,
bool concurrent_search = false);
V8_INLINE InternalIndex Search(Tagged<Name> name, Tagged<Map> map,
bool concurrent_search = false);
// Search the instance descriptors for given field offset.
V8_INLINE InternalIndex Search(int field_offset,
int number_of_own_descriptors);
V8_INLINE InternalIndex Search(int field_offset, Tagged<Map> map);
// As the above, but uses DescriptorLookupCache and updates it when
// necessary.
V8_INLINE InternalIndex SearchWithCache(Isolate* isolate, Tagged<Name> name,
Tagged<Map> map);
bool IsEqualUpTo(Tagged<DescriptorArray> desc, int nof_descriptors);
// Allocates a DescriptorArray, but returns the singleton
// empty descriptor array object if number_of_descriptors is 0.
template <typename IsolateT>
V8_EXPORT_PRIVATE static Handle<DescriptorArray> Allocate(
IsolateT* isolate, int nof_descriptors, int slack,
AllocationType allocation = AllocationType::kYoung);
void Initialize(Tagged<EnumCache> enum_cache,
Tagged<HeapObject> undefined_value, int nof_descriptors,
int slack, uint32_t raw_gc_state);
// Constant for denoting key was not found.
static const int kNotFound = -1;
static_assert(IsAligned(kStartOfWeakFieldsOffset, kTaggedSize));
static_assert(IsAligned(kHeaderSize, kTaggedSize));
// Garbage collection support.
DECL_RELAXED_UINT32_ACCESSORS(raw_gc_state)
static constexpr size_t kSizeOfRawGcState =
kRawGcStateOffsetEnd - kRawGcStateOffset + 1;
static constexpr int SizeFor(int number_of_all_descriptors) {
return OffsetOfDescriptorAt(number_of_all_descriptors);
}
static constexpr int OffsetOfDescriptorAt(int descriptor) {
return kDescriptorsOffset + descriptor * kEntrySize * kTaggedSize;
}
inline ObjectSlot GetFirstPointerSlot();
inline ObjectSlot GetDescriptorSlot(int descriptor);
static_assert(kEndOfStrongFieldsOffset == kStartOfWeakFieldsOffset,
"Weak fields follow strong fields.");
static_assert(kEndOfWeakFieldsOffset == kHeaderSize,
"Weak fields extend up to the end of the header.");
static_assert(kDescriptorsOffset == kHeaderSize,
"Variable-size array follows header.");
class BodyDescriptor;
// Layout of descriptor.
// Naming is consistent with Dictionary classes for easy templating.
static const int kEntryKeyIndex = 0;
static const int kEntryDetailsIndex = 1;
static const int kEntryValueIndex = 2;
static const int kEntrySize = 3;
static const int kEntryKeyOffset = kEntryKeyIndex * kTaggedSize;
static const int kEntryDetailsOffset = kEntryDetailsIndex * kTaggedSize;
static const int kEntryValueOffset = kEntryValueIndex * kTaggedSize;
// Print all the descriptors.
void PrintDescriptors(std::ostream& os);
void PrintDescriptorDetails(std::ostream& os, InternalIndex descriptor,
PropertyDetails::PrintMode mode);
DECL_PRINTER(DescriptorArray)
DECL_VERIFIER(DescriptorArray)
#ifdef DEBUG
// Is the descriptor array sorted and without duplicates?
V8_EXPORT_PRIVATE bool IsSortedNoDuplicates();
// Are two DescriptorArrays equal?
bool IsEqualTo(Tagged<DescriptorArray> other);
#endif
static constexpr int ToDetailsIndex(int descriptor_number) {
return (descriptor_number * kEntrySize) + kEntryDetailsIndex;
}
// Conversion from descriptor number to array indices.
static constexpr int ToKeyIndex(int descriptor_number) {
return (descriptor_number * kEntrySize) + kEntryKeyIndex;
}
static constexpr int ToValueIndex(int descriptor_number) {
return (descriptor_number * kEntrySize) + kEntryValueIndex;
}
using EntryKeyField = TaggedField<HeapObject, kEntryKeyOffset>;
using EntryDetailsField = TaggedField<Smi, kEntryDetailsOffset>;
using EntryValueField = TaggedField<MaybeObject, kEntryValueOffset>;
private:
inline void SetKey(InternalIndex descriptor_number, Tagged<Name> key);
inline void SetValue(InternalIndex descriptor_number, MaybeObject value);
inline void SetDetails(InternalIndex descriptor_number,
PropertyDetails details);
// Transfer a complete descriptor from the src descriptor array to this
// descriptor array.
void CopyFrom(InternalIndex index, Tagged<DescriptorArray> src);
inline void SetSortedKey(int pointer, int descriptor_number);
// Swap first and second descriptor.
inline void SwapSortedKeys(int first, int second);
TQ_OBJECT_CONSTRUCTORS(DescriptorArray)
};
// Custom DescriptorArray marking state for visitors that are allowed to write
// into the heap. The marking state uses DescriptorArray::raw_gc_state() as
// storage.
//
// The state essentially keeps track of 3 fields:
// 1. The collector epoch: The rest of the state is only valid if the epoch
// matches. If the epoch doesn't match, the other fields should be considered
// invalid. The epoch is necessary, as not all DescriptorArray objects are
// eventually trimmed in the atomic pause and thus available for resetting
// the state.
// 2. Number of already marked descriptors.
// 3. Delta of to be marked descriptors in this cycle. This must be 0 after
// marking is done.
class DescriptorArrayMarkingState final {
public:
#define BIT_FIELD_FIELDS(V, _) \
V(Epoch, unsigned, 2, _) \
V(Marked, uint16_t, 14, _) \
V(Delta, uint16_t, 16, _)
DEFINE_BIT_FIELDS(BIT_FIELD_FIELDS)
#undef BIT_FIELD_FIELDS
static_assert(Marked::kMax <= Delta::kMax);
static_assert(kMaxNumberOfDescriptors <= Marked::kMax);
using DescriptorIndex = uint16_t;
using RawGCStateType = uint32_t;
static constexpr RawGCStateType kInitialGCState = 0;
static constexpr RawGCStateType GetFullyMarkedState(
unsigned epoch, DescriptorIndex number_of_descriptors) {
return NewState(epoch & Epoch::kMask, number_of_descriptors, 0);
}
// Potentially updates the delta of to be marked descriptors. Returns true if
// the update was successful and the object should be processed via a marking
// visitor.
//
// The call issues and Acq/Rel barrier to allow synchronizing other state
// (e.g. value of descriptor slots) with it.
static inline bool TryUpdateIndicesToMark(unsigned gc_epoch,
Tagged<DescriptorArray> array,
DescriptorIndex index_to_mark);
// Used from the visitor when processing a DescriptorArray. Returns a range of
// start and end descriptor indices. No processing is required for start ==
// end. The method signals the first invocation by returning start == 0, and
// end != 0.
static inline std::pair<DescriptorIndex, DescriptorIndex>
AcquireDescriptorRangeToMark(unsigned gc_epoch,
Tagged<DescriptorArray> array);
private:
static constexpr RawGCStateType NewState(unsigned masked_epoch,
DescriptorIndex marked,
DescriptorIndex delta) {
return Epoch::encode(masked_epoch) | Marked::encode(marked) |
Delta::encode(delta);
}
static bool SwapState(Tagged<DescriptorArray> array, RawGCStateType old_state,
RawGCStateType new_state) {
return static_cast<RawGCStateType>(base::AcquireRelease_CompareAndSwap(
reinterpret_cast<base::Atomic32*>(
FIELD_ADDR(array, DescriptorArray::kRawGcStateOffset)),
old_state, new_state)) == old_state;
}
};
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_DESCRIPTOR_ARRAY_H_
|