• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &not_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(&not_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), &not_callable);
1024 
1025       Call(p->context(), setter, receiver, p->value());
1026       exit_point->Return(p->value());
1027 
1028       BIND(&not_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             &not_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(&not_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