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-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/transitions.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 : namespace {
17 :
18 : template <typename BitField>
19 40735 : Handle<Smi> SetBitFieldValue(Isolate* isolate, Handle<Smi> smi_handler,
20 : typename BitField::FieldType value) {
21 : int config = smi_handler->value();
22 81470 : config = BitField::update(config, true);
23 40736 : 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>
29 1213860 : 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 2359050 : 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 39051 : Handle<Context> native_context = isolate->native_context();
48 78102 : handler->set_data2(HeapObjectReference::Weak(*native_context));
49 : } else {
50 : // Enable access checks on receiver.
51 : typedef typename ICHandler::DoAccessCheckOnReceiverBits Bit;
52 39050 : *smi_handler = SetBitFieldValue<Bit>(isolate, *smi_handler, true);
53 : }
54 : checks_count++;
55 1747877 : } 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 1685 : *smi_handler = SetBitFieldValue<Bit>(isolate, *smi_handler, true);
61 : }
62 : }
63 : if (fill_handler) {
64 1213121 : handler->set_data1(*data1);
65 : }
66 1213864 : 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 3886 : if (checks_count == 0) {
71 7752 : handler->set_data2(*maybe_data2);
72 : } else {
73 : DCHECK_EQ(1, checks_count);
74 20 : handler->set_data3(*maybe_data2);
75 : }
76 : }
77 7771 : checks_count++;
78 : }
79 1213864 : 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>
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 607299 : holder, data1, maybe_data2);
97 : }
98 :
99 : template <typename ICHandler>
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 606563 : InitPrototypeChecksImpl<ICHandler, true>(
105 : isolate, handler, nullptr, receiver_map, holder, data1, maybe_data2);
106 : }
107 :
108 : } // namespace
109 :
110 : // static
111 363642 : 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 363642 : if (maybe_data1.is_null()) {
119 : data1 = MaybeObjectHandle::Weak(holder);
120 : } else {
121 16232 : 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 363645 : Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
129 :
130 363643 : int data_count = 1 + checks_count;
131 363643 : Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_count);
132 :
133 727285 : handler->set_smi_handler(*smi_handler);
134 363644 : handler->set_validity_cell(*validity_cell);
135 : InitPrototypeChecks(isolate, handler, receiver_map, holder, data1,
136 : maybe_data2);
137 363647 : return handler;
138 : }
139 :
140 : // static
141 73900 : Handle<Object> LoadHandler::LoadFullChain(Isolate* isolate,
142 : Handle<Map> receiver_map,
143 : const MaybeObjectHandle& holder,
144 : Handle<Smi> smi_handler) {
145 : Handle<JSReceiver> end; // null handle, means full prototype chain lookup.
146 73900 : MaybeObjectHandle data1 = holder;
147 : int checks_count = GetPrototypeCheckCount<LoadHandler>(
148 : isolate, &smi_handler, receiver_map, end, data1);
149 :
150 : Handle<Object> validity_cell =
151 73900 : Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
152 147800 : 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 1482 : if (!LookupOnReceiverBits::decode(smi_handler->value())) return smi_handler;
156 : }
157 :
158 73159 : int data_count = 1 + checks_count;
159 73159 : Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_count);
160 :
161 146318 : handler->set_smi_handler(*smi_handler);
162 73159 : handler->set_validity_cell(*validity_cell);
163 : InitPrototypeChecks(isolate, handler, receiver_map, end, data1);
164 73159 : return handler;
165 : }
166 :
167 : // static
168 18845 : KeyedAccessLoadMode LoadHandler::GetKeyedAccessLoadMode(MaybeObject handler) {
169 : DisallowHeapAllocation no_gc;
170 18845 : if (handler->IsSmi()) {
171 37406 : int const raw_handler = handler.ToSmi().value();
172 18703 : Kind const kind = KindBits::decode(raw_handler);
173 37405 : 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
182 1674 : Handle<Object> StoreHandler::StoreElementTransition(
183 : Isolate* isolate, Handle<Map> receiver_map, Handle<Map> transition,
184 : KeyedAccessStoreMode store_mode) {
185 : Handle<Code> stub =
186 3348 : CodeFactory::ElementsTransitionAndStore(isolate, store_mode).code();
187 : Handle<Object> validity_cell =
188 1674 : Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
189 1674 : Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(1);
190 3348 : handler->set_smi_handler(*stub);
191 1674 : handler->set_validity_cell(*validity_cell);
192 3348 : handler->set_data1(HeapObjectReference::Weak(*transition));
193 1674 : return handler;
194 : }
195 :
196 422551 : MaybeObjectHandle StoreHandler::StoreTransition(Isolate* isolate,
197 : Handle<Map> transition_map) {
198 422552 : bool is_dictionary_map = transition_map->is_dictionary_map();
199 : #ifdef DEBUG
200 : if (!is_dictionary_map) {
201 : int descriptor = transition_map->LastAdded();
202 : Handle<DescriptorArray> descriptors(transition_map->instance_descriptors(),
203 : isolate);
204 : PropertyDetails details = descriptors->GetDetails(descriptor);
205 : if (descriptors->GetKey(descriptor)->IsPrivate()) {
206 : DCHECK_EQ(DONT_ENUM, details.attributes());
207 : } else {
208 : DCHECK_EQ(NONE, details.attributes());
209 : }
210 : Representation representation = details.representation();
211 : DCHECK(!representation.IsNone());
212 : }
213 : #endif
214 : // Declarative handlers don't support access checks.
215 : DCHECK(!transition_map->is_access_check_needed());
216 :
217 : // Get validity cell value if it is necessary for the handler.
218 : Handle<Object> validity_cell;
219 805708 : if (is_dictionary_map || !transition_map->IsPrototypeValidityCellValid()) {
220 : validity_cell =
221 396374 : Map::GetOrCreatePrototypeChainValidityCell(transition_map, isolate);
222 : }
223 :
224 422551 : if (is_dictionary_map) {
225 : DCHECK(!transition_map->IsJSGlobalObjectMap());
226 39394 : Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(0);
227 : // Store normal with enabled lookup on receiver.
228 : int config = KindBits::encode(kNormal) | LookupOnReceiverBits::encode(true);
229 78788 : handler->set_smi_handler(Smi::FromInt(config));
230 39394 : handler->set_validity_cell(*validity_cell);
231 39394 : return MaybeObjectHandle(handler);
232 :
233 : } else {
234 : // Ensure the transition map contains a valid prototype validity cell.
235 383157 : if (!validity_cell.is_null()) {
236 356978 : transition_map->set_prototype_validity_cell(*validity_cell);
237 : }
238 : return MaybeObjectHandle::Weak(transition_map);
239 : }
240 : }
241 :
242 : // static
243 169757 : Handle<Object> StoreHandler::StoreThroughPrototype(
244 : Isolate* isolate, Handle<Map> receiver_map, Handle<JSReceiver> holder,
245 : Handle<Smi> smi_handler, MaybeObjectHandle maybe_data1,
246 : MaybeObjectHandle maybe_data2) {
247 : MaybeObjectHandle data1;
248 169757 : if (maybe_data1.is_null()) {
249 : data1 = MaybeObjectHandle::Weak(holder);
250 : } else {
251 42539 : data1 = maybe_data1;
252 : }
253 :
254 : int checks_count = GetPrototypeCheckCount<StoreHandler>(
255 : isolate, &smi_handler, receiver_map, holder, data1, maybe_data2);
256 :
257 : Handle<Object> validity_cell =
258 169757 : Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
259 : DCHECK_IMPLIES(validity_cell->IsSmi(), checks_count == 0);
260 :
261 169757 : int data_count = 1 + checks_count;
262 : Handle<StoreHandler> handler =
263 169757 : isolate->factory()->NewStoreHandler(data_count);
264 :
265 339514 : handler->set_smi_handler(*smi_handler);
266 169757 : handler->set_validity_cell(*validity_cell);
267 : InitPrototypeChecks(isolate, handler, receiver_map, holder, data1,
268 : maybe_data2);
269 169757 : return handler;
270 : }
271 :
272 : // static
273 582 : MaybeObjectHandle StoreHandler::StoreGlobal(Handle<PropertyCell> cell) {
274 582 : return MaybeObjectHandle::Weak(cell);
275 : }
276 :
277 : // static
278 371 : Handle<Object> StoreHandler::StoreProxy(Isolate* isolate,
279 : Handle<Map> receiver_map,
280 : Handle<JSProxy> proxy,
281 : Handle<JSReceiver> receiver) {
282 371 : Handle<Smi> smi_handler = StoreProxy(isolate);
283 371 : if (receiver.is_identical_to(proxy)) return smi_handler;
284 : return StoreThroughPrototype(isolate, receiver_map, proxy, smi_handler,
285 207 : MaybeObjectHandle::Weak(proxy));
286 : }
287 :
288 : } // namespace internal
289 178779 : } // namespace v8
|