Commit 855850eb authored by Simon Zünd's avatar Simon Zünd Committed by Commit Bot

[array] Move CopyFromPrototype to runtime

This CL re-implements CopyFromPrototype, that is used during sorting,
as a runtime function, in preparation to move Array.p.sort to CSA.

CopyFromPrototype is called for sparse non-arrays, where elements
might be available on the prototype chain. For compatibility with
JSC, we copy them to the object itself and sort only own properties.

Bug: v8:7382
Change-Id: I4f5c14995cf9769c4f9f1d62b3a5bfde6d386556
Reviewed-on: https://chromium-review.googlesource.com/1044205
Commit-Queue: Simon Zünd <szuend@google.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53061}
parent 2793d72c
......@@ -345,6 +345,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(LoadLookupSlotForCall) \
/* Arrays */ \
V(ArraySpeciesConstructor) \
V(CopyFromPrototype) \
V(EstimateNumberOfElements) \
V(GetArrayKeys) \
V(HasComplexElements) \
......
......@@ -801,36 +801,6 @@ function InnerArraySort(array, length, comparefn) {
}
};
// Copy elements in the range 0..length from obj's prototype chain
// to obj itself, if obj has holes. Return one more than the maximal index
// of a prototype property.
function CopyFromPrototype(obj, length) {
var max = 0;
for (var proto = %object_get_prototype_of(obj); proto;
proto = %object_get_prototype_of(proto)) {
var indices = IS_PROXY(proto) ? length : %GetArrayKeys(proto, length);
if (IS_NUMBER(indices)) {
// It's an interval.
var proto_length = indices;
for (var i = 0; i < proto_length; i++) {
if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) {
obj[i] = proto[i];
if (i >= max) { max = i + 1; }
}
}
} else {
for (var i = 0; i < indices.length; i++) {
var index = indices[i];
if (!HAS_OWN_PROPERTY(obj, index) && HAS_OWN_PROPERTY(proto, index)) {
obj[index] = proto[index];
if (index >= max) { max = index + 1; }
}
}
}
}
return max;
};
// Set a value of "undefined" on all indices in the range from..to
// where a prototype of obj has an element. I.e., shadow all prototype
// elements in that range.
......@@ -870,7 +840,7 @@ function InnerArraySort(array, length, comparefn) {
// The specification allows "implementation dependent" behavior
// if an element on the prototype chain has an element that
// might interact with sorting.
max_prototype_element = CopyFromPrototype(array, length);
max_prototype_element = %CopyFromPrototype(array, length);
}
// %RemoveArrayHoles moves all non-undefined elements to the front of the
......
......@@ -322,6 +322,99 @@ RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) {
return PrepareElementsForSort(object, limit);
}
namespace {
// Copy element at index from source to target only if target does not have the
// element on its own. Returns true if a copy occurred, false if not
// and Nothing if an exception occurred.
Maybe<bool> ConditionalCopy(Isolate* isolate, Handle<JSReceiver> source,
Handle<JSReceiver> target, uint32_t index) {
Maybe<bool> source_has_prop = JSReceiver::HasOwnProperty(source, index);
MAYBE_RETURN(source_has_prop, Nothing<bool>());
if (!source_has_prop.FromJust()) return Just(false);
Maybe<bool> target_has_prop = JSReceiver::HasOwnProperty(target, index);
MAYBE_RETURN(target_has_prop, Nothing<bool>());
if (target_has_prop.FromJust()) return Just(false);
Handle<Object> source_element;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, source_element, JSReceiver::GetElement(isolate, source, index),
Nothing<bool>());
Handle<Object> set_result;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, set_result,
JSReceiver::SetElement(isolate, target, index, source_element,
LanguageMode::kStrict),
Nothing<bool>());
return Just(true);
}
} // namespace
// Copy elements in the range 0..length from objects prototype chain
// to object itself, if object has holes. Return one more than the maximal
// index of a prototype property.
RUNTIME_FUNCTION(Runtime_CopyFromPrototype) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, object, 0);
DCHECK(!object->IsJSArray());
CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
if (isolate->debug_execution_mode() == DebugInfo::kSideEffects) {
if (!isolate->debug()->PerformSideEffectCheckForObject(object)) {
return isolate->heap()->exception();
}
}
uint32_t max = 0;
for (PrototypeIterator iter(isolate, object, kStartAtPrototype);
!iter.IsAtEnd(); iter.Advance()) {
Handle<JSReceiver> current(PrototypeIterator::GetCurrent<JSReceiver>(iter));
if (current->IsJSProxy()) {
for (uint32_t i = 0; i < length; ++i) {
Maybe<bool> did_copy = ConditionalCopy(isolate, current, object, i);
MAYBE_RETURN(did_copy, isolate->heap()->exception());
if (did_copy.FromJust() && i >= max) max = i + 1;
}
} else {
KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
ALL_PROPERTIES);
accumulator.CollectOwnElementIndices(object,
Handle<JSObject>::cast(current));
Handle<FixedArray> keys =
accumulator.GetKeys(GetKeysConversion::kKeepNumbers);
#ifdef DEBUG
// Check that keys are sorted.
for (int i = 1; i < keys->length(); ++i) {
uint32_t a = NumberToUint32(keys->get(i - 1));
uint32_t b = NumberToUint32(keys->get(i));
DCHECK_LE(a, b);
}
#endif
uint32_t num_indices = keys->length();
for (uint32_t i = 0; i < num_indices; ++i) {
uint32_t idx = NumberToUint32(keys->get(i));
// Prototype might have indices that go past length, but we are only
// interested in the range [0, length).
if (idx >= length) break;
Maybe<bool> did_copy = ConditionalCopy(isolate, current, object, idx);
MAYBE_RETURN(did_copy, isolate->heap()->exception());
if (did_copy.FromJust() && idx >= max) max = idx + 1;
}
}
}
return *isolate->factory()->NewNumberFromUint(max);
}
// Move contents of argument 0 (an array) to argument 1 (an array)
RUNTIME_FUNCTION(Runtime_MoveArrayContents) {
......
......@@ -50,6 +50,7 @@ namespace internal {
F(NewArray, -1 /* >= 3 */, 1) \
F(NormalizeElements, 1, 1) \
F(RemoveArrayHoles, 2, 1) \
F(CopyFromPrototype, 2, 1) \
F(TransitionElementsKind, 2, 1) \
F(TrySliceSimpleNonFastElements, 3, 1)
......
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