Commit 2441aaa3 authored by Z Nguyen-Huu's avatar Z Nguyen-Huu Committed by Commit Bot

[runtime] Process symbol last in SetDataProperties for Object.assign

Bug: v8:11177
Change-Id: Ib4bbdca5fe9811731c15edae5f58243113dd119f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2548080
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71296}
parent 0eaac02d
......@@ -660,6 +660,7 @@ TF_BUILTIN(SetDataProperties, SetOrCopyDataPropertiesAssembler) {
auto context = Parameter<Context>(Descriptor::kContext);
Label if_runtime(this, Label::kDeferred);
GotoIfForceSlowPath(&if_runtime);
Return(SetOrCopyDataProperties(context, target, source, &if_runtime, true));
BIND(&if_runtime);
......
......@@ -8217,7 +8217,7 @@ void CodeStubAssembler::LookupBinary(TNode<Name> unique_name,
void CodeStubAssembler::ForEachEnumerableOwnProperty(
TNode<Context> context, TNode<Map> map, TNode<JSObject> object,
ForEachEnumerationMode mode, const ForEachKeyValueFunction& body,
PropertiesEnumerationMode mode, const ForEachKeyValueFunction& body,
Label* bailout) {
TNode<Uint16T> type = LoadMapInstanceType(map);
TNode<Uint32T> bit_field3 = EnsureOnlyHasSimpleProperties(map, type, bailout);
......
......@@ -3600,20 +3600,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
using ForEachKeyValueFunction =
std::function<void(TNode<Name> key, TNode<Object> value)>;
enum ForEachEnumerationMode {
// String and then Symbol properties according to the spec
// ES#sec-object.assign
kEnumerationOrder,
// Order of property addition
kPropertyAdditionOrder,
};
// For each JSObject property (in DescriptorArray order), check if the key is
// enumerable, and if so, load the value from the receiver and evaluate the
// closure.
void ForEachEnumerableOwnProperty(TNode<Context> context, TNode<Map> map,
TNode<JSObject> object,
ForEachEnumerationMode mode,
PropertiesEnumerationMode mode,
const ForEachKeyValueFunction& body,
Label* bailout);
......
......@@ -1758,6 +1758,14 @@ class int31_t {
int32_t value_;
};
enum PropertiesEnumerationMode {
// String and then Symbol properties according to the spec
// ES#sec-object.assign
kEnumerationOrder,
// Order of property addition
kPropertyAdditionOrder,
};
} // namespace internal
// Tag dispatching support for acquire loads and release stores.
......
......@@ -2782,9 +2782,11 @@ static MaybeHandle<JSObject> CloneObjectSlowPath(Isolate* isolate,
return new_object;
}
MAYBE_RETURN(JSReceiver::SetOrCopyDataProperties(isolate, new_object, source,
nullptr, false),
MaybeHandle<JSObject>());
MAYBE_RETURN(
JSReceiver::SetOrCopyDataProperties(
isolate, new_object, source,
PropertiesEnumerationMode::kPropertyAdditionOrder, nullptr, false),
MaybeHandle<JSObject>());
return new_object;
}
......
......@@ -193,6 +193,7 @@ bool HasExcludedProperty(
V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign(
Handle<JSReceiver> target, Handle<Object> source,
PropertiesEnumerationMode mode,
const ScopedVector<Handle<Object>>* excluded_properties, bool use_set) {
// Non-empty strings are the only non-JSReceivers that need to be handled
// explicitly by Object.assign.
......@@ -225,86 +226,111 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign(
bool stable = true;
for (InternalIndex i : map->IterateOwnDescriptors()) {
HandleScope inner_scope(isolate);
// Process symbols last and only do that if we found symbols.
bool has_symbol = false;
bool process_symbol_only = false;
while (true) {
for (InternalIndex i : map->IterateOwnDescriptors()) {
HandleScope inner_scope(isolate);
Handle<Name> next_key(descriptors->GetKey(i), isolate);
Handle<Object> prop_value;
// Directly decode from the descriptor array if |from| did not change shape.
if (stable) {
DCHECK_EQ(from->map(), *map);
DCHECK_EQ(*descriptors, map->instance_descriptors(kRelaxedLoad));
Handle<Name> next_key(descriptors->GetKey(i), isolate);
if (mode == PropertiesEnumerationMode::kEnumerationOrder) {
if (next_key->IsSymbol()) {
has_symbol = true;
if (!process_symbol_only) continue;
} else {
if (process_symbol_only) continue;
}
}
Handle<Object> prop_value;
// Directly decode from the descriptor array if |from| did not change
// shape.
if (stable) {
DCHECK_EQ(from->map(), *map);
DCHECK_EQ(*descriptors, map->instance_descriptors(kRelaxedLoad));
PropertyDetails details = descriptors->GetDetails(i);
if (!details.IsEnumerable()) continue;
if (details.kind() == kData) {
if (details.location() == kDescriptor) {
prop_value = handle(descriptors->GetStrongValue(i), isolate);
PropertyDetails details = descriptors->GetDetails(i);
if (!details.IsEnumerable()) continue;
if (details.kind() == kData) {
if (details.location() == kDescriptor) {
prop_value = handle(descriptors->GetStrongValue(i), isolate);
} else {
Representation representation = details.representation();
FieldIndex index = FieldIndex::ForPropertyIndex(
*map, details.field_index(), representation);
prop_value = JSObject::FastPropertyAt(from, representation, index);
}
} else {
Representation representation = details.representation();
FieldIndex index = FieldIndex::ForPropertyIndex(
*map, details.field_index(), representation);
prop_value = JSObject::FastPropertyAt(from, representation, index);
LookupIterator it(isolate, from, next_key,
LookupIterator::OWN_SKIP_INTERCEPTOR);
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, prop_value, Object::GetProperty(&it), Nothing<bool>());
stable = from->map() == *map;
descriptors.PatchValue(map->instance_descriptors(kRelaxedLoad));
}
} else {
LookupIterator it(isolate, from, next_key,
// If the map did change, do a slower lookup. We are still guaranteed
// that the object has a simple shape, and that the key is a name.
LookupIterator it(isolate, from, next_key, from,
LookupIterator::OWN_SKIP_INTERCEPTOR);
if (!it.IsFound()) continue;
DCHECK(it.state() == LookupIterator::DATA ||
it.state() == LookupIterator::ACCESSOR);
if (!it.IsEnumerable()) continue;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, prop_value, Object::GetProperty(&it), Nothing<bool>());
stable = from->map() == *map;
descriptors.PatchValue(map->instance_descriptors(kRelaxedLoad));
}
} else {
// If the map did change, do a slower lookup. We are still guaranteed that
// the object has a simple shape, and that the key is a name.
LookupIterator it(isolate, from, next_key, from,
LookupIterator::OWN_SKIP_INTERCEPTOR);
if (!it.IsFound()) continue;
DCHECK(it.state() == LookupIterator::DATA ||
it.state() == LookupIterator::ACCESSOR);
if (!it.IsEnumerable()) continue;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, prop_value, Object::GetProperty(&it), Nothing<bool>());
}
if (use_set) {
// The lookup will walk the prototype chain, so we have to be careful
// to treat any key correctly for any receiver/holder.
LookupIterator::Key key(isolate, next_key);
LookupIterator it(isolate, target, key);
Maybe<bool> result =
Object::SetProperty(&it, prop_value, StoreOrigin::kNamed,
Just(ShouldThrow::kThrowOnError));
if (result.IsNothing()) return result;
if (stable) {
stable = from->map() == *map;
descriptors.PatchValue(map->instance_descriptors(kRelaxedLoad));
if (use_set) {
// The lookup will walk the prototype chain, so we have to be careful
// to treat any key correctly for any receiver/holder.
LookupIterator::Key key(isolate, next_key);
LookupIterator it(isolate, target, key);
Maybe<bool> result =
Object::SetProperty(&it, prop_value, StoreOrigin::kNamed,
Just(ShouldThrow::kThrowOnError));
if (result.IsNothing()) return result;
if (stable) {
stable = from->map() == *map;
descriptors.PatchValue(map->instance_descriptors(kRelaxedLoad));
}
} else {
if (excluded_properties != nullptr &&
HasExcludedProperty(excluded_properties, next_key)) {
continue;
}
// 4a ii 2. Perform ? CreateDataProperty(target, nextKey, propValue).
// This is an OWN lookup, so constructing a named-mode LookupIterator
// from {next_key} is safe.
LookupIterator it(isolate, target, next_key, LookupIterator::OWN);
CHECK(JSObject::CreateDataProperty(&it, prop_value, Just(kThrowOnError))
.FromJust());
}
} else {
if (excluded_properties != nullptr &&
HasExcludedProperty(excluded_properties, next_key)) {
continue;
}
if (mode == PropertiesEnumerationMode::kEnumerationOrder) {
if (process_symbol_only || !has_symbol) {
return Just(true);
}
// 4a ii 2. Perform ? CreateDataProperty(target, nextKey, propValue).
// This is an OWN lookup, so constructing a named-mode LookupIterator
// from {next_key} is safe.
LookupIterator it(isolate, target, next_key, LookupIterator::OWN);
CHECK(JSObject::CreateDataProperty(&it, prop_value, Just(kThrowOnError))
.FromJust());
if (has_symbol) {
process_symbol_only = true;
}
} else {
DCHECK_EQ(mode, PropertiesEnumerationMode::kPropertyAdditionOrder);
return Just(true);
}
}
return Just(true);
UNREACHABLE();
}
} // namespace
// static
Maybe<bool> JSReceiver::SetOrCopyDataProperties(
Isolate* isolate, Handle<JSReceiver> target, Handle<Object> source,
PropertiesEnumerationMode mode,
const ScopedVector<Handle<Object>>* excluded_properties, bool use_set) {
Maybe<bool> fast_assign =
FastAssign(target, source, excluded_properties, use_set);
FastAssign(target, source, mode, excluded_properties, use_set);
if (fast_assign.IsNothing()) return Nothing<bool>();
if (fast_assign.FromJust()) return Just(true);
......
......@@ -112,6 +112,7 @@ class JSReceiver : public HeapObject {
// maybe_excluded_properties list.
V8_WARN_UNUSED_RESULT static Maybe<bool> SetOrCopyDataProperties(
Isolate* isolate, Handle<JSReceiver> target, Handle<Object> source,
PropertiesEnumerationMode mode,
const ScopedVector<Handle<Object>>* excluded_properties = nullptr,
bool use_set = true);
......
......@@ -1069,7 +1069,9 @@ RUNTIME_FUNCTION(Runtime_SetDataProperties) {
return ReadOnlyRoots(isolate).undefined_value();
}
MAYBE_RETURN(JSReceiver::SetOrCopyDataProperties(isolate, target, source),
MAYBE_RETURN(JSReceiver::SetOrCopyDataProperties(
isolate, target, source,
PropertiesEnumerationMode::kEnumerationOrder),
ReadOnlyRoots(isolate).exception());
return ReadOnlyRoots(isolate).undefined_value();
}
......@@ -1085,9 +1087,11 @@ RUNTIME_FUNCTION(Runtime_CopyDataProperties) {
return ReadOnlyRoots(isolate).undefined_value();
}
MAYBE_RETURN(JSReceiver::SetOrCopyDataProperties(isolate, target, source,
nullptr, false),
ReadOnlyRoots(isolate).exception());
MAYBE_RETURN(
JSReceiver::SetOrCopyDataProperties(
isolate, target, source,
PropertiesEnumerationMode::kPropertyAdditionOrder, nullptr, false),
ReadOnlyRoots(isolate).exception());
return ReadOnlyRoots(isolate).undefined_value();
}
......@@ -1120,8 +1124,10 @@ RUNTIME_FUNCTION(Runtime_CopyDataPropertiesWithExcludedProperties) {
Handle<JSObject> target =
isolate->factory()->NewJSObject(isolate->object_function());
MAYBE_RETURN(JSReceiver::SetOrCopyDataProperties(isolate, target, source,
&excluded_properties, false),
MAYBE_RETURN(JSReceiver::SetOrCopyDataProperties(
isolate, target, source,
PropertiesEnumerationMode::kPropertyAdditionOrder,
&excluded_properties, false),
ReadOnlyRoots(isolate).exception());
return *target;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment