• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/objects/data-handler-inl.h"
10 #include "src/objects/maybe-object.h"
11 #include "src/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> receiver_map,Handle<JSReceiver> holder,MaybeObjectHandle data1,MaybeObjectHandle maybe_data2)29 int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler,
30                             Handle<Smi>* smi_handler, Handle<Map> receiver_map,
31                             Handle<JSReceiver> holder, MaybeObjectHandle data1,
32                             MaybeObjectHandle maybe_data2) {
33   int checks_count = 0;
34   // Holder-is-receiver case itself does not add entries unless there is an
35   // optional data2 value provided.
36 
37   if (receiver_map->IsPrimitiveMap() ||
38       receiver_map->is_access_check_needed()) {
39     DCHECK(!receiver_map->IsJSGlobalObjectMap());
40     // The validity cell check for primitive and global proxy receivers does
41     // not guarantee that certain native context ever had access to other
42     // native context. However, a handler created for one native context could
43     // be used in other native context through the megamorphic stub cache.
44     // So we record the original native context to which this handler
45     // corresponds.
46     if (fill_handler) {
47       Handle<Context> native_context = isolate->native_context();
48       handler->set_data2(HeapObjectReference::Weak(*native_context));
49     } else {
50       // Enable access checks on receiver.
51       typedef typename ICHandler::DoAccessCheckOnReceiverBits Bit;
52       *smi_handler = SetBitFieldValue<Bit>(isolate, *smi_handler, true);
53     }
54     checks_count++;
55   } else if (receiver_map->is_dictionary_map() &&
56              !receiver_map->IsJSGlobalObjectMap()) {
57     if (!fill_handler) {
58       // Enable lookup on receiver.
59       typedef typename ICHandler::LookupOnReceiverBits Bit;
60       *smi_handler = SetBitFieldValue<Bit>(isolate, *smi_handler, true);
61     }
62   }
63   if (fill_handler) {
64     handler->set_data1(*data1);
65   }
66   if (!maybe_data2.is_null()) {
67     if (fill_handler) {
68       // This value will go either to data2 or data3 slot depending on whether
69       // data2 slot is already occupied by native context.
70       if (checks_count == 0) {
71         handler->set_data2(*maybe_data2);
72       } else {
73         DCHECK_EQ(1, checks_count);
74         handler->set_data3(*maybe_data2);
75       }
76     }
77     checks_count++;
78   }
79   return checks_count;
80 }
81 
82 // Returns 0 if the validity cell check is enough to ensure that the
83 // prototype chain from |receiver_map| till |holder| did not change.
84 // If the |holder| is an empty handle then the full prototype chain is
85 // checked.
86 // Returns -1 if the handler has to be compiled or the number of prototype
87 // checks otherwise.
88 template <typename ICHandler>
GetPrototypeCheckCount(Isolate * isolate,Handle<Smi> * smi_handler,Handle<Map> receiver_map,Handle<JSReceiver> holder,MaybeObjectHandle data1,MaybeObjectHandle maybe_data2=MaybeObjectHandle ())89 int GetPrototypeCheckCount(
90     Isolate* isolate, Handle<Smi>* smi_handler, Handle<Map> receiver_map,
91     Handle<JSReceiver> holder, MaybeObjectHandle data1,
92     MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) {
93   DCHECK_NOT_NULL(smi_handler);
94   return InitPrototypeChecksImpl<ICHandler, false>(isolate, Handle<ICHandler>(),
95                                                    smi_handler, receiver_map,
96                                                    holder, data1, maybe_data2);
97 }
98 
99 template <typename ICHandler>
InitPrototypeChecks(Isolate * isolate,Handle<ICHandler> handler,Handle<Map> receiver_map,Handle<JSReceiver> holder,MaybeObjectHandle data1,MaybeObjectHandle maybe_data2=MaybeObjectHandle ())100 void InitPrototypeChecks(Isolate* isolate, Handle<ICHandler> handler,
101                          Handle<Map> receiver_map, Handle<JSReceiver> holder,
102                          MaybeObjectHandle data1,
103                          MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) {
104   InitPrototypeChecksImpl<ICHandler, true>(
105       isolate, handler, nullptr, receiver_map, holder, data1, maybe_data2);
106 }
107 
108 }  // namespace
109 
110 // static
LoadFromPrototype(Isolate * isolate,Handle<Map> receiver_map,Handle<JSReceiver> holder,Handle<Smi> smi_handler,MaybeObjectHandle maybe_data1,MaybeObjectHandle maybe_data2)111 Handle<Object> LoadHandler::LoadFromPrototype(Isolate* isolate,
112                                               Handle<Map> receiver_map,
113                                               Handle<JSReceiver> holder,
114                                               Handle<Smi> smi_handler,
115                                               MaybeObjectHandle maybe_data1,
116                                               MaybeObjectHandle maybe_data2) {
117   MaybeObjectHandle data1;
118   if (maybe_data1.is_null()) {
119     data1 = MaybeObjectHandle::Weak(holder);
120   } else {
121     data1 = maybe_data1;
122   }
123 
124   int checks_count = GetPrototypeCheckCount<LoadHandler>(
125       isolate, &smi_handler, receiver_map, holder, data1, maybe_data2);
126 
127   Handle<Object> validity_cell =
128       Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
129 
130   int data_count = 1 + checks_count;
131   Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_count);
132 
133   handler->set_smi_handler(*smi_handler);
134   handler->set_validity_cell(*validity_cell);
135   InitPrototypeChecks(isolate, handler, receiver_map, holder, data1,
136                       maybe_data2);
137   return handler;
138 }
139 
140 // static
LoadFullChain(Isolate * isolate,Handle<Map> receiver_map,MaybeObjectHandle holder,Handle<Smi> smi_handler)141 Handle<Object> LoadHandler::LoadFullChain(Isolate* isolate,
142                                           Handle<Map> receiver_map,
143                                           MaybeObjectHandle holder,
144                                           Handle<Smi> smi_handler) {
145   Handle<JSReceiver> end;  // null handle, means full prototype chain lookup.
146   MaybeObjectHandle data1 = holder;
147   int checks_count = GetPrototypeCheckCount<LoadHandler>(
148       isolate, &smi_handler, receiver_map, end, data1);
149 
150   Handle<Object> validity_cell =
151       Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
152   if (validity_cell->IsSmi()) {
153     DCHECK_EQ(0, checks_count);
154     // Lookup on receiver isn't supported in case of a simple smi handler.
155     if (!LookupOnReceiverBits::decode(smi_handler->value())) return smi_handler;
156   }
157 
158   int data_count = 1 + checks_count;
159   Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_count);
160 
161   handler->set_smi_handler(*smi_handler);
162   handler->set_validity_cell(*validity_cell);
163   InitPrototypeChecks(isolate, handler, receiver_map, end, data1);
164   return handler;
165 }
166 
167 // static
GetKeyedAccessLoadMode(MaybeObject * handler)168 KeyedAccessLoadMode LoadHandler::GetKeyedAccessLoadMode(MaybeObject* handler) {
169   DisallowHeapAllocation no_gc;
170   if (handler->IsSmi()) {
171     int const raw_handler = Smi::cast(handler->ToSmi())->value();
172     Kind const kind = KindBits::decode(raw_handler);
173     if ((kind == kElement || kind == kIndexedString) &&
174         AllowOutOfBoundsBits::decode(raw_handler)) {
175       return LOAD_IGNORE_OUT_OF_BOUNDS;
176     }
177   }
178   return STANDARD_LOAD;
179 }
180 
181 // static
StoreElementTransition(Isolate * isolate,Handle<Map> receiver_map,Handle<Map> transition,KeyedAccessStoreMode store_mode)182 Handle<Object> StoreHandler::StoreElementTransition(
183     Isolate* isolate, Handle<Map> receiver_map, Handle<Map> transition,
184     KeyedAccessStoreMode store_mode) {
185   bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
186   ElementsKind elements_kind = receiver_map->elements_kind();
187   Handle<Code> stub = ElementsTransitionAndStoreStub(
188                           isolate, elements_kind, transition->elements_kind(),
189                           is_js_array, store_mode)
190                           .GetCode();
191   Handle<Object> validity_cell =
192       Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
193   Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(1);
194   handler->set_smi_handler(*stub);
195   handler->set_validity_cell(*validity_cell);
196   handler->set_data1(HeapObjectReference::Weak(*transition));
197   return handler;
198 }
199 
StoreTransition(Isolate * isolate,Handle<Map> transition_map)200 MaybeObjectHandle StoreHandler::StoreTransition(Isolate* isolate,
201                                                 Handle<Map> transition_map) {
202   bool is_dictionary_map = transition_map->is_dictionary_map();
203 #ifdef DEBUG
204   if (!is_dictionary_map) {
205     int descriptor = transition_map->LastAdded();
206     Handle<DescriptorArray> descriptors(transition_map->instance_descriptors(),
207                                         isolate);
208     PropertyDetails details = descriptors->GetDetails(descriptor);
209     if (descriptors->GetKey(descriptor)->IsPrivate()) {
210       DCHECK_EQ(DONT_ENUM, details.attributes());
211     } else {
212       DCHECK_EQ(NONE, details.attributes());
213     }
214     Representation representation = details.representation();
215     DCHECK(!representation.IsNone());
216   }
217 #endif
218   // Declarative handlers don't support access checks.
219   DCHECK(!transition_map->is_access_check_needed());
220 
221   // Get validity cell value if it is necessary for the handler.
222   Handle<Object> validity_cell;
223   if (is_dictionary_map || !transition_map->IsPrototypeValidityCellValid()) {
224     validity_cell =
225         Map::GetOrCreatePrototypeChainValidityCell(transition_map, isolate);
226   }
227 
228   if (is_dictionary_map) {
229     DCHECK(!transition_map->IsJSGlobalObjectMap());
230     Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(0);
231     // Store normal with enabled lookup on receiver.
232     int config = KindBits::encode(kNormal) | LookupOnReceiverBits::encode(true);
233     handler->set_smi_handler(Smi::FromInt(config));
234     handler->set_validity_cell(*validity_cell);
235     return MaybeObjectHandle(handler);
236 
237   } else {
238     // Ensure the transition map contains a valid prototype validity cell.
239     if (!validity_cell.is_null()) {
240       transition_map->set_prototype_validity_cell(*validity_cell);
241     }
242     return MaybeObjectHandle::Weak(transition_map);
243   }
244 }
245 
246 // static
StoreThroughPrototype(Isolate * isolate,Handle<Map> receiver_map,Handle<JSReceiver> holder,Handle<Smi> smi_handler,MaybeObjectHandle maybe_data1,MaybeObjectHandle maybe_data2)247 Handle<Object> StoreHandler::StoreThroughPrototype(
248     Isolate* isolate, Handle<Map> receiver_map, Handle<JSReceiver> holder,
249     Handle<Smi> smi_handler, MaybeObjectHandle maybe_data1,
250     MaybeObjectHandle maybe_data2) {
251   MaybeObjectHandle data1;
252   if (maybe_data1.is_null()) {
253     data1 = MaybeObjectHandle::Weak(holder);
254   } else {
255     data1 = maybe_data1;
256   }
257 
258   int checks_count = GetPrototypeCheckCount<StoreHandler>(
259       isolate, &smi_handler, receiver_map, holder, data1, maybe_data2);
260 
261   Handle<Object> validity_cell =
262       Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
263   DCHECK_IMPLIES(validity_cell->IsSmi(), checks_count == 0);
264 
265   int data_count = 1 + checks_count;
266   Handle<StoreHandler> handler =
267       isolate->factory()->NewStoreHandler(data_count);
268 
269   handler->set_smi_handler(*smi_handler);
270   handler->set_validity_cell(*validity_cell);
271   InitPrototypeChecks(isolate, handler, receiver_map, holder, data1,
272                       maybe_data2);
273   return handler;
274 }
275 
276 // static
StoreGlobal(Handle<PropertyCell> cell)277 MaybeObjectHandle StoreHandler::StoreGlobal(Handle<PropertyCell> cell) {
278   return MaybeObjectHandle::Weak(cell);
279 }
280 
281 // static
StoreProxy(Isolate * isolate,Handle<Map> receiver_map,Handle<JSProxy> proxy,Handle<JSReceiver> receiver)282 Handle<Object> StoreHandler::StoreProxy(Isolate* isolate,
283                                         Handle<Map> receiver_map,
284                                         Handle<JSProxy> proxy,
285                                         Handle<JSReceiver> receiver) {
286   Handle<Smi> smi_handler = StoreProxy(isolate);
287   if (receiver.is_identical_to(proxy)) return smi_handler;
288   return StoreThroughPrototype(isolate, receiver_map, proxy, smi_handler,
289                                MaybeObjectHandle::Weak(proxy));
290 }
291 
292 }  // namespace internal
293 }  // namespace v8
294