Line data Source code
1 : // Copyright 2017 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 :
5 : #include "src/ic/handler-configuration.h"
6 :
7 : #include "src/code-stubs.h"
8 : #include "src/ic/handler-configuration-inl.h"
9 : #include "src/transitions.h"
10 :
11 : namespace v8 {
12 : namespace internal {
13 :
14 : namespace {
15 :
16 : template <bool fill_array = true>
17 732135 : int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
18 : Handle<JSReceiver> holder, Handle<Name> name,
19 : Handle<FixedArray> array, int first_index) {
20 1368432 : if (!holder.is_null() && holder->map() == *receiver_map) return 0;
21 :
22 : HandleScope scope(isolate);
23 : int checks_count = 0;
24 :
25 731671 : if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap()) {
26 : // The validity cell check for primitive and global proxy receivers does
27 : // not guarantee that certain native context ever had access to other
28 : // native context. However, a handler created for one native context could
29 : // be used in other native context through the megamorphic stub cache.
30 : // So we record the original native context to which this handler
31 : // corresponds.
32 : if (fill_array) {
33 39361 : Handle<Context> native_context = isolate->native_context();
34 39361 : array->set(first_index + checks_count, native_context->self_weak_cell());
35 : }
36 : checks_count++;
37 :
38 652949 : } else if (receiver_map->IsJSGlobalObjectMap()) {
39 : // If we are creating a handler for [Load/Store]GlobalIC then we need to
40 : // check that the property did not appear in the global object.
41 : if (fill_array) {
42 1586 : Handle<JSGlobalObject> global = isolate->global_object();
43 : Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
44 1586 : global, name, PropertyCellType::kInvalidated);
45 : DCHECK(cell->value()->IsTheHole(isolate));
46 1586 : Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
47 1586 : array->set(first_index + checks_count, *weak_cell);
48 : }
49 : checks_count++;
50 : }
51 :
52 : // Create/count entries for each global or dictionary prototype appeared in
53 : // the prototype chain contains from receiver till holder.
54 731671 : PrototypeIterator::WhereToEnd end = name->IsPrivate()
55 : ? PrototypeIterator::END_AT_NON_HIDDEN
56 731670 : : PrototypeIterator::END_AT_NULL;
57 2700745 : for (PrototypeIterator iter(receiver_map, end); !iter.IsAtEnd();
58 1237404 : iter.Advance()) {
59 : Handle<JSReceiver> current =
60 : PrototypeIterator::GetCurrent<JSReceiver>(iter);
61 1873227 : if (holder.is_identical_to(current)) break;
62 : Handle<Map> current_map(current->map(), isolate);
63 :
64 1237404 : if (current_map->IsJSGlobalObjectMap()) {
65 : if (fill_array) {
66 428 : Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(current);
67 : Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
68 428 : global, name, PropertyCellType::kInvalidated);
69 : DCHECK(cell->value()->IsTheHole(isolate));
70 428 : Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
71 856 : array->set(first_index + checks_count, *weak_cell);
72 : }
73 856 : checks_count++;
74 :
75 1236548 : } else if (current_map->is_dictionary_map()) {
76 : DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
77 : if (fill_array) {
78 : DCHECK_EQ(NameDictionary::kNotFound,
79 : current->property_dictionary()->FindEntry(name));
80 : Handle<WeakCell> weak_cell =
81 292 : Map::GetOrCreatePrototypeWeakCell(current, isolate);
82 584 : array->set(first_index + checks_count, *weak_cell);
83 : }
84 584 : checks_count++;
85 : }
86 : }
87 : return checks_count;
88 : }
89 :
90 : // Returns 0 if the validity cell check is enough to ensure that the
91 : // prototype chain from |receiver_map| till |holder| did not change.
92 : // If the |holder| is an empty handle then the full prototype chain is
93 : // checked.
94 : // Returns -1 if the handler has to be compiled or the number of prototype
95 : // checks otherwise.
96 : int GetPrototypeCheckCount(Isolate* isolate, Handle<Map> receiver_map,
97 : Handle<JSReceiver> holder, Handle<Name> name) {
98 : return InitPrototypeChecks<false>(isolate, receiver_map, holder, name,
99 691079 : Handle<FixedArray>(), 0);
100 : }
101 :
102 : enum class HolderCellRequest {
103 : kGlobalPropertyCell,
104 : kHolder,
105 : };
106 :
107 385661 : Handle<WeakCell> HolderCell(Isolate* isolate, Handle<JSReceiver> holder,
108 : Handle<Name> name, HolderCellRequest request) {
109 385661 : if (request == HolderCellRequest::kGlobalPropertyCell) {
110 : DCHECK(holder->IsJSGlobalObject());
111 : Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
112 : GlobalDictionary* dict = global->global_dictionary();
113 : int number = dict->FindEntry(name);
114 : DCHECK_NE(NameDictionary::kNotFound, number);
115 : Handle<PropertyCell> cell(dict->CellAt(number), isolate);
116 12253 : return isolate->factory()->NewWeakCell(cell);
117 : }
118 373408 : return Map::GetOrCreatePrototypeWeakCell(holder, isolate);
119 : }
120 :
121 : } // namespace
122 :
123 : // static
124 385661 : Handle<Object> LoadHandler::LoadFromPrototype(Isolate* isolate,
125 : Handle<Map> receiver_map,
126 : Handle<JSReceiver> holder,
127 : Handle<Name> name,
128 : Handle<Smi> smi_handler) {
129 : int checks_count =
130 : GetPrototypeCheckCount(isolate, receiver_map, holder, name);
131 : DCHECK_LE(0, checks_count);
132 :
133 740417 : if (receiver_map->IsPrimitiveMap() ||
134 : receiver_map->is_access_check_needed()) {
135 : DCHECK(!receiver_map->is_dictionary_map());
136 : DCHECK_LE(1, checks_count); // For native context.
137 33823 : smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
138 351838 : } else if (receiver_map->is_dictionary_map() &&
139 : !receiver_map->IsJSGlobalObjectMap()) {
140 724 : smi_handler = EnableLookupOnReceiver(isolate, smi_handler);
141 : }
142 :
143 : Handle<Cell> validity_cell =
144 385661 : Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
145 : DCHECK(!validity_cell.is_null());
146 :
147 : // LoadIC dispatcher expects PropertyCell as a "holder" in case of kGlobal
148 : // handler kind.
149 : HolderCellRequest request = GetHandlerKind(*smi_handler) == kGlobal
150 : ? HolderCellRequest::kGlobalPropertyCell
151 385661 : : HolderCellRequest::kHolder;
152 :
153 385661 : Handle<WeakCell> holder_cell = HolderCell(isolate, holder, name, request);
154 :
155 385661 : if (checks_count == 0) {
156 : return isolate->factory()->NewTuple3(holder_cell, smi_handler,
157 351165 : validity_cell, TENURED);
158 : }
159 : Handle<FixedArray> handler_array(isolate->factory()->NewFixedArray(
160 34496 : kFirstPrototypeIndex + checks_count, TENURED));
161 : handler_array->set(kSmiHandlerIndex, *smi_handler);
162 34496 : handler_array->set(kValidityCellIndex, *validity_cell);
163 34496 : handler_array->set(kHolderCellIndex, *holder_cell);
164 : InitPrototypeChecks(isolate, receiver_map, holder, name, handler_array,
165 34496 : kFirstPrototypeIndex);
166 34496 : return handler_array;
167 : }
168 :
169 : // static
170 81553 : Handle<Object> LoadHandler::LoadFullChain(Isolate* isolate,
171 : Handle<Map> receiver_map,
172 : Handle<Object> holder,
173 : Handle<Name> name,
174 : Handle<Smi> smi_handler) {
175 : Handle<JSReceiver> end; // null handle
176 : int checks_count = GetPrototypeCheckCount(isolate, receiver_map, end, name);
177 : DCHECK_LE(0, checks_count);
178 :
179 158926 : if (receiver_map->IsPrimitiveMap() ||
180 : receiver_map->is_access_check_needed()) {
181 : DCHECK(!receiver_map->is_dictionary_map());
182 : DCHECK_LE(1, checks_count); // For native context.
183 4254 : smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
184 77299 : } else if (receiver_map->is_dictionary_map() &&
185 : !receiver_map->IsJSGlobalObjectMap()) {
186 4917 : smi_handler = EnableLookupOnReceiver(isolate, smi_handler);
187 : }
188 :
189 : Handle<Object> validity_cell =
190 81553 : Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
191 81553 : if (validity_cell.is_null()) {
192 : DCHECK_EQ(0, checks_count);
193 : // Lookup on receiver isn't supported in case of a simple smi handler.
194 11770 : if (!LookupOnReceiverBits::decode(smi_handler->value())) return smi_handler;
195 : validity_cell = handle(Smi::kZero, isolate);
196 : }
197 :
198 : Factory* factory = isolate->factory();
199 80378 : if (checks_count == 0) {
200 75192 : return factory->NewTuple3(holder, smi_handler, validity_cell, TENURED);
201 : }
202 : Handle<FixedArray> handler_array(factory->NewFixedArray(
203 5186 : LoadHandler::kFirstPrototypeIndex + checks_count, TENURED));
204 : handler_array->set(kSmiHandlerIndex, *smi_handler);
205 5186 : handler_array->set(kValidityCellIndex, *validity_cell);
206 5186 : handler_array->set(kHolderCellIndex, *holder);
207 : InitPrototypeChecks(isolate, receiver_map, end, name, handler_array,
208 5186 : kFirstPrototypeIndex);
209 5186 : return handler_array;
210 : }
211 :
212 : // static
213 1911 : Handle<Object> StoreHandler::StoreElementTransition(
214 : Isolate* isolate, Handle<Map> receiver_map, Handle<Map> transition,
215 : KeyedAccessStoreMode store_mode) {
216 1911 : bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
217 : ElementsKind elements_kind = receiver_map->elements_kind();
218 : Handle<Code> stub = ElementsTransitionAndStoreStub(
219 : isolate, elements_kind, transition->elements_kind(),
220 : is_js_array, store_mode)
221 3822 : .GetCode();
222 : Handle<Object> validity_cell =
223 1911 : Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
224 1911 : if (validity_cell.is_null()) {
225 : validity_cell = handle(Smi::kZero, isolate);
226 : }
227 1911 : Handle<WeakCell> cell = Map::WeakCellForMap(transition);
228 1911 : return isolate->factory()->NewTuple3(cell, stub, validity_cell, TENURED);
229 : }
230 :
231 : // static
232 224173 : Handle<Object> StoreHandler::StoreTransition(Isolate* isolate,
233 : Handle<Map> receiver_map,
234 : Handle<JSObject> holder,
235 : Handle<HeapObject> transition,
236 : Handle<Name> name) {
237 : Handle<Smi> smi_handler;
238 : Handle<WeakCell> transition_cell;
239 :
240 224173 : if (transition->IsMap()) {
241 : Handle<Map> transition_map = Handle<Map>::cast(transition);
242 222383 : if (transition_map->is_dictionary_map()) {
243 12461 : smi_handler = StoreNormal(isolate);
244 : } else {
245 : int descriptor = transition_map->LastAdded();
246 : Handle<DescriptorArray> descriptors(
247 : transition_map->instance_descriptors());
248 209922 : PropertyDetails details = descriptors->GetDetails(descriptor);
249 : Representation representation = details.representation();
250 : DCHECK(!representation.IsNone());
251 :
252 : // Declarative handlers don't support access checks.
253 : DCHECK(!transition_map->is_access_check_needed());
254 :
255 : DCHECK_EQ(kData, details.kind());
256 209922 : if (details.location() == kDescriptor) {
257 217 : smi_handler = TransitionToConstant(isolate, descriptor);
258 :
259 : } else {
260 : DCHECK_EQ(kField, details.location());
261 : bool extend_storage = Map::cast(transition_map->GetBackPointer())
262 419410 : ->UnusedPropertyFields() == 0;
263 :
264 : FieldIndex index =
265 209705 : FieldIndex::ForDescriptor(*transition_map, descriptor);
266 : smi_handler = TransitionToField(isolate, descriptor, index,
267 : representation, extend_storage);
268 : }
269 : }
270 : // |holder| is either a receiver if the property is non-existent or
271 : // one of the prototypes.
272 : DCHECK(!holder.is_null());
273 222383 : bool is_nonexistent = holder->map() == transition_map->GetBackPointer();
274 222383 : if (is_nonexistent) holder = Handle<JSObject>::null();
275 222383 : transition_cell = Map::WeakCellForMap(transition_map);
276 :
277 : } else {
278 : DCHECK(transition->IsPropertyCell());
279 1790 : if (receiver_map->IsJSGlobalObjectMap()) {
280 : // TODO(ishell): this must be handled by StoreGlobalIC once it's finished.
281 : return StoreGlobal(isolate, Handle<PropertyCell>::cast(transition));
282 : } else {
283 : DCHECK(receiver_map->IsJSGlobalProxyMap());
284 1264 : smi_handler = StoreGlobalProxy(isolate);
285 1264 : transition_cell = isolate->factory()->NewWeakCell(transition);
286 : }
287 : }
288 :
289 : int checks_count =
290 : GetPrototypeCheckCount(isolate, receiver_map, holder, name);
291 :
292 : DCHECK_LE(0, checks_count);
293 : DCHECK(!receiver_map->IsJSGlobalObjectMap());
294 :
295 223647 : if (receiver_map->is_access_check_needed()) {
296 : DCHECK(!receiver_map->is_dictionary_map());
297 : DCHECK_LE(1, checks_count); // For native context.
298 1264 : smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
299 : }
300 :
301 : Handle<Object> validity_cell =
302 223647 : Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
303 223647 : if (validity_cell.is_null()) {
304 : DCHECK_EQ(0, checks_count);
305 : validity_cell = handle(Smi::kZero, isolate);
306 : }
307 :
308 : Factory* factory = isolate->factory();
309 223647 : if (checks_count == 0) {
310 : return factory->NewTuple3(transition_cell, smi_handler, validity_cell,
311 222383 : TENURED);
312 : }
313 : Handle<FixedArray> handler_array(
314 1264 : factory->NewFixedArray(kFirstPrototypeIndex + checks_count, TENURED));
315 : handler_array->set(kSmiHandlerIndex, *smi_handler);
316 1264 : handler_array->set(kValidityCellIndex, *validity_cell);
317 1264 : handler_array->set(kTransitionMapOrHolderCellIndex, *transition_cell);
318 : InitPrototypeChecks(isolate, receiver_map, holder, name, handler_array,
319 1264 : kFirstPrototypeIndex);
320 1264 : return handler_array;
321 : }
322 :
323 : // static
324 53153 : Handle<Object> StoreHandler::StoreGlobal(Isolate* isolate,
325 : Handle<PropertyCell> cell) {
326 53679 : return isolate->factory()->NewWeakCell(cell);
327 : }
328 :
329 : // static
330 354 : Handle<Object> StoreHandler::StoreProxy(Isolate* isolate,
331 : Handle<Map> receiver_map,
332 : Handle<JSProxy> proxy,
333 : Handle<JSReceiver> receiver,
334 : Handle<Name> name) {
335 354 : Handle<Smi> smi_handler = StoreProxy(isolate);
336 :
337 354 : if (receiver.is_identical_to(proxy)) return smi_handler;
338 :
339 : int checks_count = GetPrototypeCheckCount(isolate, receiver_map, proxy, name);
340 :
341 : DCHECK_LE(0, checks_count);
342 :
343 218 : if (receiver_map->is_access_check_needed()) {
344 : DCHECK(!receiver_map->is_dictionary_map());
345 : DCHECK_LE(1, checks_count); // For native context.
346 20 : smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
347 : }
348 :
349 : Handle<Object> validity_cell =
350 218 : Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
351 218 : if (validity_cell.is_null()) {
352 : DCHECK_EQ(0, checks_count);
353 : validity_cell = handle(Smi::kZero, isolate);
354 : }
355 :
356 : Factory* factory = isolate->factory();
357 218 : Handle<WeakCell> holder_cell = factory->NewWeakCell(proxy);
358 :
359 218 : if (checks_count == 0) {
360 108 : return factory->NewTuple3(holder_cell, smi_handler, validity_cell, TENURED);
361 : }
362 : Handle<FixedArray> handler_array(
363 110 : factory->NewFixedArray(kFirstPrototypeIndex + checks_count, TENURED));
364 : handler_array->set(kSmiHandlerIndex, *smi_handler);
365 110 : handler_array->set(kValidityCellIndex, *validity_cell);
366 110 : handler_array->set(kTransitionMapOrHolderCellIndex, *holder_cell);
367 : InitPrototypeChecks(isolate, receiver_map, proxy, name, handler_array,
368 110 : kFirstPrototypeIndex);
369 110 : return handler_array;
370 : }
371 :
372 22814 : Object* StoreHandler::ValidHandlerOrNull(Object* raw_handler, Name* name,
373 : Handle<Map>* out_transition) {
374 : STATIC_ASSERT(kValidityCellOffset == Tuple3::kValue3Offset);
375 :
376 : Smi* valid = Smi::FromInt(Map::kPrototypeChainValid);
377 :
378 22814 : if (raw_handler->IsTuple3()) {
379 : // Check validity cell.
380 : Tuple3* handler = Tuple3::cast(raw_handler);
381 :
382 : Object* raw_validity_cell = handler->value3();
383 : // |raw_valitity_cell| can be Smi::kZero if no validity cell is required
384 : // (which counts as valid).
385 45563 : if (raw_validity_cell->IsCell() &&
386 : Cell::cast(raw_validity_cell)->value() != valid) {
387 : return nullptr;
388 : }
389 :
390 : } else {
391 : DCHECK(raw_handler->IsFixedArray());
392 : FixedArray* handler = FixedArray::cast(raw_handler);
393 :
394 : // Check validity cell.
395 : Object* value = Cell::cast(handler->get(kValidityCellIndex))->value();
396 0 : if (value != valid) return nullptr;
397 :
398 : // Check prototypes.
399 0 : Heap* heap = handler->GetHeap();
400 : Isolate* isolate = heap->isolate();
401 : Handle<Name> name_handle(name, isolate);
402 0 : for (int i = kFirstPrototypeIndex; i < handler->length(); i++) {
403 : // This mirrors AccessorAssembler::CheckPrototype.
404 : WeakCell* prototype_cell = WeakCell::cast(handler->get(i));
405 0 : if (prototype_cell->cleared()) return nullptr;
406 : HeapObject* maybe_prototype = HeapObject::cast(prototype_cell->value());
407 0 : if (maybe_prototype->IsPropertyCell()) {
408 : Object* value = PropertyCell::cast(maybe_prototype)->value();
409 0 : if (value != heap->the_hole_value()) return nullptr;
410 : } else {
411 : DCHECK(maybe_prototype->map()->is_dictionary_map());
412 : // Do a negative dictionary lookup.
413 : NameDictionary* dict =
414 : JSObject::cast(maybe_prototype)->property_dictionary();
415 0 : int number = dict->FindEntry(isolate, name_handle);
416 0 : if (number != NameDictionary::kNotFound) {
417 : PropertyDetails details = dict->DetailsAt(number);
418 0 : if (details.IsReadOnly() || details.kind() == kAccessor) {
419 : return nullptr;
420 : }
421 : break;
422 : }
423 : }
424 : }
425 : }
426 :
427 : // Check if the transition target is deprecated.
428 12629 : WeakCell* target_cell = GetTransitionCell(raw_handler);
429 : Map* transition = Map::cast(target_cell->value());
430 12629 : if (transition->is_deprecated()) return nullptr;
431 12629 : *out_transition = handle(transition);
432 12629 : return raw_handler;
433 : }
434 :
435 : } // namespace internal
436 : } // namespace v8
|