Chromium Code Reviews| Index: src/elements.cc |
| diff --git a/src/elements.cc b/src/elements.cc |
| index 7db641649f5ba204b17806f94144fb9e6dc59eaf..0cd3fce6a03f26f242e0e5aef19927dfb52ac2a9 100644 |
| --- a/src/elements.cc |
| +++ b/src/elements.cc |
| @@ -468,6 +468,27 @@ static void SortIndices( |
| } |
| } |
| +static Maybe<bool> IncludesValueSlowPath(Isolate* isolate, |
| + Handle<JSObject> receiver, |
| + Handle<Object> value, |
| + uint32_t start_from, uint32_t length) { |
| + bool search_for_hole = value->IsUndefined(isolate); |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + LookupIterator it(isolate, receiver, k); |
| + if (!it.IsFound()) { |
| + if (search_for_hole) return Just(true); |
| + continue; |
| + } |
| + Handle<Object> element_k; |
| + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k, |
| + Object::GetProperty(&it), Nothing<bool>()); |
| + |
| + if (value->SameValueZero(*element_k)) return Just(true); |
| + } |
| + |
| + return Just(false); |
| +} |
| + |
| // Base class for element handler implementations. Contains the |
| // the common logic for objects with different ElementsKinds. |
| // Subclasses must specialize method for which the element |
| @@ -1084,6 +1105,20 @@ class ElementsAccessorBase : public ElementsAccessor { |
| return Subclass::GetCapacityImpl(holder, backing_store); |
| } |
| + static Maybe<bool> IncludesValueImpl(Isolate* isolate, |
| + Handle<JSObject> receiver, |
| + Handle<Object> value, |
| + uint32_t start_from, uint32_t length) { |
| + return IncludesValueSlowPath(isolate, receiver, value, start_from, length); |
| + } |
| + |
| + Maybe<bool> IncludesValue(Isolate* isolate, Handle<JSObject> receiver, |
| + Handle<Object> value, uint32_t start_from, |
| + uint32_t length) final { |
| + return Subclass::IncludesValueImpl(isolate, receiver, value, start_from, |
| + length); |
| + } |
| + |
| static uint32_t GetIndexForEntryImpl(FixedArrayBase* backing_store, |
| uint32_t entry) { |
| return entry; |
| @@ -1420,6 +1455,98 @@ class DictionaryElementsAccessor |
| accumulator->AddKey(value, convert); |
| } |
| } |
| + |
| + static bool IncludesValueFastPath(Isolate* isolate, Handle<JSObject> receiver, |
| + Handle<Object> value, uint32_t start_from, |
| + uint32_t length, Maybe<bool>* result) { |
| + DisallowHeapAllocation no_gc; |
| + SeededNumberDictionary* dictionary = |
| + SeededNumberDictionary::cast(receiver->elements()); |
| + int capacity = dictionary->Capacity(); |
| + Object* the_hole = isolate->heap()->the_hole_value(); |
| + for (int i = 0; i < capacity; ++i) { |
| + Object* k = dictionary->KeyAt(i); |
| + if (!dictionary->IsKey(k)) continue; |
| + |
| + Object* element_k = dictionary->ValueAt(i); |
| + if (element_k == the_hole) continue; |
| + |
| + switch (dictionary->DetailsAt(i).kind()) { |
| + case kData: { |
| + if (value->SameValueZero(element_k)) { |
| + *result = Just(true); |
| + return true; |
| + } |
| + break; |
| + } |
| + case kAccessor: { |
| + // Restart from beginning in slow path, otherwise we may observably |
| + // access getters out of order |
| + return false; |
|
caitp
2016/08/01 15:10:45
Unfortunately, this doesn't work. The only thing t
Camillo Bruni
2016/08/01 15:37:03
right. We already have a check HasAccessorsImpl()
caitp
2016/08/01 16:44:55
I've changed this to avoid having to loop through
|
| + } |
| + } |
| + } |
| + *result = Just(false); |
| + return true; |
| + } |
| + |
| + static Maybe<bool> IncludesValueImpl(Isolate* isolate, |
| + Handle<JSObject> receiver, |
| + Handle<Object> value, |
| + uint32_t start_from, uint32_t length) { |
| + bool search_for_hole = value->IsUndefined(isolate); |
| + |
| + if (!search_for_hole) { |
| + Maybe<bool> result = Nothing<bool>(); |
| + if (DictionaryElementsAccessor::IncludesValueFastPath( |
| + isolate, receiver, value, start_from, length, &result)) { |
| + return result; |
| + } |
| + } |
| + |
| + Handle<SeededNumberDictionary> dictionary( |
| + SeededNumberDictionary::cast(receiver->elements()), isolate); |
| + // Iterate through entire range, as accessing elements out of order is |
| + // observable |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + int entry = dictionary->FindEntry(k); |
| + if (entry == SeededNumberDictionary::kNotFound) { |
| + if (search_for_hole) return Just(true); |
| + continue; |
| + } |
| + |
| + PropertyDetails details = GetDetailsImpl(receiver->elements(), entry); |
| + switch (details.kind()) { |
| + case kData: { |
| + Object* element_k = dictionary->ValueAt(entry); |
| + if (value->SameValueZero(element_k)) return Just(true); |
| + break; |
| + } |
| + case kAccessor: { |
| + LookupIterator it(isolate, receiver, k, |
| + LookupIterator::OWN_SKIP_INTERCEPTOR); |
| + DCHECK(it.IsFound()); |
| + DCHECK_EQ(it.state(), LookupIterator::ACCESSOR); |
| + Handle<Object> element_k; |
| + |
| + ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| + isolate, element_k, JSObject::GetPropertyWithAccessor(&it), |
| + Nothing<bool>()); |
| + |
| + if (value->SameValueZero(*element_k)) return Just(true); |
| + |
| + // Some mutation to the prototype elements may have occurred in |
| + // accessor. |
| + if (!JSObject::PrototypeHasNoElements(isolate, *receiver)) { |
| + return IncludesValueSlowPath(isolate, receiver, value, k + 1, |
| + length); |
| + } |
| + break; |
| + } |
| + } |
| + } |
| + return Just(false); |
| + } |
| }; |
| @@ -1780,6 +1907,152 @@ class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> { |
| } |
| } |
| + static Maybe<bool> IncludesValueImpl(Isolate* isolate, |
| + Handle<JSObject> receiver, |
| + Handle<Object> search_value, |
| + uint32_t start_from, uint32_t length) { |
| + DisallowHeapAllocation no_gc; |
| + FixedArrayBase* elements_base = receiver->elements(); |
| + Object* the_hole = isolate->heap()->the_hole_value(); |
| + Object* undefined = isolate->heap()->undefined_value(); |
| + Object* value = *search_value; |
| + |
| + // Elements beyond the capacity of the backing store treated as undefined. |
| + if (value == undefined && elements_base->length() < length) { |
| + return Just(true); |
| + } |
| + |
| + if (start_from >= length) return Just(false); |
| + |
| + if (!value->IsNumber()) { |
| + if (value == undefined) { |
| + // Only FAST_ELEMENTS, FAST_HOLEY_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, and |
| + // FAST_HOLEY_DOUBLE_ELEMENTS can have `undefined` as a value. |
| + if (!IsFastObjectElementsKind(Subclass::kind()) && |
| + !IsFastHoleyElementsKind(Subclass::kind())) { |
| + return Just(false); |
| + } |
| + |
| + // Search for `undefined` or The Hole in FAST_ELEMENTS, |
| + // FAST_HOLEY_ELEMENTS or FAST_HOLEY_SMI_ELEMENTS |
| + if (IsFastSmiOrObjectElementsKind(Subclass::kind())) { |
| + auto elements = FixedArray::cast(receiver->elements()); |
| + |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + Object* element_k = elements->get(k); |
| + |
| + if (IsFastHoleyElementsKind(Subclass::kind()) && |
| + element_k == the_hole) { |
| + return Just(true); |
| + } |
| + if (IsFastObjectElementsKind(Subclass::kind()) && |
| + element_k == undefined) { |
| + return Just(true); |
| + } |
| + } |
| + return Just(false); |
| + } else { |
| + // Seach for The Hole in FAST_HOLEY_DOUBLE_ELEMENTS |
| + DCHECK_EQ(Subclass::kind(), FAST_HOLEY_DOUBLE_ELEMENTS); |
| + auto elements = FixedDoubleArray::cast(receiver->elements()); |
| + |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + if (IsFastHoleyElementsKind(Subclass::kind()) && |
| + elements->is_the_hole(k)) { |
| + return Just(true); |
| + } |
| + } |
| + return Just(false); |
| + } |
| + } else if (!IsFastObjectElementsKind(Subclass::kind())) { |
| + // Search for non-number, non-Undefined value, with either |
| + // FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS or |
| + // FAST_HOLEY_DOUBLE_ELEMENTS. Guaranteed to return false, since these |
| + // elements kinds can only contain Number values or undefined. |
| + return Just(false); |
| + } else { |
| + // Search for non-number, non-Undefined value with either |
| + // FAST_ELEMENTS or FAST_HOLEY_ELEMENTS. |
| + DCHECK(IsFastObjectElementsKind(Subclass::kind())); |
| + auto elements = FixedArray::cast(receiver->elements()); |
| + |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + Object* element_k = elements->get(k); |
| + if (IsFastHoleyElementsKind(Subclass::kind()) && |
| + element_k == the_hole) { |
| + continue; |
| + } |
| + |
| + if (value->SameValueZero(element_k)) return Just(true); |
| + } |
| + return Just(false); |
| + } |
| + } else { |
| + if (!value->IsNaN()) { |
| + double search_value = value->Number(); |
| + if (IsFastDoubleElementsKind(Subclass::kind())) { |
| + // Search for non-NaN Number in FAST_DOUBLE_ELEMENTS or |
| + // FAST_HOLEY_DOUBLE_ELEMENTS --- Skip TheHole, and trust UCOMISD or |
| + // similar operation for result. |
| + auto elements = FixedDoubleArray::cast(receiver->elements()); |
| + |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + if (IsFastHoleyElementsKind(Subclass::kind()) && |
| + elements->is_the_hole(k)) { |
| + continue; |
| + } |
|
Camillo Bruni
2016/08/01 07:23:47
bah, sucks that you can just fold the hole check i
|
| + if (elements->get_scalar(k) == search_value) return Just(true); |
| + } |
| + return Just(false); |
| + } else { |
| + // Search for non-NaN Number in FAST_ELEMENTS, FAST_HOLEY_ELEMENTS, |
| + // FAST_SMI_ELEMENTS or FAST_HOLEY_SMI_ELEMENTS --- Skip non-Numbers, |
| + // and trust UCOMISD or similar operation for result |
| + auto elements = FixedArray::cast(receiver->elements()); |
| + |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + Object* element_k = elements->get(k); |
| + if (element_k->IsNumber() && element_k->Number() == search_value) { |
| + return Just(true); |
| + } |
| + } |
| + return Just(false); |
| + } |
| + } else { |
| + // Search for NaN --- NaN cannot be represented with Smi elements, so |
| + // abort if ElementsKind is FAST_SMI_ELEMENTS or FAST_HOLEY_SMI_ELEMENTS |
| + if (IsFastSmiElementsKind(Subclass::kind())) return Just(false); |
| + |
| + if (IsFastDoubleElementsKind(Subclass::kind())) { |
| + // Search for NaN in FAST_DOUBLE_ELEMENTS or |
| + // FAST_HOLEY_DOUBLE_ELEMENTS --- Skip The Hole and trust |
| + // std::isnan(elementK) for result |
| + auto elements = FixedDoubleArray::cast(receiver->elements()); |
| + |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + if (IsFastHoleyElementsKind(Subclass::kind()) && |
| + elements->is_the_hole(k)) { |
| + continue; |
| + } |
| + if (std::isnan(elements->get_scalar(k))) return Just(true); |
| + } |
| + return Just(false); |
| + } else { |
| + // Search for NaN in FAST_ELEMENTS, FAST_HOLEY_ELEMENTS, |
| + // FAST_SMI_ELEMENTS or FAST_HOLEY_SMI_ELEMENTS. Return true if |
| + // elementK->IsHeapNumber() && std::isnan(elementK->Number()) |
|
Camillo Bruni
2016/08/01 07:23:47
thanks for all the comments!
|
| + DCHECK(IsFastSmiOrObjectElementsKind(Subclass::kind())); |
| + auto elements = FixedArray::cast(receiver->elements()); |
| + |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + if (elements->get(k)->IsNaN()) return Just(true); |
| + } |
| + return Just(false); |
| + } |
| + } |
| + } |
| + } |
| + |
| private: |
| // SpliceShrinkStep might modify the backing_store. |
| static void SpliceShrinkStep(Isolate* isolate, Handle<JSArray> receiver, |
| @@ -2125,17 +2398,17 @@ class FastHoleyDoubleElementsAccessor |
| // Super class for all external element arrays. |
| -template<ElementsKind Kind> |
| +template <ElementsKind Kind, typename ctype> |
| class TypedElementsAccessor |
| - : public ElementsAccessorBase<TypedElementsAccessor<Kind>, |
| - ElementsKindTraits<Kind> > { |
| + : public ElementsAccessorBase<TypedElementsAccessor<Kind, ctype>, |
| + ElementsKindTraits<Kind>> { |
| public: |
| explicit TypedElementsAccessor(const char* name) |
| : ElementsAccessorBase<AccessorClass, |
| ElementsKindTraits<Kind> >(name) {} |
| typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore; |
| - typedef TypedElementsAccessor<Kind> AccessorClass; |
| + typedef TypedElementsAccessor<Kind, ctype> AccessorClass; |
| static inline void SetImpl(Handle<JSObject> holder, uint32_t entry, |
| Object* value) { |
| @@ -2241,12 +2514,51 @@ class TypedElementsAccessor |
| *nof_items = count; |
| return Just(true); |
| } |
| -}; |
| + static Maybe<bool> IncludesValueImpl(Isolate* isolate, |
| + Handle<JSObject> receiver, |
| + Handle<Object> value, |
| + uint32_t start_from, uint32_t length) { |
| + DisallowHeapAllocation no_gc; |
| + |
| + BackingStore* elements = BackingStore::cast(receiver->elements()); |
| + if (value->IsUndefined(isolate) && length > elements->length()) { |
| + return Just(true); |
| + } |
| + if (!value->IsNumber()) return Just(false); |
| + |
| + double search_value = value->Number(); |
| + |
| + if (!std::isfinite(search_value)) { |
| + // Integral types cannot represent +Inf or NaN |
| + if (AccessorClass::kind() < FLOAT32_ELEMENTS || |
| + AccessorClass::kind() > FLOAT64_ELEMENTS) { |
| + return Just(false); |
| + } |
| + } else if (search_value < std::numeric_limits<ctype>::lowest() || |
| + search_value > std::numeric_limits<ctype>::max()) { |
| + // Return false if value can't be represented in this space |
| + return Just(false); |
| + } |
| + if (!std::isnan(search_value)) { |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + double element_k = elements->get_scalar(k); |
| + if (element_k == search_value) return Just(true); |
| + } |
| + return Just(false); |
| + } else { |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + double element_k = elements->get_scalar(k); |
| + if (std::isnan(element_k)) return Just(true); |
| + } |
| + return Just(false); |
| + } |
| + } |
| +}; |
| -#define FIXED_ELEMENTS_ACCESSOR(Type, type, TYPE, ctype, size) \ |
| - typedef TypedElementsAccessor<TYPE##_ELEMENTS > \ |
| +#define FIXED_ELEMENTS_ACCESSOR(Type, type, TYPE, ctype, size) \ |
| + typedef TypedElementsAccessor<TYPE##_ELEMENTS, ctype> \ |
| Fixed##Type##ElementsAccessor; |
| TYPED_ARRAYS(FIXED_ELEMENTS_ACCESSOR) |
| @@ -2481,6 +2793,45 @@ class SloppyArgumentsElementsAccessor |
| isolate, object, store, convert, filter, list, nof_indices, |
| insertion_index); |
| } |
| + |
| + static Maybe<bool> IncludesValueImpl(Isolate* isolate, |
| + Handle<JSObject> object, |
| + Handle<Object> value, |
| + uint32_t start_from, uint32_t length) { |
|
Camillo Bruni
2016/08/01 07:23:47
nit: could you add DCHECK(JSObject::PrototypeHasNo
caitp
2016/08/01 15:10:45
I've added the DCHECK for all of the ElementsAcces
Camillo Bruni
2016/08/01 15:37:03
nice
|
| + Handle<Map> original_map = handle(object->map(), isolate); |
| + FixedArray* parameter_map = FixedArray::cast(object->elements()); |
| + bool search_for_hole = value->IsUndefined(isolate); |
| + |
| + for (uint32_t k = start_from; k < length; ++k) { |
| + uint32_t entry = |
| + GetEntryForIndexImpl(*object, parameter_map, k, ALL_PROPERTIES); |
| + if (entry == kMaxUInt32) { |
| + if (search_for_hole) return Just(true); |
| + continue; |
| + } |
| + |
| + Handle<Object> element_k = GetImpl(parameter_map, entry); |
| + |
| + if (element_k->IsAccessorPair()) { |
| + LookupIterator it(isolate, object, k, LookupIterator::OWN); |
| + DCHECK(it.IsFound()); |
| + DCHECK_EQ(it.state(), LookupIterator::ACCESSOR); |
| + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_k, |
| + Object::GetPropertyWithAccessor(&it), |
| + Nothing<bool>()); |
| + |
| + if (value->SameValueZero(*element_k)) return Just(true); |
| + |
| + if (object->map() != *original_map) { |
| + // Some mutation occurred in accessor. Abort "fast" path |
| + return IncludesValueSlowPath(isolate, object, value, k + 1, length); |
| + } |
| + } else if (value->SameValueZero(*element_k)) { |
| + return Just(true); |
| + } |
| + } |
| + return Just(false); |
| + } |
| }; |