1 // Copyright 2016 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/keyed-store-generic.h"
6
7 #include "src/codegen/code-factory.h"
8 #include "src/codegen/code-stub-assembler.h"
9 #include "src/codegen/interface-descriptors.h"
10 #include "src/execution/isolate.h"
11 #include "src/ic/accessor-assembler.h"
12 #include "src/objects/contexts.h"
13 #include "src/objects/feedback-vector.h"
14 #include "src/objects/objects-inl.h"
15
16 namespace v8 {
17 namespace internal {
18
19 enum class StoreMode {
20 // kSet implements [[Set]] in the spec and traverses the prototype
21 // chain to invoke setters. it's used by KeyedStoreIC and StoreIC to
22 // set the properties when there is no feedback.
23 kSet,
24 // kDefineKeyedOwnInLiteral implements [[CreateDataProperty]] in the spec,
25 // and it assumes that the receiver is a JSObject that is created by us.
26 // It is used by Object.fromEntries(), CloneObjectIC and
27 // StoreInArrayLiteralIC to define a property in an object without
28 // traversing the prototype chain.
29 // TODO(v8:12548): merge this into the more generic kDefineKeyedOwn.
30 kDefineKeyedOwnInLiteral,
31 // kDefineNamedOwn implements [[CreateDataProperty]] but it can deal with
32 // user-defined receivers such as a JSProxy. It also assumes that the key
33 // is statically known. It's used to initialize named roperties in object
34 // literals and named public class fields.
35 kDefineNamedOwn,
36 // kDefineKeyedOwn implements [[CreateDataProperty]], but it can deal with
37 // user-defined receivers such as a JSProxy, and for private class fields,
38 // it will throw if the field does already exist. It's different from
39 // kDefineNamedOwn in that it does not assume the key is statically known.
40 // It's used to initialized computed public class fields and private
41 // class fields.
42 kDefineKeyedOwn
43 };
44
45 // With private symbols, 'define' semantics will throw if the field already
46 // exists, while 'update' semantics will throw if the field does not exist.
47 enum class PrivateNameSemantics { kUpdate, kDefine };
48
49 class KeyedStoreGenericAssembler : public AccessorAssembler {
50 public:
KeyedStoreGenericAssembler(compiler::CodeAssemblerState * state,StoreMode mode)51 explicit KeyedStoreGenericAssembler(compiler::CodeAssemblerState* state,
52 StoreMode mode)
53 : AccessorAssembler(state), mode_(mode) {}
54
55 void KeyedStoreGeneric();
56
57 void StoreIC_NoFeedback();
58
59 // Generates code for [[Set]] or [[CreateDataProperty]] operation,
60 // the |unique_name| is supposed to be unique otherwise this code will
61 // always go to runtime.
62 void StoreProperty(TNode<Context> context, TNode<JSReceiver> receiver,
63 TNode<BoolT> is_simple_receiver, TNode<Name> unique_name,
64 TNode<Object> value, LanguageMode language_mode);
65
66 // This does [[Set]] or [[CreateDataProperty]] but it's more generic than
67 // the above. It is essentially the same as "KeyedStoreGeneric" but does not
68 // use feedback slot and uses a hardcoded LanguageMode instead of trying
69 // to deduce it from the feedback slot's kind.
70 void StoreProperty(TNode<Context> context, TNode<Object> receiver,
71 TNode<Object> key, TNode<Object> value,
72 LanguageMode language_mode);
73
74 private:
75 StoreMode mode_;
76
77 enum UpdateLength {
78 kDontChangeLength,
79 kIncrementLengthByOne,
80 kBumpLengthWithGap
81 };
82
83 enum UseStubCache { kUseStubCache, kDontUseStubCache };
84
85 // Helper that is used by the public KeyedStoreGeneric and by StoreProperty.
86 void KeyedStoreGeneric(TNode<Context> context, TNode<Object> receiver,
87 TNode<Object> key, TNode<Object> value,
88 Maybe<LanguageMode> language_mode);
89
90 void EmitGenericElementStore(TNode<JSObject> receiver,
91 TNode<Map> receiver_map,
92 TNode<Uint16T> instance_type,
93 TNode<IntPtrT> index, TNode<Object> value,
94 TNode<Context> context, Label* slow);
95
96 // If language mode is not provided it is deduced from the feedback slot's
97 // kind.
98 void EmitGenericPropertyStore(TNode<JSReceiver> receiver,
99 TNode<Map> receiver_map,
100 TNode<Uint16T> instance_type,
101 const StoreICParameters* p,
102 ExitPoint* exit_point, Label* slow,
103 Maybe<LanguageMode> maybe_language_mode);
104
EmitGenericPropertyStore(TNode<JSReceiver> receiver,TNode<Map> receiver_map,TNode<Uint16T> instance_type,const StoreICParameters * p,Label * slow)105 void EmitGenericPropertyStore(TNode<JSReceiver> receiver,
106 TNode<Map> receiver_map,
107 TNode<Uint16T> instance_type,
108 const StoreICParameters* p, Label* slow) {
109 ExitPoint direct_exit(this);
110 EmitGenericPropertyStore(receiver, receiver_map, instance_type, p,
111 &direct_exit, slow, Nothing<LanguageMode>());
112 }
113
114 void BranchIfPrototypesMayHaveReadOnlyElements(
115 TNode<Map> receiver_map, Label* maybe_read_only_elements,
116 Label* only_fast_writable_elements);
117
118 void TryRewriteElements(TNode<JSObject> receiver, TNode<Map> receiver_map,
119 TNode<FixedArrayBase> elements,
120 TNode<NativeContext> native_context,
121 ElementsKind from_kind, ElementsKind to_kind,
122 Label* bailout);
123
124 void StoreElementWithCapacity(TNode<JSObject> receiver,
125 TNode<Map> receiver_map,
126 TNode<FixedArrayBase> elements,
127 TNode<Word32T> elements_kind,
128 TNode<IntPtrT> index, TNode<Object> value,
129 TNode<Context> context, Label* slow,
130 UpdateLength update_length);
131
132 void MaybeUpdateLengthAndReturn(TNode<JSObject> receiver,
133 TNode<IntPtrT> index, TNode<Object> value,
134 UpdateLength update_length);
135
136 void TryChangeToHoleyMapHelper(TNode<JSObject> receiver,
137 TNode<Map> receiver_map,
138 TNode<NativeContext> native_context,
139 ElementsKind packed_kind,
140 ElementsKind holey_kind, Label* done,
141 Label* map_mismatch, Label* bailout);
142 void TryChangeToHoleyMap(TNode<JSObject> receiver, TNode<Map> receiver_map,
143 TNode<Word32T> current_elements_kind,
144 TNode<Context> context, ElementsKind packed_kind,
145 Label* bailout);
146 void TryChangeToHoleyMapMulti(TNode<JSObject> receiver,
147 TNode<Map> receiver_map,
148 TNode<Word32T> current_elements_kind,
149 TNode<Context> context,
150 ElementsKind packed_kind,
151 ElementsKind packed_kind_2, Label* bailout);
152
153 void LookupPropertyOnPrototypeChain(
154 TNode<Map> receiver_map, TNode<Name> name, Label* accessor,
155 TVariable<Object>* var_accessor_pair,
156 TVariable<HeapObject>* var_accessor_holder, Label* readonly,
157 Label* bailout);
158
159 TNode<Map> FindCandidateStoreICTransitionMapHandler(TNode<Map> map,
160 TNode<Name> name,
161 Label* slow);
162
IsSet() const163 bool IsSet() const { return mode_ == StoreMode::kSet; }
IsDefineKeyedOwnInLiteral() const164 bool IsDefineKeyedOwnInLiteral() const {
165 return mode_ == StoreMode::kDefineKeyedOwnInLiteral;
166 }
IsDefineNamedOwn() const167 bool IsDefineNamedOwn() const { return mode_ == StoreMode::kDefineNamedOwn; }
IsDefineKeyedOwn() const168 bool IsDefineKeyedOwn() const { return mode_ == StoreMode::kDefineKeyedOwn; }
IsAnyDefineOwn() const169 bool IsAnyDefineOwn() const {
170 return IsDefineNamedOwn() || IsDefineKeyedOwn();
171 }
172
ShouldCheckPrototype() const173 bool ShouldCheckPrototype() const { return IsSet(); }
ShouldReconfigureExisting() const174 bool ShouldReconfigureExisting() const { return IsDefineKeyedOwnInLiteral(); }
ShouldCallSetter() const175 bool ShouldCallSetter() const { return IsSet(); }
ShouldCheckPrototypeValidity() const176 bool ShouldCheckPrototypeValidity() const {
177 // We don't do this for "in-literal" stores, because it is impossible for
178 // the target object to be a "prototype".
179 // We don't need the prototype validity check for "own" stores, because
180 // we don't care about the prototype chain.
181 // Thus, we need the prototype check only for ordinary stores.
182 DCHECK_IMPLIES(!IsSet(), IsDefineKeyedOwnInLiteral() ||
183 IsDefineNamedOwn() || IsDefineKeyedOwn());
184 return IsSet();
185 }
186 };
187
Generate(compiler::CodeAssemblerState * state)188 void KeyedStoreGenericGenerator::Generate(compiler::CodeAssemblerState* state) {
189 KeyedStoreGenericAssembler assembler(state, StoreMode::kSet);
190 assembler.KeyedStoreGeneric();
191 }
192
Generate(compiler::CodeAssemblerState * state)193 void DefineKeyedOwnGenericGenerator::Generate(
194 compiler::CodeAssemblerState* state) {
195 KeyedStoreGenericAssembler assembler(state, StoreMode::kDefineKeyedOwn);
196 assembler.KeyedStoreGeneric();
197 }
198
Generate(compiler::CodeAssemblerState * state)199 void StoreICNoFeedbackGenerator::Generate(compiler::CodeAssemblerState* state) {
200 KeyedStoreGenericAssembler assembler(state, StoreMode::kSet);
201 assembler.StoreIC_NoFeedback();
202 }
203
Generate(compiler::CodeAssemblerState * state)204 void DefineNamedOwnICNoFeedbackGenerator::Generate(
205 compiler::CodeAssemblerState* state) {
206 // TODO(v8:12548): it's a hack to reuse KeyedStoreGenericAssembler for
207 // DefineNamedOwnIC, we should separate it out.
208 KeyedStoreGenericAssembler assembler(state, StoreMode::kDefineNamedOwn);
209 assembler.StoreIC_NoFeedback();
210 }
211
SetProperty(compiler::CodeAssemblerState * state,TNode<Context> context,TNode<JSReceiver> receiver,TNode<BoolT> is_simple_receiver,TNode<Name> name,TNode<Object> value,LanguageMode language_mode)212 void KeyedStoreGenericGenerator::SetProperty(
213 compiler::CodeAssemblerState* state, TNode<Context> context,
214 TNode<JSReceiver> receiver, TNode<BoolT> is_simple_receiver,
215 TNode<Name> name, TNode<Object> value, LanguageMode language_mode) {
216 KeyedStoreGenericAssembler assembler(state, StoreMode::kSet);
217 assembler.StoreProperty(context, receiver, is_simple_receiver, name, value,
218 language_mode);
219 }
220
SetProperty(compiler::CodeAssemblerState * state,TNode<Context> context,TNode<Object> receiver,TNode<Object> key,TNode<Object> value,LanguageMode language_mode)221 void KeyedStoreGenericGenerator::SetProperty(
222 compiler::CodeAssemblerState* state, TNode<Context> context,
223 TNode<Object> receiver, TNode<Object> key, TNode<Object> value,
224 LanguageMode language_mode) {
225 KeyedStoreGenericAssembler assembler(state, StoreMode::kSet);
226 assembler.StoreProperty(context, receiver, key, value, language_mode);
227 }
228
CreateDataProperty(compiler::CodeAssemblerState * state,TNode<Context> context,TNode<JSObject> receiver,TNode<Object> key,TNode<Object> value)229 void KeyedStoreGenericGenerator::CreateDataProperty(
230 compiler::CodeAssemblerState* state, TNode<Context> context,
231 TNode<JSObject> receiver, TNode<Object> key, TNode<Object> value) {
232 KeyedStoreGenericAssembler assembler(state,
233 StoreMode::kDefineKeyedOwnInLiteral);
234 assembler.StoreProperty(context, receiver, key, value, LanguageMode::kStrict);
235 }
236
BranchIfPrototypesMayHaveReadOnlyElements(TNode<Map> receiver_map,Label * maybe_read_only_elements,Label * only_fast_writable_elements)237 void KeyedStoreGenericAssembler::BranchIfPrototypesMayHaveReadOnlyElements(
238 TNode<Map> receiver_map, Label* maybe_read_only_elements,
239 Label* only_fast_writable_elements) {
240 TVARIABLE(Map, var_map);
241 var_map = receiver_map;
242 Label loop_body(this, &var_map);
243 Goto(&loop_body);
244
245 BIND(&loop_body);
246 {
247 TNode<Map> map = var_map.value();
248 TNode<HeapObject> prototype = LoadMapPrototype(map);
249 GotoIf(IsNull(prototype), only_fast_writable_elements);
250 TNode<Map> prototype_map = LoadMap(prototype);
251 var_map = prototype_map;
252 TNode<Uint16T> instance_type = LoadMapInstanceType(prototype_map);
253 GotoIf(IsCustomElementsReceiverInstanceType(instance_type),
254 maybe_read_only_elements);
255 TNode<Int32T> elements_kind = LoadMapElementsKind(prototype_map);
256 GotoIf(IsFastOrNonExtensibleOrSealedElementsKind(elements_kind),
257 &loop_body);
258 GotoIf(Word32Equal(elements_kind, Int32Constant(NO_ELEMENTS)), &loop_body);
259 Goto(maybe_read_only_elements);
260 }
261 }
262
TryRewriteElements(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<FixedArrayBase> elements,TNode<NativeContext> native_context,ElementsKind from_kind,ElementsKind to_kind,Label * bailout)263 void KeyedStoreGenericAssembler::TryRewriteElements(
264 TNode<JSObject> receiver, TNode<Map> receiver_map,
265 TNode<FixedArrayBase> elements, TNode<NativeContext> native_context,
266 ElementsKind from_kind, ElementsKind to_kind, Label* bailout) {
267 DCHECK(IsFastPackedElementsKind(from_kind));
268 ElementsKind holey_from_kind = GetHoleyElementsKind(from_kind);
269 ElementsKind holey_to_kind = GetHoleyElementsKind(to_kind);
270 if (AllocationSite::ShouldTrack(from_kind, to_kind)) {
271 TrapAllocationMemento(receiver, bailout);
272 }
273 Label perform_transition(this), check_holey_map(this);
274 TVARIABLE(Map, var_target_map);
275 // Check if the receiver has the default |from_kind| map.
276 {
277 TNode<Map> packed_map = LoadJSArrayElementsMap(from_kind, native_context);
278 GotoIf(TaggedNotEqual(receiver_map, packed_map), &check_holey_map);
279 var_target_map = CAST(
280 LoadContextElement(native_context, Context::ArrayMapIndex(to_kind)));
281 Goto(&perform_transition);
282 }
283
284 // Check if the receiver has the default |holey_from_kind| map.
285 BIND(&check_holey_map);
286 {
287 TNode<Object> holey_map = LoadContextElement(
288 native_context, Context::ArrayMapIndex(holey_from_kind));
289 GotoIf(TaggedNotEqual(receiver_map, holey_map), bailout);
290 var_target_map = CAST(LoadContextElement(
291 native_context, Context::ArrayMapIndex(holey_to_kind)));
292 Goto(&perform_transition);
293 }
294
295 // Found a supported transition target map, perform the transition!
296 BIND(&perform_transition);
297 {
298 if (IsDoubleElementsKind(from_kind) != IsDoubleElementsKind(to_kind)) {
299 TNode<IntPtrT> capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
300 GrowElementsCapacity(receiver, elements, from_kind, to_kind, capacity,
301 capacity, bailout);
302 }
303 StoreMap(receiver, var_target_map.value());
304 }
305 }
306
TryChangeToHoleyMapHelper(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<NativeContext> native_context,ElementsKind packed_kind,ElementsKind holey_kind,Label * done,Label * map_mismatch,Label * bailout)307 void KeyedStoreGenericAssembler::TryChangeToHoleyMapHelper(
308 TNode<JSObject> receiver, TNode<Map> receiver_map,
309 TNode<NativeContext> native_context, ElementsKind packed_kind,
310 ElementsKind holey_kind, Label* done, Label* map_mismatch, Label* bailout) {
311 TNode<Map> packed_map = LoadJSArrayElementsMap(packed_kind, native_context);
312 GotoIf(TaggedNotEqual(receiver_map, packed_map), map_mismatch);
313 if (AllocationSite::ShouldTrack(packed_kind, holey_kind)) {
314 TrapAllocationMemento(receiver, bailout);
315 }
316 TNode<Map> holey_map = CAST(
317 LoadContextElement(native_context, Context::ArrayMapIndex(holey_kind)));
318 StoreMap(receiver, holey_map);
319 Goto(done);
320 }
321
TryChangeToHoleyMap(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<Word32T> current_elements_kind,TNode<Context> context,ElementsKind packed_kind,Label * bailout)322 void KeyedStoreGenericAssembler::TryChangeToHoleyMap(
323 TNode<JSObject> receiver, TNode<Map> receiver_map,
324 TNode<Word32T> current_elements_kind, TNode<Context> context,
325 ElementsKind packed_kind, Label* bailout) {
326 ElementsKind holey_kind = GetHoleyElementsKind(packed_kind);
327 Label already_holey(this);
328
329 GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind)),
330 &already_holey);
331 TNode<NativeContext> native_context = LoadNativeContext(context);
332 TryChangeToHoleyMapHelper(receiver, receiver_map, native_context, packed_kind,
333 holey_kind, &already_holey, bailout, bailout);
334 BIND(&already_holey);
335 }
336
TryChangeToHoleyMapMulti(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<Word32T> current_elements_kind,TNode<Context> context,ElementsKind packed_kind,ElementsKind packed_kind_2,Label * bailout)337 void KeyedStoreGenericAssembler::TryChangeToHoleyMapMulti(
338 TNode<JSObject> receiver, TNode<Map> receiver_map,
339 TNode<Word32T> current_elements_kind, TNode<Context> context,
340 ElementsKind packed_kind, ElementsKind packed_kind_2, Label* bailout) {
341 ElementsKind holey_kind = GetHoleyElementsKind(packed_kind);
342 ElementsKind holey_kind_2 = GetHoleyElementsKind(packed_kind_2);
343 Label already_holey(this), check_other_kind(this);
344
345 GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind)),
346 &already_holey);
347 GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind_2)),
348 &already_holey);
349
350 TNode<NativeContext> native_context = LoadNativeContext(context);
351 TryChangeToHoleyMapHelper(receiver, receiver_map, native_context, packed_kind,
352 holey_kind, &already_holey, &check_other_kind,
353 bailout);
354 BIND(&check_other_kind);
355 TryChangeToHoleyMapHelper(receiver, receiver_map, native_context,
356 packed_kind_2, holey_kind_2, &already_holey,
357 bailout, bailout);
358 BIND(&already_holey);
359 }
360
MaybeUpdateLengthAndReturn(TNode<JSObject> receiver,TNode<IntPtrT> index,TNode<Object> value,UpdateLength update_length)361 void KeyedStoreGenericAssembler::MaybeUpdateLengthAndReturn(
362 TNode<JSObject> receiver, TNode<IntPtrT> index, TNode<Object> value,
363 UpdateLength update_length) {
364 if (update_length != kDontChangeLength) {
365 TNode<Smi> new_length = SmiTag(Signed(IntPtrAdd(index, IntPtrConstant(1))));
366 StoreObjectFieldNoWriteBarrier(receiver, JSArray::kLengthOffset,
367 new_length);
368 }
369 Return(value);
370 }
371
StoreElementWithCapacity(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<FixedArrayBase> elements,TNode<Word32T> elements_kind,TNode<IntPtrT> index,TNode<Object> value,TNode<Context> context,Label * slow,UpdateLength update_length)372 void KeyedStoreGenericAssembler::StoreElementWithCapacity(
373 TNode<JSObject> receiver, TNode<Map> receiver_map,
374 TNode<FixedArrayBase> elements, TNode<Word32T> elements_kind,
375 TNode<IntPtrT> index, TNode<Object> value, TNode<Context> context,
376 Label* slow, UpdateLength update_length) {
377 if (update_length != kDontChangeLength) {
378 CSA_DCHECK(this, IsJSArrayMap(receiver_map));
379 // Check if the length property is writable. The fast check is only
380 // supported for fast properties.
381 GotoIf(IsDictionaryMap(receiver_map), slow);
382 // The length property is non-configurable, so it's guaranteed to always
383 // be the first property.
384 TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map);
385 TNode<Uint32T> details = LoadDetailsByDescriptorEntry(descriptors, 0);
386 GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
387 slow);
388 }
389 STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize);
390 const int kHeaderSize = FixedArray::kHeaderSize - kHeapObjectTag;
391
392 Label check_double_elements(this), check_cow_elements(this);
393 TNode<Map> elements_map = LoadMap(elements);
394 GotoIf(IsNotFixedArrayMap(elements_map), &check_double_elements);
395
396 // FixedArray backing store -> Smi or object elements.
397 {
398 TNode<IntPtrT> offset =
399 ElementOffsetFromIndex(index, PACKED_ELEMENTS, kHeaderSize);
400 if (!IsDefineKeyedOwnInLiteral()) {
401 // Check if we're about to overwrite the hole. We can safely do that
402 // only if there can be no setters on the prototype chain.
403 // If we know that we're storing beyond the previous array length, we
404 // can skip the hole check (and always assume the hole).
405 {
406 Label hole_check_passed(this);
407 if (update_length == kDontChangeLength) {
408 TNode<Object> element =
409 CAST(Load(MachineType::AnyTagged(), elements, offset));
410 GotoIf(IsNotTheHole(element), &hole_check_passed);
411 }
412 BranchIfPrototypesMayHaveReadOnlyElements(receiver_map, slow,
413 &hole_check_passed);
414 BIND(&hole_check_passed);
415 }
416 }
417
418 // Check if the value we're storing matches the elements_kind. Smis
419 // can always be stored.
420 {
421 Label non_smi_value(this);
422 GotoIfNot(TaggedIsSmi(value), &non_smi_value);
423 // If we're about to introduce holes, ensure holey elements.
424 if (update_length == kBumpLengthWithGap) {
425 TryChangeToHoleyMapMulti(receiver, receiver_map, elements_kind, context,
426 PACKED_SMI_ELEMENTS, PACKED_ELEMENTS, slow);
427 }
428 StoreNoWriteBarrier(MachineRepresentation::kTaggedSigned, elements,
429 offset, value);
430 MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
431
432 BIND(&non_smi_value);
433 }
434
435 // Check if we already have object elements; just do the store if so.
436 {
437 Label must_transition(this);
438 STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
439 STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
440 GotoIf(Int32LessThanOrEqual(elements_kind,
441 Int32Constant(HOLEY_SMI_ELEMENTS)),
442 &must_transition);
443 if (update_length == kBumpLengthWithGap) {
444 TryChangeToHoleyMap(receiver, receiver_map, elements_kind, context,
445 PACKED_ELEMENTS, slow);
446 }
447 Store(elements, offset, value);
448 MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
449
450 BIND(&must_transition);
451 }
452
453 // Transition to the required ElementsKind.
454 {
455 Label transition_to_double(this), transition_to_object(this);
456 TNode<NativeContext> native_context = LoadNativeContext(context);
457 Branch(IsHeapNumber(CAST(value)), &transition_to_double,
458 &transition_to_object);
459 BIND(&transition_to_double);
460 {
461 // If we're adding holes at the end, always transition to a holey
462 // elements kind, otherwise try to remain packed.
463 ElementsKind target_kind = update_length == kBumpLengthWithGap
464 ? HOLEY_DOUBLE_ELEMENTS
465 : PACKED_DOUBLE_ELEMENTS;
466 TryRewriteElements(receiver, receiver_map, elements, native_context,
467 PACKED_SMI_ELEMENTS, target_kind, slow);
468 // Reload migrated elements.
469 TNode<FixedArrayBase> double_elements = LoadElements(receiver);
470 TNode<IntPtrT> double_offset =
471 ElementOffsetFromIndex(index, PACKED_DOUBLE_ELEMENTS, kHeaderSize);
472 // Make sure we do not store signalling NaNs into double arrays.
473 TNode<Float64T> double_value =
474 Float64SilenceNaN(LoadHeapNumberValue(CAST(value)));
475 StoreNoWriteBarrier(MachineRepresentation::kFloat64, double_elements,
476 double_offset, double_value);
477 MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
478 }
479
480 BIND(&transition_to_object);
481 {
482 // If we're adding holes at the end, always transition to a holey
483 // elements kind, otherwise try to remain packed.
484 ElementsKind target_kind = update_length == kBumpLengthWithGap
485 ? HOLEY_ELEMENTS
486 : PACKED_ELEMENTS;
487 TryRewriteElements(receiver, receiver_map, elements, native_context,
488 PACKED_SMI_ELEMENTS, target_kind, slow);
489 // The elements backing store didn't change, no reload necessary.
490 CSA_DCHECK(this, TaggedEqual(elements, LoadElements(receiver)));
491 Store(elements, offset, value);
492 MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
493 }
494 }
495 }
496
497 BIND(&check_double_elements);
498 GotoIf(IsNotFixedDoubleArrayMap(elements_map), &check_cow_elements);
499 // FixedDoubleArray backing store -> double elements.
500 {
501 TNode<IntPtrT> offset =
502 ElementOffsetFromIndex(index, PACKED_DOUBLE_ELEMENTS, kHeaderSize);
503 if (!IsDefineKeyedOwnInLiteral()) {
504 // Check if we're about to overwrite the hole. We can safely do that
505 // only if there can be no setters on the prototype chain.
506 {
507 Label hole_check_passed(this);
508 // If we know that we're storing beyond the previous array length, we
509 // can skip the hole check (and always assume the hole).
510 if (update_length == kDontChangeLength) {
511 Label found_hole(this);
512 LoadDoubleWithHoleCheck(elements, offset, &found_hole,
513 MachineType::None());
514 Goto(&hole_check_passed);
515 BIND(&found_hole);
516 }
517 BranchIfPrototypesMayHaveReadOnlyElements(receiver_map, slow,
518 &hole_check_passed);
519 BIND(&hole_check_passed);
520 }
521 }
522
523 // Try to store the value as a double.
524 {
525 Label non_number_value(this);
526 TNode<Float64T> double_value =
527 TryTaggedToFloat64(value, &non_number_value);
528
529 // Make sure we do not store signalling NaNs into double arrays.
530 double_value = Float64SilenceNaN(double_value);
531 // If we're about to introduce holes, ensure holey elements.
532 if (update_length == kBumpLengthWithGap) {
533 TryChangeToHoleyMap(receiver, receiver_map, elements_kind, context,
534 PACKED_DOUBLE_ELEMENTS, slow);
535 }
536 StoreNoWriteBarrier(MachineRepresentation::kFloat64, elements, offset,
537 double_value);
538 MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
539
540 BIND(&non_number_value);
541 }
542
543 // Transition to object elements.
544 {
545 TNode<NativeContext> native_context = LoadNativeContext(context);
546 ElementsKind target_kind = update_length == kBumpLengthWithGap
547 ? HOLEY_ELEMENTS
548 : PACKED_ELEMENTS;
549 TryRewriteElements(receiver, receiver_map, elements, native_context,
550 PACKED_DOUBLE_ELEMENTS, target_kind, slow);
551 // Reload migrated elements.
552 TNode<FixedArrayBase> fast_elements = LoadElements(receiver);
553 TNode<IntPtrT> fast_offset =
554 ElementOffsetFromIndex(index, PACKED_ELEMENTS, kHeaderSize);
555 Store(fast_elements, fast_offset, value);
556 MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
557 }
558 }
559
560 BIND(&check_cow_elements);
561 {
562 // TODO(jkummerow): Use GrowElementsCapacity instead of bailing out.
563 Goto(slow);
564 }
565 }
566
EmitGenericElementStore(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<Uint16T> instance_type,TNode<IntPtrT> index,TNode<Object> value,TNode<Context> context,Label * slow)567 void KeyedStoreGenericAssembler::EmitGenericElementStore(
568 TNode<JSObject> receiver, TNode<Map> receiver_map,
569 TNode<Uint16T> instance_type, TNode<IntPtrT> index, TNode<Object> value,
570 TNode<Context> context, Label* slow) {
571 Label if_fast(this), if_in_bounds(this), if_increment_length_by_one(this),
572 if_bump_length_with_gap(this), if_grow(this), if_nonfast(this),
573 if_typed_array(this), if_dictionary(this);
574 TNode<FixedArrayBase> elements = LoadElements(receiver);
575 TNode<Int32T> elements_kind = LoadMapElementsKind(receiver_map);
576 Branch(IsFastElementsKind(elements_kind), &if_fast, &if_nonfast);
577 BIND(&if_fast);
578
579 Label if_array(this);
580 GotoIf(IsJSArrayInstanceType(instance_type), &if_array);
581 {
582 TNode<IntPtrT> capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
583 Branch(UintPtrLessThan(index, capacity), &if_in_bounds, &if_grow);
584 }
585 BIND(&if_array);
586 {
587 TNode<IntPtrT> length = SmiUntag(LoadFastJSArrayLength(CAST(receiver)));
588 GotoIf(UintPtrLessThan(index, length), &if_in_bounds);
589 TNode<IntPtrT> capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
590 GotoIf(UintPtrGreaterThanOrEqual(index, capacity), &if_grow);
591 Branch(WordEqual(index, length), &if_increment_length_by_one,
592 &if_bump_length_with_gap);
593 }
594
595 BIND(&if_in_bounds);
596 {
597 StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
598 index, value, context, slow, kDontChangeLength);
599 }
600
601 BIND(&if_increment_length_by_one);
602 {
603 StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
604 index, value, context, slow,
605 kIncrementLengthByOne);
606 }
607
608 BIND(&if_bump_length_with_gap);
609 {
610 StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
611 index, value, context, slow, kBumpLengthWithGap);
612 }
613
614 // Out-of-capacity accesses (index >= capacity) jump here. Additionally,
615 // an ElementsKind transition might be necessary.
616 // The index can also be negative or larger than kMaxElementIndex at this
617 // point! Jump to the runtime in that case to convert it to a named property.
618 BIND(&if_grow);
619 {
620 Comment("Grow backing store");
621 // TODO(jkummerow): Support inline backing store growth.
622 Goto(slow);
623 }
624
625 // Any ElementsKind > LAST_FAST_ELEMENTS_KIND jumps here for further
626 // dispatch.
627 BIND(&if_nonfast);
628 {
629 STATIC_ASSERT(LAST_ELEMENTS_KIND ==
630 LAST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
631 GotoIf(Int32GreaterThanOrEqual(
632 elements_kind,
633 Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
634 &if_typed_array);
635 GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
636 &if_dictionary);
637 Goto(slow);
638 }
639
640 BIND(&if_dictionary);
641 {
642 Comment("Dictionary");
643 // TODO(jkummerow): Support storing to dictionary elements.
644 Goto(slow);
645 }
646
647 BIND(&if_typed_array);
648 {
649 Comment("Typed array");
650 // TODO(jkummerow): Support typed arrays. Note: RAB / GSAB backed typed
651 // arrays end up here too.
652 Goto(slow);
653 }
654 }
655
LookupPropertyOnPrototypeChain(TNode<Map> receiver_map,TNode<Name> name,Label * accessor,TVariable<Object> * var_accessor_pair,TVariable<HeapObject> * var_accessor_holder,Label * readonly,Label * bailout)656 void KeyedStoreGenericAssembler::LookupPropertyOnPrototypeChain(
657 TNode<Map> receiver_map, TNode<Name> name, Label* accessor,
658 TVariable<Object>* var_accessor_pair,
659 TVariable<HeapObject>* var_accessor_holder, Label* readonly,
660 Label* bailout) {
661 Label ok_to_write(this);
662 TVARIABLE(HeapObject, var_holder);
663 TVARIABLE(Map, var_holder_map);
664 var_holder = LoadMapPrototype(receiver_map);
665 var_holder_map = LoadMap(var_holder.value());
666
667 Label loop(this, {&var_holder, &var_holder_map});
668 Goto(&loop);
669 BIND(&loop);
670 {
671 TNode<HeapObject> holder = var_holder.value();
672 GotoIf(IsNull(holder), &ok_to_write);
673 TNode<Map> holder_map = var_holder_map.value();
674 TNode<Uint16T> instance_type = LoadMapInstanceType(holder_map);
675 Label next_proto(this);
676 {
677 Label found(this), found_fast(this), found_dict(this), found_global(this);
678 TVARIABLE(HeapObject, var_meta_storage);
679 TVARIABLE(IntPtrT, var_entry);
680 TryLookupProperty(holder, holder_map, instance_type, name, &found_fast,
681 &found_dict, &found_global, &var_meta_storage,
682 &var_entry, &next_proto, bailout);
683 BIND(&found_fast);
684 {
685 TNode<DescriptorArray> descriptors = CAST(var_meta_storage.value());
686 TNode<IntPtrT> name_index = var_entry.value();
687 TNode<Uint32T> details = LoadDetailsByKeyIndex(descriptors, name_index);
688 JumpIfDataProperty(details, &ok_to_write, readonly);
689
690 // Accessor case.
691 // TODO(jkummerow): Implement a trimmed-down
692 // LoadAccessorFromFastObject.
693 LoadPropertyFromFastObject(holder, holder_map, descriptors, name_index,
694 details, var_accessor_pair);
695 *var_accessor_holder = holder;
696 Goto(accessor);
697 }
698
699 BIND(&found_dict);
700 {
701 TNode<PropertyDictionary> dictionary = CAST(var_meta_storage.value());
702 TNode<IntPtrT> entry = var_entry.value();
703 TNode<Uint32T> details = LoadDetailsByKeyIndex(dictionary, entry);
704 JumpIfDataProperty(details, &ok_to_write, readonly);
705
706 if (accessor != nullptr) {
707 // Accessor case.
708 *var_accessor_pair = LoadValueByKeyIndex(dictionary, entry);
709 *var_accessor_holder = holder;
710 Goto(accessor);
711 } else {
712 Goto(&ok_to_write);
713 }
714 }
715
716 BIND(&found_global);
717 {
718 TNode<GlobalDictionary> dictionary = CAST(var_meta_storage.value());
719 TNode<IntPtrT> entry = var_entry.value();
720 TNode<PropertyCell> property_cell =
721 CAST(LoadValueByKeyIndex(dictionary, entry));
722 TNode<Object> value =
723 LoadObjectField(property_cell, PropertyCell::kValueOffset);
724 GotoIf(TaggedEqual(value, TheHoleConstant()), &next_proto);
725 TNode<Uint32T> details = Unsigned(LoadAndUntagToWord32ObjectField(
726 property_cell, PropertyCell::kPropertyDetailsRawOffset));
727 JumpIfDataProperty(details, &ok_to_write, readonly);
728
729 if (accessor != nullptr) {
730 // Accessor case.
731 *var_accessor_pair = value;
732 *var_accessor_holder = holder;
733 Goto(accessor);
734 } else {
735 Goto(&ok_to_write);
736 }
737 }
738 }
739
740 BIND(&next_proto);
741 // Bailout if it can be an integer indexed exotic case.
742 GotoIf(IsJSTypedArrayInstanceType(instance_type), bailout);
743 TNode<HeapObject> proto = LoadMapPrototype(holder_map);
744 GotoIf(IsNull(proto), &ok_to_write);
745 var_holder = proto;
746 var_holder_map = LoadMap(proto);
747 Goto(&loop);
748 }
749 BIND(&ok_to_write);
750 }
751
FindCandidateStoreICTransitionMapHandler(TNode<Map> map,TNode<Name> name,Label * slow)752 TNode<Map> KeyedStoreGenericAssembler::FindCandidateStoreICTransitionMapHandler(
753 TNode<Map> map, TNode<Name> name, Label* slow) {
754 TVARIABLE(Map, var_transition_map);
755 Label simple_transition(this), transition_array(this),
756 found_handler_candidate(this);
757
758 TNode<MaybeObject> maybe_handler =
759 LoadMaybeWeakObjectField(map, Map::kTransitionsOrPrototypeInfoOffset);
760
761 // Smi -> slow,
762 // Cleared weak reference -> slow
763 // weak reference -> simple_transition
764 // strong reference -> transition_array
765 TVARIABLE(Object, var_transition_map_or_array);
766 DispatchMaybeObject(maybe_handler, slow, slow, &simple_transition,
767 &transition_array, &var_transition_map_or_array);
768
769 BIND(&simple_transition);
770 {
771 var_transition_map = CAST(var_transition_map_or_array.value());
772 Goto(&found_handler_candidate);
773 }
774
775 BIND(&transition_array);
776 {
777 TNode<Map> maybe_handler_map =
778 LoadMap(CAST(var_transition_map_or_array.value()));
779 GotoIfNot(IsTransitionArrayMap(maybe_handler_map), slow);
780
781 TVARIABLE(IntPtrT, var_name_index);
782 Label if_found_candidate(this);
783 TNode<TransitionArray> transitions =
784 CAST(var_transition_map_or_array.value());
785 TransitionLookup(name, transitions, &if_found_candidate, &var_name_index,
786 slow);
787
788 BIND(&if_found_candidate);
789 {
790 // Given that
791 // 1) transitions with the same name are ordered in the transition
792 // array by PropertyKind and then by PropertyAttributes values,
793 // 2) kData < kAccessor,
794 // 3) NONE == 0,
795 // 4) properties with private symbol names are guaranteed to be
796 // non-enumerable (so DONT_ENUM bit in attributes is always set),
797 // the resulting map of transitioning store if it exists in the
798 // transition array is expected to be the first among the transitions
799 // with the same name.
800 // See TransitionArray::CompareDetails() for details.
801 STATIC_ASSERT(static_cast<int>(PropertyKind::kData) == 0);
802 STATIC_ASSERT(NONE == 0);
803 const int kKeyToTargetOffset = (TransitionArray::kEntryTargetIndex -
804 TransitionArray::kEntryKeyIndex) *
805 kTaggedSize;
806 var_transition_map = CAST(GetHeapObjectAssumeWeak(
807 LoadArrayElement(transitions, WeakFixedArray::kHeaderSize,
808 var_name_index.value(), kKeyToTargetOffset)));
809 Goto(&found_handler_candidate);
810 }
811 }
812
813 BIND(&found_handler_candidate);
814 return var_transition_map.value();
815 }
816
EmitGenericPropertyStore(TNode<JSReceiver> receiver,TNode<Map> receiver_map,TNode<Uint16T> instance_type,const StoreICParameters * p,ExitPoint * exit_point,Label * slow,Maybe<LanguageMode> maybe_language_mode)817 void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
818 TNode<JSReceiver> receiver, TNode<Map> receiver_map,
819 TNode<Uint16T> instance_type, const StoreICParameters* p,
820 ExitPoint* exit_point, Label* slow,
821 Maybe<LanguageMode> maybe_language_mode) {
822 CSA_DCHECK(this, IsSimpleObjectMap(receiver_map));
823 // TODO(rmcilroy) Type as Struct once we use a trimmed down
824 // LoadAccessorFromFastObject instead of LoadPropertyFromFastObject.
825 TVARIABLE(Object, var_accessor_pair);
826 TVARIABLE(HeapObject, var_accessor_holder);
827 Label fast_properties(this), dictionary_properties(this), accessor(this),
828 readonly(this);
829 TNode<Uint32T> bitfield3 = LoadMapBitField3(receiver_map);
830 TNode<Name> name = CAST(p->name());
831 Branch(IsSetWord32<Map::Bits3::IsDictionaryMapBit>(bitfield3),
832 &dictionary_properties, &fast_properties);
833
834 BIND(&fast_properties);
835 {
836 Comment("fast property store");
837 TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map);
838 Label descriptor_found(this), lookup_transition(this);
839 TVARIABLE(IntPtrT, var_name_index);
840 DescriptorLookup(name, descriptors, bitfield3,
841 IsAnyDefineOwn() ? slow : &descriptor_found,
842 &var_name_index, &lookup_transition);
843
844 // When dealing with class fields defined with DefineKeyedOwnIC or
845 // DefineNamedOwnIC, use the slow path to check the existing property.
846 if (!IsAnyDefineOwn()) {
847 BIND(&descriptor_found);
848 {
849 TNode<IntPtrT> name_index = var_name_index.value();
850 TNode<Uint32T> details = LoadDetailsByKeyIndex(descriptors, name_index);
851 Label data_property(this);
852 JumpIfDataProperty(details, &data_property,
853 ShouldReconfigureExisting() ? nullptr : &readonly);
854
855 if (ShouldCallSetter()) {
856 // Accessor case.
857 // TODO(jkummerow): Implement a trimmed-down
858 // LoadAccessorFromFastObject.
859 LoadPropertyFromFastObject(receiver, receiver_map, descriptors,
860 name_index, details, &var_accessor_pair);
861 var_accessor_holder = receiver;
862 Goto(&accessor);
863 } else {
864 // Handle accessor to data property reconfiguration in runtime.
865 Goto(slow);
866 }
867
868 BIND(&data_property);
869 {
870 Label shared(this);
871 GotoIf(IsJSSharedStructInstanceType(instance_type), &shared);
872
873 CheckForAssociatedProtector(name, slow);
874 OverwriteExistingFastDataProperty(receiver, receiver_map, descriptors,
875 name_index, details, p->value(),
876 slow, false);
877 exit_point->Return(p->value());
878
879 BIND(&shared);
880 {
881 StoreJSSharedStructField(p->context(), receiver, receiver_map,
882 descriptors, name_index, details,
883 p->value());
884 exit_point->Return(p->value());
885 }
886 }
887 }
888 }
889
890 BIND(&lookup_transition);
891 {
892 Comment("lookup transition");
893 CheckForAssociatedProtector(name, slow);
894 TNode<Map> transition_map =
895 FindCandidateStoreICTransitionMapHandler(receiver_map, name, slow);
896
897 // Validate the transition handler candidate and apply the transition.
898 StoreTransitionMapFlags flags = kValidateTransitionHandler;
899 if (ShouldCheckPrototypeValidity()) {
900 flags = StoreTransitionMapFlags(flags | kCheckPrototypeValidity);
901 }
902 HandleStoreICTransitionMapHandlerCase(p, transition_map, slow, flags);
903 exit_point->Return(p->value());
904 }
905 }
906
907 BIND(&dictionary_properties);
908 {
909 Comment("dictionary property store");
910 // We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out
911 // seeing global objects here (which would need special handling).
912
913 TVARIABLE(IntPtrT, var_name_index);
914 Label dictionary_found(this, &var_name_index), not_found(this);
915 TNode<PropertyDictionary> properties = CAST(LoadSlowProperties(receiver));
916
917 // When dealing with class fields defined with DefineKeyedOwnIC or
918 // DefineNamedOwnIC, use the slow path to check the existing property.
919 NameDictionaryLookup<PropertyDictionary>(
920 properties, name, IsAnyDefineOwn() ? slow : &dictionary_found,
921 &var_name_index, ¬_found);
922
923 if (!IsAnyDefineOwn()) {
924 BIND(&dictionary_found);
925 {
926 Label check_const(this), overwrite(this), done(this);
927 TNode<Uint32T> details =
928 LoadDetailsByKeyIndex(properties, var_name_index.value());
929 JumpIfDataProperty(details, &check_const,
930 ShouldReconfigureExisting() ? nullptr : &readonly);
931
932 if (ShouldCallSetter()) {
933 // Accessor case.
934 var_accessor_pair =
935 LoadValueByKeyIndex(properties, var_name_index.value());
936 var_accessor_holder = receiver;
937 Goto(&accessor);
938 } else {
939 // We must reconfigure an accessor property to a data property
940 // here, let the runtime take care of that.
941 Goto(slow);
942 }
943
944 BIND(&check_const);
945 {
946 if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL) {
947 GotoIfNot(IsPropertyDetailsConst(details), &overwrite);
948 TNode<Object> prev_value =
949 LoadValueByKeyIndex(properties, var_name_index.value());
950
951 BranchIfSameValue(prev_value, p->value(), &done, slow,
952 SameValueMode::kNumbersOnly);
953 } else {
954 Goto(&overwrite);
955 }
956 }
957
958 BIND(&overwrite);
959 {
960 CheckForAssociatedProtector(name, slow);
961 StoreValueByKeyIndex<PropertyDictionary>(
962 properties, var_name_index.value(), p->value());
963 Goto(&done);
964 }
965
966 BIND(&done);
967 exit_point->Return(p->value());
968 }
969 }
970
971 BIND(¬_found);
972 {
973 // TODO(jkummerow): Also add support to correctly handle integer exotic
974 // cases for typed arrays and remove this check here.
975 GotoIf(IsJSTypedArrayMap(receiver_map), slow);
976 CheckForAssociatedProtector(name, slow);
977 Label extensible(this), is_private_symbol(this);
978 GotoIf(IsPrivateSymbol(name), &is_private_symbol);
979 Branch(IsSetWord32<Map::Bits3::IsExtensibleBit>(bitfield3), &extensible,
980 slow);
981
982 BIND(&is_private_symbol);
983 {
984 CSA_DCHECK(this, IsPrivateSymbol(name));
985 // For private names, we miss to the runtime which will throw.
986 // For private symbols, we extend and store an own property.
987 Branch(IsPrivateName(CAST(name)), slow, &extensible);
988 }
989
990 BIND(&extensible);
991 if (ShouldCheckPrototype()) {
992 DCHECK(ShouldCallSetter());
993 LookupPropertyOnPrototypeChain(
994 receiver_map, name, &accessor, &var_accessor_pair,
995 &var_accessor_holder,
996 ShouldReconfigureExisting() ? nullptr : &readonly, slow);
997 }
998 Label add_dictionary_property_slow(this);
999 InvalidateValidityCellIfPrototype(receiver_map, bitfield3);
1000 Add<PropertyDictionary>(properties, name, p->value(),
1001 &add_dictionary_property_slow);
1002 exit_point->Return(p->value());
1003
1004 BIND(&add_dictionary_property_slow);
1005 exit_point->ReturnCallRuntime(Runtime::kAddDictionaryProperty,
1006 p->context(), p->receiver(), name,
1007 p->value());
1008 }
1009 }
1010
1011 if (ShouldCallSetter()) {
1012 BIND(&accessor);
1013 {
1014 Label not_callable(this);
1015 TNode<Struct> accessor_pair = CAST(var_accessor_pair.value());
1016 GotoIf(IsAccessorInfo(accessor_pair), slow);
1017 CSA_DCHECK(this, IsAccessorPair(accessor_pair));
1018 TNode<HeapObject> setter =
1019 CAST(LoadObjectField(accessor_pair, AccessorPair::kSetterOffset));
1020 TNode<Map> setter_map = LoadMap(setter);
1021 // FunctionTemplateInfo setters are not supported yet.
1022 GotoIf(IsFunctionTemplateInfoMap(setter_map), slow);
1023 GotoIfNot(IsCallableMap(setter_map), ¬_callable);
1024
1025 Call(p->context(), setter, receiver, p->value());
1026 exit_point->Return(p->value());
1027
1028 BIND(¬_callable);
1029 {
1030 LanguageMode language_mode;
1031 if (maybe_language_mode.To(&language_mode)) {
1032 if (language_mode == LanguageMode::kStrict) {
1033 exit_point->ReturnCallRuntime(
1034 Runtime::kThrowTypeError, p->context(),
1035 SmiConstant(MessageTemplate::kNoSetterInCallback), name,
1036 var_accessor_holder.value());
1037 } else {
1038 exit_point->Return(p->value());
1039 }
1040 } else {
1041 CallRuntime(Runtime::kThrowTypeErrorIfStrict, p->context(),
1042 SmiConstant(MessageTemplate::kNoSetterInCallback), name,
1043 var_accessor_holder.value());
1044 exit_point->Return(p->value());
1045 }
1046 }
1047 }
1048 }
1049
1050 if (!ShouldReconfigureExisting() && !IsAnyDefineOwn()) {
1051 BIND(&readonly);
1052 {
1053 LanguageMode language_mode;
1054 if (maybe_language_mode.To(&language_mode)) {
1055 if (language_mode == LanguageMode::kStrict) {
1056 TNode<String> type = Typeof(p->receiver());
1057 ThrowTypeError(p->context(), MessageTemplate::kStrictReadOnlyProperty,
1058 name, type, p->receiver());
1059 } else {
1060 exit_point->Return(p->value());
1061 }
1062 } else {
1063 CallRuntime(Runtime::kThrowTypeErrorIfStrict, p->context(),
1064 SmiConstant(MessageTemplate::kStrictReadOnlyProperty), name,
1065 Typeof(p->receiver()), p->receiver());
1066 exit_point->Return(p->value());
1067 }
1068 }
1069 }
1070 }
1071
1072 // Helper that is used by the public KeyedStoreGeneric and by StoreProperty.
KeyedStoreGeneric(TNode<Context> context,TNode<Object> receiver_maybe_smi,TNode<Object> key,TNode<Object> value,Maybe<LanguageMode> language_mode)1073 void KeyedStoreGenericAssembler::KeyedStoreGeneric(
1074 TNode<Context> context, TNode<Object> receiver_maybe_smi, TNode<Object> key,
1075 TNode<Object> value, Maybe<LanguageMode> language_mode) {
1076 TVARIABLE(IntPtrT, var_index);
1077 TVARIABLE(Name, var_unique);
1078 Label if_index(this, &var_index), if_unique_name(this),
1079 not_internalized(this), slow(this);
1080
1081 GotoIf(TaggedIsSmi(receiver_maybe_smi), &slow);
1082 TNode<HeapObject> receiver = CAST(receiver_maybe_smi);
1083 TNode<Map> receiver_map = LoadMap(receiver);
1084 TNode<Uint16T> instance_type = LoadMapInstanceType(receiver_map);
1085 // Receivers requiring non-standard element accesses (interceptors, access
1086 // checks, strings and string wrappers, proxies) are handled in the runtime.
1087 GotoIf(IsCustomElementsReceiverInstanceType(instance_type), &slow);
1088
1089 TryToName(key, &if_index, &var_index, &if_unique_name, &var_unique, &slow,
1090 ¬_internalized);
1091
1092 BIND(&if_index);
1093 {
1094 Comment("integer index");
1095 EmitGenericElementStore(CAST(receiver), receiver_map, instance_type,
1096 var_index.value(), value, context, &slow);
1097 }
1098
1099 BIND(&if_unique_name);
1100 {
1101 Comment("key is unique name");
1102 StoreICParameters p(context, receiver, var_unique.value(), value, {},
1103 UndefinedConstant(), StoreICMode::kDefault);
1104 ExitPoint direct_exit(this);
1105 EmitGenericPropertyStore(CAST(receiver), receiver_map, instance_type, &p,
1106 &direct_exit, &slow, language_mode);
1107 }
1108
1109 BIND(¬_internalized);
1110 {
1111 if (FLAG_internalize_on_the_fly) {
1112 TryInternalizeString(CAST(key), &if_index, &var_index, &if_unique_name,
1113 &var_unique, &slow, &slow);
1114 } else {
1115 Goto(&slow);
1116 }
1117 }
1118
1119 BIND(&slow);
1120 {
1121 if (IsSet() || IsDefineNamedOwn()) {
1122 // The DefineNamedOwnIC hacky reuse should never reach here.
1123 CSA_DCHECK(this, BoolConstant(!IsDefineNamedOwn()));
1124 Comment("KeyedStoreGeneric_slow");
1125 TailCallRuntime(Runtime::kSetKeyedProperty, context, receiver, key,
1126 value);
1127 } else if (IsDefineKeyedOwn()) {
1128 TailCallRuntime(Runtime::kDefineObjectOwnProperty, context, receiver, key,
1129 value);
1130 } else {
1131 DCHECK(IsDefineKeyedOwnInLiteral());
1132 TailCallRuntime(Runtime::kDefineKeyedOwnPropertyInLiteral_Simple, context,
1133 receiver, key, value);
1134 }
1135 }
1136 }
1137
KeyedStoreGeneric()1138 void KeyedStoreGenericAssembler::KeyedStoreGeneric() {
1139 using Descriptor = StoreDescriptor;
1140
1141 auto receiver = Parameter<Object>(Descriptor::kReceiver);
1142 auto name = Parameter<Object>(Descriptor::kName);
1143 auto value = Parameter<Object>(Descriptor::kValue);
1144 auto context = Parameter<Context>(Descriptor::kContext);
1145
1146 KeyedStoreGeneric(context, receiver, name, value, Nothing<LanguageMode>());
1147 }
1148
StoreProperty(TNode<Context> context,TNode<Object> receiver,TNode<Object> key,TNode<Object> value,LanguageMode language_mode)1149 void KeyedStoreGenericAssembler::StoreProperty(TNode<Context> context,
1150 TNode<Object> receiver,
1151 TNode<Object> key,
1152 TNode<Object> value,
1153 LanguageMode language_mode) {
1154 KeyedStoreGeneric(context, receiver, key, value, Just(language_mode));
1155 }
1156
StoreIC_NoFeedback()1157 void KeyedStoreGenericAssembler::StoreIC_NoFeedback() {
1158 using Descriptor = StoreDescriptor;
1159
1160 auto receiver_maybe_smi = Parameter<Object>(Descriptor::kReceiver);
1161 auto name = Parameter<Object>(Descriptor::kName);
1162 auto value = Parameter<Object>(Descriptor::kValue);
1163 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
1164 auto context = Parameter<Context>(Descriptor::kContext);
1165
1166 Label miss(this, Label::kDeferred), store_property(this);
1167
1168 GotoIf(TaggedIsSmi(receiver_maybe_smi), &miss);
1169
1170 {
1171 TNode<HeapObject> receiver = CAST(receiver_maybe_smi);
1172 TNode<Map> receiver_map = LoadMap(receiver);
1173 TNode<Uint16T> instance_type = LoadMapInstanceType(receiver_map);
1174 // Receivers requiring non-standard element accesses (interceptors, access
1175 // checks, strings and string wrappers, proxies) are handled in the runtime.
1176 GotoIf(IsSpecialReceiverInstanceType(instance_type), &miss);
1177 {
1178 StoreICParameters p(context, receiver, name, value, slot,
1179 UndefinedConstant(),
1180 IsDefineNamedOwn() ? StoreICMode::kDefineNamedOwn
1181 : StoreICMode::kDefault);
1182 EmitGenericPropertyStore(CAST(receiver), receiver_map, instance_type, &p,
1183 &miss);
1184 }
1185 }
1186
1187 BIND(&miss);
1188 {
1189 auto runtime = IsDefineNamedOwn() ? Runtime::kDefineNamedOwnIC_Miss
1190 : Runtime::kStoreIC_Miss;
1191 TailCallRuntime(runtime, context, value, slot, UndefinedConstant(),
1192 receiver_maybe_smi, name);
1193 }
1194 }
1195
StoreProperty(TNode<Context> context,TNode<JSReceiver> receiver,TNode<BoolT> is_simple_receiver,TNode<Name> unique_name,TNode<Object> value,LanguageMode language_mode)1196 void KeyedStoreGenericAssembler::StoreProperty(TNode<Context> context,
1197 TNode<JSReceiver> receiver,
1198 TNode<BoolT> is_simple_receiver,
1199 TNode<Name> unique_name,
1200 TNode<Object> value,
1201 LanguageMode language_mode) {
1202 StoreICParameters p(context, receiver, unique_name, value, {},
1203 UndefinedConstant(), StoreICMode::kDefault);
1204
1205 Label done(this), slow(this, Label::kDeferred);
1206 ExitPoint exit_point(this, [&](TNode<Object> result) { Goto(&done); });
1207
1208 CSA_DCHECK(this, Word32Equal(is_simple_receiver,
1209 IsSimpleObjectMap(LoadMap(receiver))));
1210 GotoIfNot(is_simple_receiver, &slow);
1211
1212 TNode<Map> map = LoadMap(receiver);
1213 TNode<Uint16T> instance_type = LoadMapInstanceType(map);
1214 EmitGenericPropertyStore(receiver, map, instance_type, &p, &exit_point, &slow,
1215 Just(language_mode));
1216
1217 BIND(&slow);
1218 {
1219 if (IsDefineKeyedOwnInLiteral()) {
1220 CallRuntime(Runtime::kDefineKeyedOwnPropertyInLiteral_Simple, context,
1221 receiver, unique_name, value);
1222 } else {
1223 CallRuntime(Runtime::kSetKeyedProperty, context, receiver, unique_name,
1224 value);
1225 }
1226 Goto(&done);
1227 }
1228
1229 BIND(&done);
1230 }
1231
1232 } // namespace internal
1233 } // namespace v8
1234