Index: src/builtins.cc |
diff --git a/src/builtins.cc b/src/builtins.cc |
index 3aef9abb15ad267bee440a3e1705840d13a9fa5b..990f7d9eefea0c2367453ebd5b21f719cd68b683 100644 |
--- a/src/builtins.cc |
+++ b/src/builtins.cc |
@@ -1562,6 +1562,75 @@ BUILTIN(ArrayIsArray) { |
return *isolate->factory()->ToBoolean(result.FromJust()); |
} |
+namespace { |
+ |
+MUST_USE_RESULT Maybe<bool> FastAssign(Handle<JSReceiver> to, |
+ Handle<Object> next_source) { |
+ // Non-empty strings are the only non-JSReceivers that need to be handled |
+ // explicitly by Object.assign. |
+ if (!next_source->IsJSReceiver()) { |
+ return Just(!next_source->IsString() || |
+ String::cast(*next_source)->length() == 0); |
+ } |
+ |
+ Isolate* isolate = to->GetIsolate(); |
+ Handle<Map> map(JSReceiver::cast(*next_source)->map(), isolate); |
+ |
+ if (!map->IsJSObjectMap()) return Just(false); |
+ if (!map->OnlyHasSimpleProperties()) return Just(false); |
+ |
+ Handle<JSObject> from = Handle<JSObject>::cast(next_source); |
+ if (from->elements() != isolate->heap()->empty_fixed_array()) { |
+ return Just(false); |
+ } |
+ |
+ Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate); |
+ int length = map->NumberOfOwnDescriptors(); |
+ |
+ for (int i = 0; i < length; i++) { |
+ 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 (from->map() == *map) { |
+ PropertyDetails details = descriptors->GetDetails(i); |
+ if (!details.IsEnumerable()) continue; |
+ if (details.kind() == kData) { |
+ if (details.location() == kDescriptor) { |
+ prop_value = handle(descriptors->GetValue(i), isolate); |
+ } else { |
+ Representation representation = details.representation(); |
+ FieldIndex index = FieldIndex::ForDescriptor(*map, i); |
+ prop_value = JSObject::FastPropertyAt(from, representation, index); |
+ } |
+ } else { |
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
+ isolate, prop_value, Object::GetProperty(from, next_key, STRICT), |
+ Nothing<bool>()); |
+ } |
+ } 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(from, next_key, 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, STRICT), |
+ Nothing<bool>()); |
+ } |
+ Handle<Object> status; |
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
+ isolate, status, |
+ Object::SetProperty(to, next_key, prop_value, STRICT, |
+ Object::CERTAINLY_NOT_STORE_FROM_KEYED), |
+ Nothing<bool>()); |
+ } |
+ |
+ return Just(true); |
+} |
+ |
+} // namespace |
// ES6 19.1.2.1 Object.assign |
BUILTIN(ObjectAssign) { |
@@ -1579,10 +1648,13 @@ BUILTIN(ObjectAssign) { |
// 4. For each element nextSource of sources, in ascending index order, |
for (int i = 2; i < args.length(); ++i) { |
Handle<Object> next_source = args.at<Object>(i); |
+ Maybe<bool> fast_assign = FastAssign(to, next_source); |
+ if (fast_assign.IsNothing()) return isolate->heap()->exception(); |
+ if (fast_assign.FromJust()) continue; |
// 4a. If nextSource is undefined or null, let keys be an empty List. |
- if (next_source->IsUndefined() || next_source->IsNull()) continue; |
// 4b. Else, |
// 4b i. Let from be ToObject(nextSource). |
+ // Only non-empty strings and JSReceivers have enumerable properties. |
Handle<JSReceiver> from = |
Object::ToObject(isolate, next_source).ToHandleChecked(); |
// 4b ii. Let keys be ? from.[[OwnPropertyKeys]](). |