Index: src/builtins/builtins-object-gen.cc |
diff --git a/src/builtins/builtins-object-gen.cc b/src/builtins/builtins-object-gen.cc |
index cdf9d9026630b36d3e50452b6f2b09c1cbecbc6f..6f03d613e6690c4fd5cf2c85965f80a5f9f16b3a 100644 |
--- a/src/builtins/builtins-object-gen.cc |
+++ b/src/builtins/builtins-object-gen.cc |
@@ -104,6 +104,88 @@ TF_BUILTIN(ObjectHasOwnProperty, ObjectBuiltinsAssembler) { |
Return(CallRuntime(Runtime::kObjectHasOwnProperty, context, object, key)); |
} |
+// ES #sec-object.keys |
+TF_BUILTIN(ObjectKeys, ObjectBuiltinsAssembler) { |
+ Node* object = Parameter(Descriptor::kObject); |
+ Node* context = Parameter(Descriptor::kContext); |
+ |
+ VARIABLE(var_length, MachineRepresentation::kTagged); |
+ VARIABLE(var_elements, MachineRepresentation::kTagged); |
+ Label if_empty(this, Label::kDeferred), if_fast(this), |
+ if_slow(this, Label::kDeferred), if_join(this); |
+ |
+ // Check if the {object} has a usable enum cache. |
+ GotoIf(TaggedIsSmi(object), &if_slow); |
+ Node* object_map = LoadMap(object); |
+ Node* object_bit_field3 = LoadMapBitField3(object_map); |
+ Node* object_enum_length = |
+ DecodeWordFromWord32<Map::EnumLengthBits>(object_bit_field3); |
+ GotoIf( |
+ WordEqual(object_enum_length, IntPtrConstant(kInvalidEnumCacheSentinel)), |
+ &if_slow); |
+ |
+ // Ensure that the {object} doesn't have any elements. |
+ CSA_ASSERT(this, IsJSObjectMap(object_map)); |
+ Node* object_elements = LoadObjectField(object, JSObject::kElementsOffset); |
+ GotoIfNot(IsEmptyFixedArray(object_elements), &if_slow); |
+ Branch(WordEqual(object_enum_length, IntPtrConstant(0)), &if_empty, &if_fast); |
+ |
+ BIND(&if_fast); |
+ { |
+ // The {object} has a usable enum cache, use that. |
+ Node* object_descriptors = LoadMapDescriptors(object_map); |
+ Node* object_enum_cache_bridge = LoadObjectField( |
+ object_descriptors, DescriptorArray::kEnumCacheBridgeOffset); |
+ Node* object_enum_cache = LoadObjectField( |
+ object_enum_cache_bridge, DescriptorArray::kEnumCacheBridgeCacheOffset); |
+ |
+ // Allocate a JSArray and copy the elements from the {object_enum_cache}. |
+ Node* array = nullptr; |
+ Node* elements = nullptr; |
+ Node* native_context = LoadNativeContext(context); |
+ Node* array_map = LoadJSArrayElementsMap(FAST_ELEMENTS, native_context); |
+ Node* array_length = SmiTag(object_enum_length); |
+ std::tie(array, elements) = AllocateUninitializedJSArrayWithElements( |
+ FAST_ELEMENTS, array_map, array_length, nullptr, object_enum_length, |
+ INTPTR_PARAMETERS); |
+ StoreMapNoWriteBarrier(elements, Heap::kFixedArrayMapRootIndex); |
+ StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, |
+ array_length); |
+ CopyFixedArrayElements(FAST_ELEMENTS, object_enum_cache, elements, |
+ object_enum_length, SKIP_WRITE_BARRIER); |
+ Return(array); |
+ } |
+ |
+ BIND(&if_empty); |
+ { |
+ // The {object} doesn't have any enumerable keys. |
+ var_length.Bind(SmiConstant(0)); |
+ var_elements.Bind(EmptyFixedArrayConstant()); |
+ Goto(&if_join); |
+ } |
+ |
+ BIND(&if_slow); |
+ { |
+ // Let the runtime compute the elements. |
+ Node* elements = CallRuntime(Runtime::kObjectKeys, context, object); |
+ var_length.Bind(LoadObjectField(elements, FixedArray::kLengthOffset)); |
+ var_elements.Bind(elements); |
+ Goto(&if_join); |
+ } |
+ |
+ BIND(&if_join); |
+ { |
+ // Wrap the elements into a proper JSArray and return that. |
+ Node* native_context = LoadNativeContext(context); |
+ Node* array_map = LoadJSArrayElementsMap(FAST_ELEMENTS, native_context); |
+ Node* array = AllocateUninitializedJSArrayWithoutElements( |
+ FAST_ELEMENTS, array_map, var_length.value(), nullptr); |
+ StoreObjectFieldNoWriteBarrier(array, JSArray::kElementsOffset, |
+ var_elements.value()); |
+ Return(array); |
+ } |
+} |
+ |
// ES6 #sec-object.prototype.tostring |
TF_BUILTIN(ObjectProtoToString, ObjectBuiltinsAssembler) { |
Label return_undefined(this, Label::kDeferred), |