Commit da5d713d authored by mattloring's avatar mattloring Committed by Commit bot

[builtins] Array indexOf in TurboFan/Runtime

Includes fast paths in the runtime for
DictionaryElementsAccessor, FastSmiOrObjectElementsAccessor,
FastDoubleElementsAccessor, TypedElementsAccessor, and
SloppyArgumentsElementsAccessor.

BUG=

Review-Url: https://codereview.chromium.org/2232063002
Cr-Commit-Position: refs/heads/master@{#38800}
parent 7b8d7604
This diff is collapsed.
......@@ -198,6 +198,7 @@ namespace internal {
TFJ(ArrayIsArray, 2) \
/* ES7 #sec-array.prototype.includes */ \
TFJ(ArrayIncludes, 3) \
TFJ(ArrayIndexOf, 3) \
CPP(ArrayPop) \
CPP(ArrayPush) \
CPP(ArrayShift) \
......
This diff is collapsed.
......@@ -160,6 +160,13 @@ class ElementsAccessor {
Handle<Object> value, uint32_t start,
uint32_t length) = 0;
// Check an Object's own elements for the index of an element (using SameValue
// semantics)
virtual Maybe<int64_t> IndexOfValue(Isolate* isolate,
Handle<JSObject> receiver,
Handle<Object> value, uint32_t start,
uint32_t length) = 0;
protected:
friend class LookupIterator;
......
......@@ -1135,72 +1135,6 @@ function ArrayMap(f, receiver) {
}
// For .indexOf, we don't need to pass in the number of arguments
// at the callsite since ToInteger(undefined) == 0; however, for
// .lastIndexOf, we need to pass it, since the behavior for passing
// undefined is 0 but for not including the argument is length-1.
function InnerArrayIndexOf(array, element, index, length) {
if (length == 0) return -1;
if (IS_UNDEFINED(index)) {
index = 0;
} else {
index = INVERT_NEG_ZERO(TO_INTEGER(index));
// If index is negative, index from the end of the array.
if (index < 0) {
index = length + index;
// If index is still negative, search the entire array.
if (index < 0) index = 0;
}
}
var min = index;
var max = length;
if (UseSparseVariant(array, length, IS_ARRAY(array), max - min)) {
%NormalizeElements(array);
var indices = %GetArrayKeys(array, length);
if (IS_NUMBER(indices)) {
// It's an interval.
max = indices; // Capped by length already.
// Fall through to loop below.
} else {
if (indices.length == 0) return -1;
// Get all the keys in sorted order.
var sortedKeys = GetSortedArrayKeys(array, indices);
var n = sortedKeys.length;
var i = 0;
while (i < n && sortedKeys[i] < index) i++;
while (i < n) {
var key = sortedKeys[i];
if (array[key] === element) return key;
i++;
}
return -1;
}
}
// Lookup through the array.
if (!IS_UNDEFINED(element)) {
for (var i = min; i < max; i++) {
if (array[i] === element) return i;
}
return -1;
}
// Lookup through the array.
for (var i = min; i < max; i++) {
if (IS_UNDEFINED(array[i]) && i in array) {
return i;
}
}
return -1;
}
function ArrayIndexOf(element, index) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf");
var length = TO_LENGTH(this.length);
return InnerArrayIndexOf(this, element, index, length);
}
function InnerArrayLastIndexOf(array, element, index, length, argumentsLength) {
if (length == 0) return -1;
if (argumentsLength < 2) {
......@@ -1626,7 +1560,7 @@ utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
"some", getFunction("some", ArraySome, 1),
"every", getFunction("every", ArrayEvery, 1),
"map", getFunction("map", ArrayMap, 1),
"indexOf", getFunction("indexOf", ArrayIndexOf, 1),
"indexOf", getFunction("indexOf", null, 1),
"lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
"reduce", getFunction("reduce", ArrayReduce, 1),
"reduceRight", getFunction("reduceRight", ArrayReduceRight, 1),
......@@ -1645,7 +1579,7 @@ utils.InstallGetter(GlobalArray, speciesSymbol, ArraySpecies);
// exposed to user code.
// Adding only the functions that are actually used.
utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [
"indexOf", getFunction("indexOf", ArrayIndexOf),
"indexOf", getFunction("indexOf", null),
"join", getFunction("join", ArrayJoin),
"pop", getFunction("pop", ArrayPop),
"push", getFunction("push", ArrayPush),
......@@ -1677,7 +1611,6 @@ utils.SetUpLockedPrototype(extrasUtils.InternalPackedArray, GlobalArray(), [
utils.Export(function(to) {
to.ArrayFrom = ArrayFrom;
to.ArrayIndexOf = ArrayIndexOf;
to.ArrayJoin = ArrayJoin;
to.ArrayPush = ArrayPush;
to.ArrayToString = ArrayToString;
......@@ -1688,7 +1621,6 @@ utils.Export(function(to) {
to.InnerArrayFind = InnerArrayFind;
to.InnerArrayFindIndex = InnerArrayFindIndex;
to.InnerArrayForEach = InnerArrayForEach;
to.InnerArrayIndexOf = InnerArrayIndexOf;
to.InnerArrayJoin = InnerArrayJoin;
to.InnerArrayLastIndexOf = InnerArrayLastIndexOf;
to.InnerArrayReduce = InnerArrayReduce;
......
......@@ -17,7 +17,6 @@
// -------------------------------------------------------------------
// Imports
var ArrayIndexOf;
var ArrayJoin;
var ArrayPush;
var FLAG_intl_extra;
......@@ -42,7 +41,6 @@ var StringSubstr;
var StringSubstring;
utils.Import(function(from) {
ArrayIndexOf = from.ArrayIndexOf;
ArrayJoin = from.ArrayJoin;
ArrayPush = from.ArrayPush;
IsNaN = from.IsNaN;
......@@ -369,7 +367,7 @@ function getGetOption(options, caller) {
throw %make_error(kWrongValueType);
}
if (!IS_UNDEFINED(values) && %_Call(ArrayIndexOf, values, value) === -1) {
if (!IS_UNDEFINED(values) && %ArrayIndexOf(values, value, 0) === -1) {
throw %make_range_error(kValueOutOfRange, value, caller, property);
}
......@@ -803,7 +801,7 @@ function canonicalizeLocaleList(locales) {
var tag = canonicalizeLanguageTag(value);
if (%_Call(ArrayIndexOf, seen, tag) === -1) {
if (%ArrayIndexOf(seen, tag, 0) === -1) {
%_Call(ArrayPush, seen, tag);
}
}
......@@ -859,7 +857,7 @@ function isStructuallyValidLanguageTag(locale) {
var value = parts[i];
if (!IS_NULL(InternalRegExpMatch(GetLanguageVariantRE(), value)) &&
extensions.length === 0) {
if (%_Call(ArrayIndexOf, variants, value) === -1) {
if (%ArrayIndexOf(variants, value, 0) === -1) {
%_Call(ArrayPush, variants, value);
} else {
return false;
......@@ -867,7 +865,7 @@ function isStructuallyValidLanguageTag(locale) {
}
if (!IS_NULL(InternalRegExpMatch(GetLanguageSingletonRE(), value))) {
if (%_Call(ArrayIndexOf, extensions, value) === -1) {
if (%ArrayIndexOf(extensions, value, 0) === -1) {
%_Call(ArrayPush, extensions, value);
} else {
return false;
......@@ -1005,7 +1003,7 @@ function initializeCollator(collator, locales, options) {
'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin'
];
if (%_Call(ArrayIndexOf, ALLOWED_CO_VALUES, extensionMap.co) !== -1) {
if (%ArrayIndexOf(ALLOWED_CO_VALUES, extensionMap.co, 0) !== -1) {
extension = '-u-co-' + extensionMap.co;
// ICU can't tell us what the collation is, so save user's input.
collation = extensionMap.co;
......@@ -2088,7 +2086,7 @@ function LocaleConvertCase(s, locales, isToUpper) {
}
var CUSTOM_CASE_LANGUAGES = ['az', 'el', 'lt', 'tr'];
var langIndex = %_Call(ArrayIndexOf, CUSTOM_CASE_LANGUAGES, language);
var langIndex = %ArrayIndexOf(CUSTOM_CASE_LANGUAGES, language, 0);
if (langIndex == -1) {
// language-independent case conversion.
return isToUpper ? %StringToUpperCaseI18N(s) : %StringToLowerCaseI18N(s);
......@@ -2139,7 +2137,7 @@ OverrideFunction(GlobalString.prototype, 'normalize', function() {
var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form);
var normalizationForm = %ArrayIndexOf(NORMALIZATION_FORMS, form, 0);
if (normalizationForm === -1) {
throw %make_range_error(kNormalizationForm,
%_Call(ArrayJoin, NORMALIZATION_FORMS, ', '));
......
......@@ -9,7 +9,6 @@
// -------------------------------------------------------------------
// Imports
var ArrayIndexOf;
var ArrayJoin;
var GlobalRegExp = global.RegExp;
var GlobalString = global.String;
......@@ -23,7 +22,6 @@ var searchSymbol = utils.ImportNow("search_symbol");
var splitSymbol = utils.ImportNow("split_symbol");
utils.Import(function(from) {
ArrayIndexOf = from.ArrayIndexOf;
ArrayJoin = from.ArrayJoin;
IsRegExp = from.IsRegExp;
MaxSimple = from.MaxSimple;
......@@ -132,7 +130,7 @@ function StringNormalize(formArg) { // length == 0
var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg);
var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form);
var normalizationForm = %ArrayIndexOf(NORMALIZATION_FORMS, form, 0);
if (normalizationForm === -1) {
throw %make_range_error(kNormalizationForm,
%_Call(ArrayJoin, NORMALIZATION_FORMS, ', '));
......
......@@ -70,6 +70,7 @@ RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) {
InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice);
InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice);
InstallBuiltin(isolate, holder, "includes", Builtins::kArrayIncludes, 2);
InstallBuiltin(isolate, holder, "indexOf", Builtins::kArrayIndexOf, 2);
return *holder;
}
......@@ -544,5 +545,105 @@ RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow) {
return isolate->heap()->false_value();
}
RUNTIME_FUNCTION(Runtime_ArrayIndexOf) {
HandleScope shs(isolate);
DCHECK(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2);
// Let O be ? ToObject(this value).
Handle<Object> receiver_obj = args.at<Object>(0);
if (receiver_obj->IsNull(isolate) || receiver_obj->IsUndefined(isolate)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
isolate->factory()->NewStringFromAsciiChecked(
"Array.prototype.indexOf")));
}
Handle<JSReceiver> object;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, object, Object::ToObject(isolate, args.at<Object>(0)));
// Let len be ? ToLength(? Get(O, "length")).
int64_t len;
{
if (object->IsJSArray()) {
uint32_t len32 = 0;
bool success = JSArray::cast(*object)->length()->ToArrayLength(&len32);
DCHECK(success);
USE(success);
len = len32;
} else {
Handle<Object> len_;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, len_,
Object::GetProperty(object, isolate->factory()->length_string()));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
Object::ToLength(isolate, len_));
len = static_cast<int64_t>(len_->Number());
DCHECK_EQ(len, len_->Number());
}
}
if (len == 0) return Smi::FromInt(-1);
// Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step
// produces the value 0.)
int64_t start_from;
{
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index,
Object::ToInteger(isolate, from_index));
double fp = from_index->Number();
if (fp > len) return Smi::FromInt(-1);
start_from = static_cast<int64_t>(fp);
}
int64_t index;
if (start_from >= 0) {
index = start_from;
} else {
index = len + start_from;
if (index < 0) {
index = 0;
}
}
// If the receiver is not a special receiver type, and the length is a valid
// element index, perform fast operation tailored to specific ElementsKinds.
if (object->map()->instance_type() > LAST_SPECIAL_RECEIVER_TYPE &&
len < kMaxUInt32 &&
JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
Handle<JSObject> obj = Handle<JSObject>::cast(object);
ElementsAccessor* elements = obj->GetElementsAccessor();
Maybe<int64_t> result = elements->IndexOfValue(isolate, obj, search_element,
static_cast<uint32_t>(index),
static_cast<uint32_t>(len));
MAYBE_RETURN(result, isolate->heap()->exception());
return *isolate->factory()->NewNumberFromInt64(result.FromJust());
}
// Otherwise, perform slow lookups for special receiver types
for (; index < len; ++index) {
// Let elementK be the result of ? Get(O, ! ToString(k)).
Handle<Object> element_k;
{
Handle<Object> index_obj = isolate->factory()->NewNumberFromInt64(index);
bool success;
LookupIterator it = LookupIterator::PropertyOrElement(
isolate, object, index_obj, &success);
DCHECK(success);
if (!JSReceiver::HasProperty(&it).FromJust()) {
continue;
}
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
Object::GetProperty(&it));
if (search_element->StrictEquals(*element_k)) {
return *index_obj;
}
}
}
return Smi::FromInt(-1);
}
} // namespace internal
} // namespace v8
......@@ -56,7 +56,8 @@ namespace internal {
F(FixedArrayGet, 2, 1) \
F(FixedArraySet, 3, 1) \
F(ArraySpeciesConstructor, 1, 1) \
F(ArrayIncludes_Slow, 3, 1)
F(ArrayIncludes_Slow, 3, 1) \
F(ArrayIndexOf, 3, 1)
#define FOR_EACH_INTRINSIC_ATOMICS(F) \
F(ThrowNotIntegerSharedTypedArrayError, 1, 1) \
......
This diff is collapsed.
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