@@ -16,6 +16,8 @@ namespace internal {
1616// ES6 section 19.1 Object Objects
1717
1818typedef compiler::Node Node;
19+ template <class T >
20+ using TNode = CodeStubAssembler::TNode<T>;
1921
2022class ObjectBuiltinsAssembler : public CodeStubAssembler {
2123 public:
@@ -34,6 +36,46 @@ class ObjectBuiltinsAssembler : public CodeStubAssembler {
3436 Node* ConstructDataDescriptor (Node* context, Node* value, Node* writable,
3537 Node* enumerable, Node* configurable);
3638 Node* GetAccessorOrUndefined (Node* accessor, Label* if_bailout);
39+
40+ Node* IsSpecialReceiverMap (SloppyTNode<Map> map);
41+ };
42+
43+ class ObjectEntriesValuesBuiltinsAssembler : public ObjectBuiltinsAssembler {
44+ public:
45+ explicit ObjectEntriesValuesBuiltinsAssembler (
46+ compiler::CodeAssemblerState* state)
47+ : ObjectBuiltinsAssembler(state) {}
48+
49+ protected:
50+ enum CollectType { kEntries , kValues };
51+
52+ TNode<Word32T> IsStringWrapperElementsKind (TNode<Map> map);
53+
54+ TNode<BoolT> IsPropertyEnumerable (TNode<Uint32T> details);
55+
56+ TNode<BoolT> IsPropertyKindAccessor (TNode<Uint32T> kind);
57+
58+ TNode<BoolT> IsPropertyKindData (TNode<Uint32T> kind);
59+
60+ TNode<Uint32T> HasHiddenPrototype (TNode<Map> map);
61+
62+ TNode<Uint32T> LoadPropertyKind (TNode<Uint32T> details) {
63+ return DecodeWord32<PropertyDetails::KindField>(details);
64+ }
65+
66+ void GetOwnValuesOrEntries (TNode<Context> context, TNode<Object> maybe_object,
67+ CollectType collect_type);
68+
69+ void GotoIfMapHasSlowProperties (TNode<Map> map, Label* if_slow);
70+
71+ TNode<JSArray> FastGetOwnValuesOrEntries (
72+ TNode<Context> context, TNode<JSObject> object,
73+ Label* if_call_runtime_with_fast_path, Label* if_no_properties,
74+ CollectType collect_type);
75+
76+ TNode<JSArray> FinalizeValuesOrEntriesJSArray (
77+ TNode<Context> context, TNode<FixedArray> values_or_entries,
78+ TNode<IntPtrT> size, TNode<Map> array_map, Label* if_empty);
3779};
3880
3981void ObjectBuiltinsAssembler::ReturnToStringFormat (Node* context,
@@ -97,6 +139,249 @@ Node* ObjectBuiltinsAssembler::ConstructDataDescriptor(Node* context,
97139 return js_desc;
98140}
99141
142+ Node* ObjectBuiltinsAssembler::IsSpecialReceiverMap (SloppyTNode<Map> map) {
143+ CSA_SLOW_ASSERT (this , IsMap (map));
144+ Node* is_special = IsSpecialReceiverInstanceType (LoadMapInstanceType (map));
145+ uint32_t mask =
146+ Map::HasNamedInterceptorBit::kMask | Map::IsAccessCheckNeededBit::kMask ;
147+ USE (mask);
148+ // Interceptors or access checks imply special receiver.
149+ CSA_ASSERT (this ,
150+ SelectConstant (IsSetWord32 (LoadMapBitField (map), mask), is_special,
151+ Int32Constant (1 ), MachineRepresentation::kWord32 ));
152+ return is_special;
153+ }
154+
155+ TNode<Word32T>
156+ ObjectEntriesValuesBuiltinsAssembler::IsStringWrapperElementsKind (
157+ TNode<Map> map) {
158+ Node* kind = LoadMapElementsKind (map);
159+ return Word32Or (
160+ Word32Equal (kind, Int32Constant (FAST_STRING_WRAPPER_ELEMENTS)),
161+ Word32Equal (kind, Int32Constant (SLOW_STRING_WRAPPER_ELEMENTS)));
162+ }
163+
164+ TNode<BoolT> ObjectEntriesValuesBuiltinsAssembler::IsPropertyEnumerable (
165+ TNode<Uint32T> details) {
166+ TNode<Uint32T> attributes =
167+ DecodeWord32<PropertyDetails::AttributesField>(details);
168+ return IsNotSetWord32 (attributes, PropertyAttributes::DONT_ENUM);
169+ }
170+
171+ TNode<BoolT> ObjectEntriesValuesBuiltinsAssembler::IsPropertyKindAccessor (
172+ TNode<Uint32T> kind) {
173+ return Word32Equal (kind, Int32Constant (PropertyKind::kAccessor ));
174+ }
175+
176+ TNode<BoolT> ObjectEntriesValuesBuiltinsAssembler::IsPropertyKindData (
177+ TNode<Uint32T> kind) {
178+ return Word32Equal (kind, Int32Constant (PropertyKind::kData ));
179+ }
180+
181+ TNode<Uint32T> ObjectEntriesValuesBuiltinsAssembler::HasHiddenPrototype (
182+ TNode<Map> map) {
183+ TNode<Uint32T> bit_field3 = LoadMapBitField3 (map);
184+ return DecodeWord32<Map::HasHiddenPrototypeBit>(bit_field3);
185+ }
186+
187+ void ObjectEntriesValuesBuiltinsAssembler::GetOwnValuesOrEntries (
188+ TNode<Context> context, TNode<Object> maybe_object,
189+ CollectType collect_type) {
190+ TNode<JSObject> object = TNode<JSObject>::UncheckedCast (
191+ CallBuiltin (Builtins::kToObject , context, maybe_object));
192+
193+ Label if_call_runtime_with_fast_path (this , Label::kDeferred ),
194+ if_call_runtime (this , Label::kDeferred ),
195+ if_no_properties (this , Label::kDeferred );
196+
197+ TNode<Map> map = LoadMap (object);
198+ GotoIfNot (IsJSObjectMap (map), &if_call_runtime);
199+ GotoIfMapHasSlowProperties (map, &if_call_runtime);
200+
201+ TNode<FixedArrayBase> elements = LoadElements (object);
202+ // If the object has elements, we treat it as slow case.
203+ // So, we go to runtime call.
204+ GotoIfNot (IsEmptyFixedArray (elements), &if_call_runtime_with_fast_path);
205+
206+ TNode<JSArray> result = FastGetOwnValuesOrEntries (
207+ context, object, &if_call_runtime_with_fast_path, &if_no_properties,
208+ collect_type);
209+ Return (result);
210+
211+ BIND (&if_no_properties);
212+ {
213+ Node* native_context = LoadNativeContext (context);
214+ Node* array_map = LoadJSArrayElementsMap (PACKED_ELEMENTS, native_context);
215+ Node* empty_array = AllocateJSArray (PACKED_ELEMENTS, array_map,
216+ IntPtrConstant (0 ), SmiConstant (0 ));
217+ Return (empty_array);
218+ }
219+
220+ BIND (&if_call_runtime_with_fast_path);
221+ {
222+ // In slow case, we simply call runtime.
223+ if (collect_type == CollectType::kEntries ) {
224+ Return (CallRuntime (Runtime::kObjectEntries , context, object));
225+ } else {
226+ DCHECK (collect_type == CollectType::kValues );
227+ Return (CallRuntime (Runtime::kObjectValues , context, object));
228+ }
229+ }
230+
231+ BIND (&if_call_runtime);
232+ {
233+ // In slow case, we simply call runtime.
234+ if (collect_type == CollectType::kEntries ) {
235+ Return (CallRuntime (Runtime::kObjectEntriesSkipFastPath , context, object));
236+ } else {
237+ DCHECK (collect_type == CollectType::kValues );
238+ Return (CallRuntime (Runtime::kObjectValuesSkipFastPath , context, object));
239+ }
240+ }
241+ }
242+
243+ void ObjectEntriesValuesBuiltinsAssembler::GotoIfMapHasSlowProperties (
244+ TNode<Map> map, Label* if_slow) {
245+ GotoIf (IsStringWrapperElementsKind (map), if_slow);
246+ GotoIf (IsSpecialReceiverMap (map), if_slow);
247+ GotoIf (HasHiddenPrototype (map), if_slow);
248+ GotoIf (IsDictionaryMap (map), if_slow);
249+ }
250+
251+ TNode<JSArray> ObjectEntriesValuesBuiltinsAssembler::FastGetOwnValuesOrEntries (
252+ TNode<Context> context, TNode<JSObject> object,
253+ Label* if_call_runtime_with_fast_path, Label* if_no_properties,
254+ CollectType collect_type) {
255+ Node* native_context = LoadNativeContext (context);
256+ TNode<Map> array_map =
257+ LoadJSArrayElementsMap (PACKED_ELEMENTS, native_context);
258+ TNode<Map> map = LoadMap (object);
259+ TNode<Uint32T> bit_field3 = LoadMapBitField3 (map);
260+
261+ Label if_has_enum_cache (this ), if_not_has_enum_cache (this ),
262+ collect_entries (this );
263+ Node* object_enum_length =
264+ DecodeWordFromWord32<Map::EnumLengthBits>(bit_field3);
265+ Node* has_enum_cache = WordNotEqual (
266+ object_enum_length, IntPtrConstant (kInvalidEnumCacheSentinel ));
267+
268+ // In case, we found enum_cache in object,
269+ // we use it as array_length becuase it has same size for
270+ // Object.(entries/values) result array object length.
271+ // So object_enum_length use less memory space than
272+ // NumberOfOwnDescriptorsBits value.
273+ // And in case, if enum_cache_not_found,
274+ // we call runtime and initialize enum_cache for subsequent call of
275+ // CSA fast path.
276+ Branch (has_enum_cache, &if_has_enum_cache, if_call_runtime_with_fast_path);
277+
278+ BIND (&if_has_enum_cache);
279+ {
280+ GotoIf (WordEqual (object_enum_length, IntPtrConstant (0 )), if_no_properties);
281+ TNode<FixedArray> values_or_entries = TNode<FixedArray>::UncheckedCast (
282+ AllocateFixedArray (PACKED_ELEMENTS, object_enum_length,
283+ INTPTR_PARAMETERS, kAllowLargeObjectAllocation ));
284+
285+ // If in case we have enum_cache,
286+ // we can't detect accessor of object until loop through descritpros.
287+ // So if object might have accessor,
288+ // we will remain invalid addresses of FixedArray.
289+ // Because in that case, we need to jump to runtime call.
290+ // So the array filled by the-hole even if enum_cache exists.
291+ FillFixedArrayWithValue (PACKED_ELEMENTS, values_or_entries,
292+ IntPtrConstant (0 ), object_enum_length,
293+ Heap::kTheHoleValueRootIndex );
294+
295+ TVARIABLE (IntPtrT, var_result_index, IntPtrConstant (0 ));
296+ TVARIABLE (IntPtrT, var_descriptor_index, IntPtrConstant (0 ));
297+ Variable* vars[] = {&var_descriptor_index, &var_result_index};
298+ // Let desc be ? O.[[GetOwnProperty]](key).
299+ TNode<DescriptorArray> descriptors = LoadMapDescriptors (map);
300+ Label loop (this , 2 , vars), after_loop (this ), loop_condition (this );
301+ Branch (IntPtrEqual (var_descriptor_index, object_enum_length), &after_loop,
302+ &loop);
303+
304+ // We dont use BuildFastLoop.
305+ // Instead, we use hand-written loop
306+ // because of we need to use 'continue' functionality.
307+ BIND (&loop);
308+ {
309+ // Currently, we will not invoke getters,
310+ // so, map will not be changed.
311+ CSA_ASSERT (this , WordEqual (map, LoadMap (object)));
312+ TNode<Uint32T> descriptor_index = TNode<Uint32T>::UncheckedCast (
313+ TruncateWordToWord32 (var_descriptor_index));
314+ Node* next_key = DescriptorArrayGetKey (descriptors, descriptor_index);
315+
316+ // Skip Symbols.
317+ GotoIf (IsSymbol (next_key), &loop_condition);
318+
319+ TNode<Uint32T> details = TNode<Uint32T>::UncheckedCast (
320+ DescriptorArrayGetDetails (descriptors, descriptor_index));
321+ TNode<Uint32T> kind = LoadPropertyKind (details);
322+
323+ // If property is accessor, we escape fast path and call runtime.
324+ GotoIf (IsPropertyKindAccessor (kind), if_call_runtime_with_fast_path);
325+ CSA_ASSERT (this , IsPropertyKindData (kind));
326+
327+ // If desc is not undefined and desc.[[Enumerable]] is true, then
328+ GotoIfNot (IsPropertyEnumerable (details), &loop_condition);
329+
330+ VARIABLE (var_property_value, MachineRepresentation::kTagged ,
331+ UndefinedConstant ());
332+ Node* descriptor_name_index = DescriptorNumberToIndex (descriptor_index);
333+
334+ // Let value be ? Get(O, key).
335+ LoadPropertyFromFastObject (object, map, descriptors,
336+ descriptor_name_index, details,
337+ &var_property_value);
338+
339+ // If kind is "value", append value to properties.
340+ Node* value = var_property_value.value ();
341+
342+ if (collect_type == CollectType::kEntries ) {
343+ // Let entry be CreateArrayFromList(« key, value »).
344+ Node* array = nullptr ;
345+ Node* elements = nullptr ;
346+ std::tie (array, elements) = AllocateUninitializedJSArrayWithElements (
347+ PACKED_ELEMENTS, array_map, SmiConstant (2 ), nullptr ,
348+ IntPtrConstant (2 ));
349+ StoreFixedArrayElement (elements, 0 , next_key, SKIP_WRITE_BARRIER);
350+ StoreFixedArrayElement (elements, 1 , value, SKIP_WRITE_BARRIER);
351+ value = array;
352+ }
353+
354+ StoreFixedArrayElement (values_or_entries, var_result_index, value);
355+ Increment (&var_result_index, 1 );
356+ Goto (&loop_condition);
357+
358+ BIND (&loop_condition);
359+ {
360+ Increment (&var_descriptor_index, 1 );
361+ Branch (IntPtrEqual (var_descriptor_index, object_enum_length),
362+ &after_loop, &loop);
363+ }
364+ }
365+ BIND (&after_loop);
366+ return FinalizeValuesOrEntriesJSArray (context, values_or_entries,
367+ var_result_index, array_map,
368+ if_no_properties);
369+ }
370+ }
371+
372+ TNode<JSArray>
373+ ObjectEntriesValuesBuiltinsAssembler::FinalizeValuesOrEntriesJSArray (
374+ TNode<Context> context, TNode<FixedArray> result, TNode<IntPtrT> size,
375+ TNode<Map> array_map, Label* if_empty) {
376+ CSA_ASSERT (this , IsJSArrayMap (array_map));
377+
378+ GotoIf (IntPtrEqual (size, IntPtrConstant (0 )), if_empty);
379+ Node* array = AllocateUninitializedJSArrayWithoutElements (
380+ array_map, SmiTag (size), nullptr );
381+ StoreObjectField (array, JSArray::kElementsOffset , result);
382+ return TNode<JSArray>::UncheckedCast (array);
383+ }
384+
100385TF_BUILTIN (ObjectPrototypeToLocaleString, CodeStubAssembler) {
101386 TNode<Context> context = CAST (Parameter (Descriptor::kContext ));
102387 TNode<Object> receiver = CAST (Parameter (Descriptor::kReceiver ));
@@ -268,6 +553,22 @@ TF_BUILTIN(ObjectKeys, ObjectBuiltinsAssembler) {
268553 }
269554}
270555
556+ TF_BUILTIN (ObjectValues, ObjectEntriesValuesBuiltinsAssembler) {
557+ TNode<JSObject> object =
558+ TNode<JSObject>::UncheckedCast (Parameter (Descriptor::kObject ));
559+ TNode<Context> context =
560+ TNode<Context>::UncheckedCast (Parameter (Descriptor::kContext ));
561+ GetOwnValuesOrEntries (context, object, CollectType::kValues );
562+ }
563+
564+ TF_BUILTIN (ObjectEntries, ObjectEntriesValuesBuiltinsAssembler) {
565+ TNode<JSObject> object =
566+ TNode<JSObject>::UncheckedCast (Parameter (Descriptor::kObject ));
567+ TNode<Context> context =
568+ TNode<Context>::UncheckedCast (Parameter (Descriptor::kContext ));
569+ GetOwnValuesOrEntries (context, object, CollectType::kEntries );
570+ }
571+
271572// ES #sec-object.prototype.isprototypeof
272573TF_BUILTIN (ObjectPrototypeIsPrototypeOf, ObjectBuiltinsAssembler) {
273574 Node* receiver = Parameter (Descriptor::kReceiver );
0 commit comments