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/codegen/code-factory.h"
8 #include "src/ic/handler-configuration-inl.h"
9 #include "src/objects/data-handler-inl.h"
10 #include "src/objects/maybe-object.h"
11 #include "src/objects/transitions.h"
12
13 namespace v8 {
14 namespace internal {
15
16 namespace {
17
18 template <typename BitField>
SetBitFieldValue(Isolate * isolate,Handle<Smi> smi_handler,typename BitField::FieldType value)19 Handle<Smi> SetBitFieldValue(Isolate* isolate, Handle<Smi> smi_handler,
20 typename BitField::FieldType value) {
21 int config = smi_handler->value();
22 config = BitField::update(config, true);
23 return handle(Smi::FromInt(config), isolate);
24 }
25
26 // TODO(ishell): Remove templatezation once we move common bits from
27 // Load/StoreHandler to the base class.
28 template <typename ICHandler, bool fill_handler = true>
InitPrototypeChecksImpl(Isolate * isolate,Handle<ICHandler> handler,Handle<Smi> * smi_handler,Handle<Map> lookup_start_object_map,MaybeObjectHandle data1,MaybeObjectHandle maybe_data2)29 int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler,
30 Handle<Smi>* smi_handler,
31 Handle<Map> lookup_start_object_map,
32 MaybeObjectHandle data1,
33 MaybeObjectHandle maybe_data2) {
34 int data_size = 1;
35 // Holder-is-receiver case itself does not add entries unless there is an
36 // optional data2 value provided.
37
38 DCHECK_IMPLIES(lookup_start_object_map->IsJSGlobalObjectMap(),
39 lookup_start_object_map->is_prototype_map());
40
41 if (lookup_start_object_map->IsPrimitiveMap() ||
42 lookup_start_object_map->is_access_check_needed()) {
43 DCHECK(!lookup_start_object_map->IsJSGlobalObjectMap());
44 // The validity cell check for primitive and global proxy receivers does
45 // not guarantee that certain native context ever had access to other
46 // native context. However, a handler created for one native context could
47 // be used in other native context through the megamorphic stub cache.
48 // So we record the original native context to which this handler
49 // corresponds.
50 if (fill_handler) {
51 Handle<Context> native_context = isolate->native_context();
52 handler->set_data2(HeapObjectReference::Weak(*native_context));
53 } else {
54 // Enable access checks on the lookup start object.
55 *smi_handler = SetBitFieldValue<
56 typename ICHandler::DoAccessCheckOnLookupStartObjectBits>(
57 isolate, *smi_handler, true);
58 }
59 data_size++;
60 } else if (lookup_start_object_map->is_dictionary_map() &&
61 !lookup_start_object_map->IsJSGlobalObjectMap()) {
62 if (!fill_handler) {
63 // Enable lookup on lookup start object.
64 *smi_handler =
65 SetBitFieldValue<typename ICHandler::LookupOnLookupStartObjectBits>(
66 isolate, *smi_handler, true);
67 }
68 }
69 if (fill_handler) {
70 handler->set_data1(*data1);
71 }
72 if (!maybe_data2.is_null()) {
73 if (fill_handler) {
74 // This value will go either to data2 or data3 slot depending on whether
75 // data2 slot is already occupied by native context.
76 if (data_size == 1) {
77 handler->set_data2(*maybe_data2);
78 } else {
79 DCHECK_EQ(2, data_size);
80 handler->set_data3(*maybe_data2);
81 }
82 }
83 data_size++;
84 }
85 return data_size;
86 }
87
88 // Returns 0 if the validity cell check is enough to ensure that the
89 // prototype chain from |lookup_start_object_map| till |holder| did not change.
90 // If the |holder| is an empty handle then the full prototype chain is
91 // checked.
92 template <typename ICHandler>
GetHandlerDataSize(Isolate * isolate,Handle<Smi> * smi_handler,Handle<Map> lookup_start_object_map,MaybeObjectHandle data1,MaybeObjectHandle maybe_data2=MaybeObjectHandle ())93 int GetHandlerDataSize(Isolate* isolate, Handle<Smi>* smi_handler,
94 Handle<Map> lookup_start_object_map,
95 MaybeObjectHandle data1,
96 MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) {
97 DCHECK_NOT_NULL(smi_handler);
98 return InitPrototypeChecksImpl<ICHandler, false>(
99 isolate, Handle<ICHandler>(), smi_handler, lookup_start_object_map, data1,
100 maybe_data2);
101 }
102
103 template <typename ICHandler>
InitPrototypeChecks(Isolate * isolate,Handle<ICHandler> handler,Handle<Map> lookup_start_object_map,MaybeObjectHandle data1,MaybeObjectHandle maybe_data2=MaybeObjectHandle ())104 void InitPrototypeChecks(Isolate* isolate, Handle<ICHandler> handler,
105 Handle<Map> lookup_start_object_map,
106 MaybeObjectHandle data1,
107 MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) {
108 InitPrototypeChecksImpl<ICHandler, true>(
109 isolate, handler, nullptr, lookup_start_object_map, data1, maybe_data2);
110 }
111
112 } // namespace
113
114 // static
LoadFromPrototype(Isolate * isolate,Handle<Map> lookup_start_object_map,Handle<JSReceiver> holder,Handle<Smi> smi_handler,MaybeObjectHandle maybe_data1,MaybeObjectHandle maybe_data2)115 Handle<Object> LoadHandler::LoadFromPrototype(
116 Isolate* isolate, Handle<Map> lookup_start_object_map,
117 Handle<JSReceiver> holder, Handle<Smi> smi_handler,
118 MaybeObjectHandle maybe_data1, MaybeObjectHandle maybe_data2) {
119 MaybeObjectHandle data1;
120 if (maybe_data1.is_null()) {
121 data1 = MaybeObjectHandle::Weak(holder);
122 } else {
123 data1 = maybe_data1;
124 }
125
126 int data_size = GetHandlerDataSize<LoadHandler>(
127 isolate, &smi_handler, lookup_start_object_map, data1, maybe_data2);
128
129 Handle<Object> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(
130 lookup_start_object_map, isolate);
131
132 Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size);
133
134 handler->set_smi_handler(*smi_handler);
135 handler->set_validity_cell(*validity_cell);
136 InitPrototypeChecks(isolate, handler, lookup_start_object_map, data1,
137 maybe_data2);
138 return handler;
139 }
140
141 // static
LoadFullChain(Isolate * isolate,Handle<Map> lookup_start_object_map,const MaybeObjectHandle & holder,Handle<Smi> smi_handler)142 Handle<Object> LoadHandler::LoadFullChain(Isolate* isolate,
143 Handle<Map> lookup_start_object_map,
144 const MaybeObjectHandle& holder,
145 Handle<Smi> smi_handler) {
146 MaybeObjectHandle data1 = holder;
147 int data_size = GetHandlerDataSize<LoadHandler>(
148 isolate, &smi_handler, lookup_start_object_map, data1);
149
150 Handle<Object> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(
151 lookup_start_object_map, isolate);
152 if (validity_cell->IsSmi()) {
153 DCHECK_EQ(1, data_size);
154 // Lookup on lookup start object isn't supported in case of a simple smi
155 // handler.
156 if (!LookupOnLookupStartObjectBits::decode(smi_handler->value())) {
157 return smi_handler;
158 }
159 }
160
161 Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size);
162
163 handler->set_smi_handler(*smi_handler);
164 handler->set_validity_cell(*validity_cell);
165 InitPrototypeChecks(isolate, handler, lookup_start_object_map, data1);
166 return handler;
167 }
168
169 // static
GetKeyedAccessLoadMode(MaybeObject handler)170 KeyedAccessLoadMode LoadHandler::GetKeyedAccessLoadMode(MaybeObject handler) {
171 DisallowHeapAllocation no_gc;
172 if (handler->IsSmi()) {
173 int const raw_handler = handler.ToSmi().value();
174 Kind const kind = KindBits::decode(raw_handler);
175 if ((kind == kElement || kind == kIndexedString) &&
176 AllowOutOfBoundsBits::decode(raw_handler)) {
177 return LOAD_IGNORE_OUT_OF_BOUNDS;
178 }
179 }
180 return STANDARD_LOAD;
181 }
182
183 // static
GetKeyedAccessStoreMode(MaybeObject handler)184 KeyedAccessStoreMode StoreHandler::GetKeyedAccessStoreMode(
185 MaybeObject handler) {
186 DisallowHeapAllocation no_gc;
187 if (handler->IsSmi()) {
188 int const raw_handler = handler.ToSmi().value();
189 Kind const kind = KindBits::decode(raw_handler);
190 // All the handlers except the Slow Handler that use the
191 // KeyedAccessStoreMode, compute it using KeyedAccessStoreModeForBuiltin
192 // method. Hence if any other Handler get to this path, just return
193 // STANDARD_STORE.
194 if (kind != kSlow) {
195 return STANDARD_STORE;
196 }
197 KeyedAccessStoreMode store_mode =
198 KeyedAccessStoreModeBits::decode(raw_handler);
199 return store_mode;
200 }
201 return STANDARD_STORE;
202 }
203
204 // static
StoreElementTransition(Isolate * isolate,Handle<Map> receiver_map,Handle<Map> transition,KeyedAccessStoreMode store_mode,MaybeHandle<Object> prev_validity_cell)205 Handle<Object> StoreHandler::StoreElementTransition(
206 Isolate* isolate, Handle<Map> receiver_map, Handle<Map> transition,
207 KeyedAccessStoreMode store_mode, MaybeHandle<Object> prev_validity_cell) {
208 Handle<Code> stub =
209 CodeFactory::ElementsTransitionAndStore(isolate, store_mode).code();
210 Handle<Object> validity_cell;
211 if (!prev_validity_cell.ToHandle(&validity_cell)) {
212 validity_cell =
213 Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
214 }
215 Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(1);
216 handler->set_smi_handler(*stub);
217 handler->set_validity_cell(*validity_cell);
218 handler->set_data1(HeapObjectReference::Weak(*transition));
219 return handler;
220 }
221
StoreTransition(Isolate * isolate,Handle<Map> transition_map)222 MaybeObjectHandle StoreHandler::StoreTransition(Isolate* isolate,
223 Handle<Map> transition_map) {
224 bool is_dictionary_map = transition_map->is_dictionary_map();
225 #ifdef DEBUG
226 if (!is_dictionary_map) {
227 InternalIndex descriptor = transition_map->LastAdded();
228 Handle<DescriptorArray> descriptors(
229 transition_map->instance_descriptors(kRelaxedLoad), isolate);
230 PropertyDetails details = descriptors->GetDetails(descriptor);
231 if (descriptors->GetKey(descriptor).IsPrivate()) {
232 DCHECK_EQ(DONT_ENUM, details.attributes());
233 } else {
234 DCHECK_EQ(NONE, details.attributes());
235 }
236 Representation representation = details.representation();
237 DCHECK(!representation.IsNone());
238 }
239 #endif
240 // Declarative handlers don't support access checks.
241 DCHECK(!transition_map->is_access_check_needed());
242
243 // Get validity cell value if it is necessary for the handler.
244 Handle<Object> validity_cell;
245 if (is_dictionary_map || !transition_map->IsPrototypeValidityCellValid()) {
246 validity_cell =
247 Map::GetOrCreatePrototypeChainValidityCell(transition_map, isolate);
248 }
249
250 if (is_dictionary_map) {
251 DCHECK(!transition_map->IsJSGlobalObjectMap());
252 Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(0);
253 // Store normal with enabled lookup on receiver.
254 int config =
255 KindBits::encode(kNormal) | LookupOnLookupStartObjectBits::encode(true);
256 handler->set_smi_handler(Smi::FromInt(config));
257 handler->set_validity_cell(*validity_cell);
258 return MaybeObjectHandle(handler);
259
260 } else {
261 // Ensure the transition map contains a valid prototype validity cell.
262 if (!validity_cell.is_null()) {
263 transition_map->set_prototype_validity_cell(*validity_cell);
264 }
265 return MaybeObjectHandle::Weak(transition_map);
266 }
267 }
268
269 // static
StoreThroughPrototype(Isolate * isolate,Handle<Map> receiver_map,Handle<JSReceiver> holder,Handle<Smi> smi_handler,MaybeObjectHandle maybe_data1,MaybeObjectHandle maybe_data2)270 Handle<Object> StoreHandler::StoreThroughPrototype(
271 Isolate* isolate, Handle<Map> receiver_map, Handle<JSReceiver> holder,
272 Handle<Smi> smi_handler, MaybeObjectHandle maybe_data1,
273 MaybeObjectHandle maybe_data2) {
274 MaybeObjectHandle data1;
275 if (maybe_data1.is_null()) {
276 data1 = MaybeObjectHandle::Weak(holder);
277 } else {
278 data1 = maybe_data1;
279 }
280
281 int data_size = GetHandlerDataSize<StoreHandler>(
282 isolate, &smi_handler, receiver_map, data1, maybe_data2);
283
284 Handle<Object> validity_cell =
285 Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
286
287 Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(data_size);
288
289 handler->set_smi_handler(*smi_handler);
290 handler->set_validity_cell(*validity_cell);
291 InitPrototypeChecks(isolate, handler, receiver_map, data1, maybe_data2);
292 return handler;
293 }
294
295 // static
StoreGlobal(Handle<PropertyCell> cell)296 MaybeObjectHandle StoreHandler::StoreGlobal(Handle<PropertyCell> cell) {
297 return MaybeObjectHandle::Weak(cell);
298 }
299
300 // static
StoreProxy(Isolate * isolate,Handle<Map> receiver_map,Handle<JSProxy> proxy,Handle<JSReceiver> receiver)301 Handle<Object> StoreHandler::StoreProxy(Isolate* isolate,
302 Handle<Map> receiver_map,
303 Handle<JSProxy> proxy,
304 Handle<JSReceiver> receiver) {
305 Handle<Smi> smi_handler = StoreProxy(isolate);
306 if (receiver.is_identical_to(proxy)) return smi_handler;
307 return StoreThroughPrototype(isolate, receiver_map, proxy, smi_handler,
308 MaybeObjectHandle::Weak(proxy));
309 }
310
311 #if defined(OBJECT_PRINT)
312 namespace {
PrintSmiLoadHandler(int raw_handler,std::ostream & os)313 void PrintSmiLoadHandler(int raw_handler, std::ostream& os) {
314 LoadHandler::Kind kind = LoadHandler::KindBits::decode(raw_handler);
315 os << "kind = ";
316 switch (kind) {
317 case LoadHandler::Kind::kElement:
318 os << "kElement, allow out of bounds = "
319 << LoadHandler::AllowOutOfBoundsBits::decode(raw_handler)
320 << ", is JSArray = " << LoadHandler::IsJsArrayBits::decode(raw_handler)
321 << ", convert hole = "
322 << LoadHandler::ConvertHoleBits::decode(raw_handler)
323 << ", elements kind = "
324 << ElementsKindToString(
325 LoadHandler::ElementsKindBits::decode(raw_handler));
326 break;
327 case LoadHandler::Kind::kIndexedString:
328 os << "kIndexedString, allow out of bounds = "
329 << LoadHandler::AllowOutOfBoundsBits::decode(raw_handler);
330 break;
331 case LoadHandler::Kind::kNormal:
332 os << "kNormal";
333 break;
334 case LoadHandler::Kind::kGlobal:
335 os << "kGlobal";
336 break;
337 case LoadHandler::Kind::kField: {
338 CompactElementsKind compact_elements_kind =
339 LoadHandler::CompactElementsKindBits::decode(raw_handler);
340 os << "kField, is in object = "
341 << LoadHandler::IsInobjectBits::decode(raw_handler)
342 << ", is double = " << LoadHandler::IsDoubleBits::decode(raw_handler)
343 << ", field index = "
344 << LoadHandler::FieldIndexBits::decode(raw_handler)
345 << ", elements kind = "
346 << CompactElementsKindToString(compact_elements_kind);
347 break;
348 }
349 case LoadHandler::Kind::kConstantFromPrototype: {
350 CompactElementsKind compact_elements_kind =
351 LoadHandler::CompactElementsKindBits::decode(raw_handler);
352 os << "kConstantFromPrototype, elements kind = "
353 << CompactElementsKindToString(compact_elements_kind);
354 break;
355 }
356 case LoadHandler::Kind::kAccessor:
357 os << "kAccessor, descriptor = "
358 << LoadHandler::DescriptorBits::decode(raw_handler);
359 break;
360 case LoadHandler::Kind::kNativeDataProperty:
361 os << "kNativeDataProperty, descriptor = "
362 << LoadHandler::DescriptorBits::decode(raw_handler);
363 break;
364 case LoadHandler::Kind::kApiGetter:
365 os << "kApiGetter";
366 break;
367 case LoadHandler::Kind::kApiGetterHolderIsPrototype:
368 os << "kApiGetterHolderIsPrototype";
369 break;
370 case LoadHandler::Kind::kInterceptor:
371 os << "kInterceptor";
372 break;
373 case LoadHandler::Kind::kSlow:
374 os << "kSlow";
375 break;
376 case LoadHandler::Kind::kProxy:
377 os << "kProxy";
378 break;
379 case LoadHandler::Kind::kNonExistent:
380 os << "kNonExistent";
381 break;
382 case LoadHandler::Kind::kModuleExport:
383 os << "kModuleExport, exports index = "
384 << LoadHandler::ExportsIndexBits::decode(raw_handler);
385 break;
386 default:
387 UNREACHABLE();
388 }
389 }
390
KeyedAccessStoreModeToString(KeyedAccessStoreMode mode)391 const char* KeyedAccessStoreModeToString(KeyedAccessStoreMode mode) {
392 switch (mode) {
393 case STANDARD_STORE:
394 return "STANDARD_STORE";
395 case STORE_AND_GROW_HANDLE_COW:
396 return "STORE_AND_GROW_HANDLE_COW";
397 case STORE_IGNORE_OUT_OF_BOUNDS:
398 return "STORE_IGNORE_OUT_OF_BOUNDS";
399 case STORE_HANDLE_COW:
400 return "STORE_HANDLE_COW";
401 }
402 UNREACHABLE();
403 }
404
PrintSmiStoreHandler(int raw_handler,std::ostream & os)405 void PrintSmiStoreHandler(int raw_handler, std::ostream& os) {
406 StoreHandler::Kind kind = StoreHandler::KindBits::decode(raw_handler);
407 os << "kind = ";
408 switch (kind) {
409 case StoreHandler::Kind::kField:
410 case StoreHandler::Kind::kConstField: {
411 os << "k";
412 if (kind == StoreHandler::Kind::kConstField) {
413 os << "Const";
414 }
415 Representation representation = Representation::FromKind(
416 StoreHandler::RepresentationBits::decode(raw_handler));
417 os << "Field, descriptor = "
418 << StoreHandler::DescriptorBits::decode(raw_handler)
419 << ", is in object = "
420 << StoreHandler::IsInobjectBits::decode(raw_handler)
421 << ", representation = " << representation.Mnemonic()
422 << ", field index = "
423 << StoreHandler::FieldIndexBits::decode(raw_handler);
424 break;
425 }
426 case StoreHandler::Kind::kAccessor:
427 os << "kAccessor, descriptor = "
428 << StoreHandler::DescriptorBits::decode(raw_handler);
429 break;
430 case StoreHandler::Kind::kNativeDataProperty:
431 os << "kNativeDataProperty, descriptor = "
432 << StoreHandler::DescriptorBits::decode(raw_handler);
433 break;
434 case StoreHandler::Kind::kApiSetter:
435 os << "kApiSetter";
436 break;
437 case StoreHandler::Kind::kApiSetterHolderIsPrototype:
438 os << "kApiSetterHolderIsPrototype";
439 break;
440 case StoreHandler::Kind::kGlobalProxy:
441 os << "kGlobalProxy";
442 break;
443 case StoreHandler::Kind::kNormal:
444 os << "kNormal";
445 break;
446 case StoreHandler::Kind::kInterceptor:
447 os << "kInterceptor";
448 break;
449 case StoreHandler::Kind::kSlow: {
450 KeyedAccessStoreMode keyed_access_store_mode =
451 StoreHandler::KeyedAccessStoreModeBits::decode(raw_handler);
452 os << "kSlow, keyed access store mode = "
453 << KeyedAccessStoreModeToString(keyed_access_store_mode);
454 break;
455 }
456 case StoreHandler::Kind::kProxy:
457 os << "kProxy";
458 break;
459 default:
460 UNREACHABLE();
461 }
462 }
463
464 } // namespace
465
466 // static
PrintHandler(Object handler,std::ostream & os)467 void LoadHandler::PrintHandler(Object handler, std::ostream& os) {
468 DisallowHeapAllocation no_gc;
469 if (handler.IsSmi()) {
470 int raw_handler = handler.ToSmi().value();
471 os << "LoadHandler(Smi)(";
472 PrintSmiLoadHandler(raw_handler, os);
473 os << ")" << std::endl;
474 } else {
475 LoadHandler load_handler = LoadHandler::cast(handler);
476 int raw_handler = load_handler.smi_handler().ToSmi().value();
477 os << "LoadHandler(do access check on lookup start object = "
478 << DoAccessCheckOnLookupStartObjectBits::decode(raw_handler)
479 << ", lookup on lookup start object = "
480 << LookupOnLookupStartObjectBits::decode(raw_handler) << ", ";
481 PrintSmiLoadHandler(raw_handler, os);
482 DCHECK_GE(load_handler.data_field_count(), 1);
483 os << ", data1 = ";
484 load_handler.data1().ShortPrint(os);
485 if (load_handler.data_field_count() >= 2) {
486 os << ", data2 = ";
487 load_handler.data2().ShortPrint(os);
488 }
489 if (load_handler.data_field_count() >= 3) {
490 os << ", data3 = ";
491 load_handler.data3().ShortPrint(os);
492 }
493 os << ", validity cell = ";
494 load_handler.validity_cell().ShortPrint(os);
495 os << ")" << std::endl;
496 }
497 }
498
PrintHandler(Object handler,std::ostream & os)499 void StoreHandler::PrintHandler(Object handler, std::ostream& os) {
500 DisallowHeapAllocation no_gc;
501 if (handler.IsSmi()) {
502 int raw_handler = handler.ToSmi().value();
503 os << "StoreHandler(Smi)(";
504 PrintSmiStoreHandler(raw_handler, os);
505 os << ")" << std::endl;
506 } else {
507 os << "StoreHandler(";
508 StoreHandler store_handler = StoreHandler::cast(handler);
509 if (store_handler.smi_handler().IsCode()) {
510 Code code = Code::cast(store_handler.smi_handler());
511 os << "builtin = ";
512 code.ShortPrint(os);
513 } else {
514 int raw_handler = store_handler.smi_handler().ToSmi().value();
515 os << "do access check on lookup start object = "
516 << DoAccessCheckOnLookupStartObjectBits::decode(raw_handler)
517 << ", lookup on lookup start object = "
518 << LookupOnLookupStartObjectBits::decode(raw_handler) << ", ";
519 PrintSmiStoreHandler(raw_handler, os);
520 }
521 DCHECK_GE(store_handler.data_field_count(), 1);
522 os << ", data1 = ";
523 store_handler.data1().ShortPrint(os);
524 if (store_handler.data_field_count() >= 2) {
525 os << ", data2 = ";
526 store_handler.data2().ShortPrint(os);
527 }
528 if (store_handler.data_field_count() >= 3) {
529 os << ", data3 = ";
530 store_handler.data3().ShortPrint(os);
531 }
532 os << ", validity cell = ";
533 store_handler.validity_cell().ShortPrint(os);
534 os << ")" << std::endl;
535 }
536 }
537 #endif // defined(OBJECT_PRINT)
538
539 } // namespace internal
540 } // namespace v8
541