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