Revert of [builtins] implement Array.prototype.includes in TurboFan (patchset #20 id:380001 of https://codereview.chromium.org/2146293003/ )

Reason for revert:
[Sheriff] Breaks:
https://build.chromium.org/p/client.v8.ports/builders/V8%20Arm%20-%20builder/builds/2592

Original issue's description:
> [builtins] implement Array.prototype.includes in TurboFan
>
> BUG=v8:5162
> R=bmeurer@chromium.org, ishell@chromium.org
>
> Committed: https://crrev.com/a488b5d8eb111a4883dc400bd826d079420edd68
> Cr-Commit-Position: refs/heads/master@{#38223}

TBR=adamk@chromium.org,bmeurer@chromium.org,cbruni@chromium.org,danno@chromium.org,ishell@chromium.org,littledan@chromium.org,caitp@igalia.com
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:5162

Review-Url: https://codereview.chromium.org/2202163002
Cr-Commit-Position: refs/heads/master@{#38226}
This commit is contained in:
machenbach 2016-08-01 23:52:28 -07:00 committed by Commit bot
parent a9d51f9e97
commit c98f3a98d7
19 changed files with 153 additions and 1887 deletions

View File

@ -4,8 +4,6 @@
#include "src/builtins/builtins.h" #include "src/builtins/builtins.h"
#include "src/builtins/builtins-utils.h" #include "src/builtins/builtins-utils.h"
#include "src/code-factory.h"
#include "src/elements.h" #include "src/elements.h"
namespace v8 { namespace v8 {
@ -57,9 +55,23 @@ inline bool GetSloppyArgumentsLength(Isolate* isolate, Handle<JSObject> object,
return *out <= object->elements()->length(); return *out <= object->elements()->length();
} }
inline bool PrototypeHasNoElements(Isolate* isolate, JSObject* object) {
DisallowHeapAllocation no_gc;
HeapObject* prototype = HeapObject::cast(object->map()->prototype());
HeapObject* null = isolate->heap()->null_value();
HeapObject* empty = isolate->heap()->empty_fixed_array();
while (prototype != null) {
Map* map = prototype->map();
if (map->instance_type() <= LAST_CUSTOM_ELEMENTS_RECEIVER) return false;
if (JSObject::cast(prototype)->elements() != empty) return false;
prototype = HeapObject::cast(map->prototype());
}
return true;
}
inline bool IsJSArrayFastElementMovingAllowed(Isolate* isolate, inline bool IsJSArrayFastElementMovingAllowed(Isolate* isolate,
JSArray* receiver) { JSArray* receiver) {
return JSObject::PrototypeHasNoElements(isolate, receiver); return PrototypeHasNoElements(isolate, receiver);
} }
inline bool HasSimpleElements(JSObject* current) { inline bool HasSimpleElements(JSObject* current) {
@ -71,7 +83,7 @@ inline bool HasOnlySimpleReceiverElements(Isolate* isolate,
JSObject* receiver) { JSObject* receiver) {
// Check that we have no accessors on the receiver's elements. // Check that we have no accessors on the receiver's elements.
if (!HasSimpleElements(receiver)) return false; if (!HasSimpleElements(receiver)) return false;
return JSObject::PrototypeHasNoElements(isolate, receiver); return PrototypeHasNoElements(isolate, receiver);
} }
inline bool HasOnlySimpleElements(Isolate* isolate, JSReceiver* receiver) { inline bool HasOnlySimpleElements(Isolate* isolate, JSReceiver* receiver) {
@ -1259,485 +1271,5 @@ void Builtins::Generate_ArrayIsArray(CodeStubAssembler* assembler) {
assembler->CallRuntime(Runtime::kArrayIsArray, context, object)); assembler->CallRuntime(Runtime::kArrayIsArray, context, object));
} }
void Builtins::Generate_ArrayIncludes(CodeStubAssembler* assembler) {
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
Node* array = assembler->Parameter(0);
Node* search_element = assembler->Parameter(1);
Node* start_from = assembler->Parameter(2);
Node* context = assembler->Parameter(3 + 2);
Node* int32_zero = assembler->Int32Constant(0);
Node* int32_one = assembler->Int32Constant(1);
Node* the_hole = assembler->TheHoleConstant();
Node* undefined = assembler->UndefinedConstant();
Node* heap_number_map = assembler->HeapNumberMapConstant();
Variable len_var(assembler, MachineRepresentation::kWord32),
index_var(assembler, MachineRepresentation::kWord32),
start_from_var(assembler, MachineRepresentation::kWord32);
Label init_k(assembler), return_true(assembler), return_false(assembler),
call_runtime(assembler);
Label init_len(assembler);
index_var.Bind(int32_zero);
len_var.Bind(int32_zero);
// Take slow path if not a JSArray, if retrieving elements requires
// traversing prototype, or if access checks are required.
assembler->BranchIfFastJSArray(array, context, &init_len, &call_runtime);
assembler->Bind(&init_len);
{
// Handle case where JSArray length is not an Smi in the runtime
Node* len = assembler->LoadObjectField(array, JSArray::kLengthOffset);
assembler->GotoUnless(assembler->WordIsSmi(len), &call_runtime);
len_var.Bind(assembler->SmiToWord(len));
assembler->Branch(assembler->Word32Equal(len_var.value(), int32_zero),
&return_false, &init_k);
}
assembler->Bind(&init_k);
{
Label done(assembler), init_k_smi(assembler), init_k_heap_num(assembler),
init_k_zero(assembler), init_k_n(assembler);
Callable call_to_integer = CodeFactory::ToInteger(assembler->isolate());
Node* tagged_n = assembler->CallStub(call_to_integer, context, start_from);
assembler->Branch(assembler->WordIsSmi(tagged_n), &init_k_smi,
&init_k_heap_num);
assembler->Bind(&init_k_smi);
{
start_from_var.Bind(assembler->SmiToWord32(tagged_n));
assembler->Goto(&init_k_n);
}
assembler->Bind(&init_k_heap_num);
{
Label do_return_false(assembler);
Node* fp_len = assembler->ChangeInt32ToFloat64(len_var.value());
Node* fp_n = assembler->LoadHeapNumberValue(tagged_n);
assembler->GotoIf(assembler->Float64GreaterThanOrEqual(fp_n, fp_len),
&do_return_false);
start_from_var.Bind(assembler->TruncateFloat64ToWord32(fp_n));
assembler->Goto(&init_k_n);
assembler->Bind(&do_return_false);
{
index_var.Bind(int32_zero);
assembler->Goto(&return_false);
}
}
assembler->Bind(&init_k_n);
{
Label if_positive(assembler), if_negative(assembler), done(assembler);
assembler->Branch(
assembler->Int32LessThan(start_from_var.value(), int32_zero),
&if_negative, &if_positive);
assembler->Bind(&if_positive);
{
index_var.Bind(start_from_var.value());
assembler->Goto(&done);
}
assembler->Bind(&if_negative);
{
index_var.Bind(
assembler->Int32Add(len_var.value(), start_from_var.value()));
assembler->Branch(
assembler->Int32LessThan(index_var.value(), int32_zero),
&init_k_zero, &done);
}
assembler->Bind(&init_k_zero);
{
index_var.Bind(int32_zero);
assembler->Goto(&done);
}
assembler->Bind(&done);
}
}
static int32_t kElementsKind[] = {
FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS,
FAST_HOLEY_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS,
};
Label if_smiorobjects(assembler), if_packed_doubles(assembler),
if_holey_doubles(assembler);
Label* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects,
&if_smiorobjects, &if_smiorobjects,
&if_packed_doubles, &if_holey_doubles};
Node* map = assembler->LoadMap(array);
Node* bit_field2 = assembler->LoadMapBitField2(map);
Node* elements_kind =
assembler->BitFieldDecode<Map::ElementsKindBits>(bit_field2);
Node* elements = assembler->LoadElements(array);
assembler->Switch(elements_kind, &return_false, kElementsKind,
element_kind_handlers, arraysize(kElementsKind));
assembler->Bind(&if_smiorobjects);
{
Variable search_num(assembler, MachineRepresentation::kFloat64);
Label ident_loop(assembler, &index_var),
heap_num_loop(assembler, &search_num),
string_loop(assembler, &index_var), simd_loop(assembler),
undef_loop(assembler, &index_var), not_smi(assembler),
not_heap_num(assembler);
assembler->GotoUnless(assembler->WordIsSmi(search_element), &not_smi);
search_num.Bind(assembler->SmiToFloat64(search_element));
assembler->Goto(&heap_num_loop);
assembler->Bind(&not_smi);
assembler->GotoIf(assembler->WordEqual(search_element, undefined),
&undef_loop);
Node* map = assembler->LoadMap(search_element);
assembler->GotoIf(assembler->WordNotEqual(map, heap_number_map),
&not_heap_num);
search_num.Bind(assembler->LoadHeapNumberValue(search_element));
assembler->Goto(&heap_num_loop);
assembler->Bind(&not_heap_num);
Node* search_type = assembler->LoadMapInstanceType(map);
assembler->GotoIf(
assembler->Int32LessThan(
search_type, assembler->Int32Constant(FIRST_NONSTRING_TYPE)),
&string_loop);
assembler->GotoIf(
assembler->WordEqual(search_type,
assembler->Int32Constant(SIMD128_VALUE_TYPE)),
&simd_loop);
assembler->Goto(&ident_loop);
assembler->Bind(&ident_loop);
{
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
Node* element_k =
assembler->LoadFixedArrayElement(elements, index_var.value());
assembler->GotoIf(assembler->WordEqual(element_k, search_element),
&return_true);
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&ident_loop);
}
assembler->Bind(&undef_loop);
{
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
Node* element_k =
assembler->LoadFixedArrayElement(elements, index_var.value());
assembler->GotoIf(assembler->WordEqual(element_k, undefined),
&return_true);
assembler->GotoIf(assembler->WordEqual(element_k, the_hole),
&return_true);
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&undef_loop);
}
assembler->Bind(&heap_num_loop);
{
Label nan_loop(assembler, &index_var),
not_nan_loop(assembler, &index_var);
assembler->BranchIfFloat64IsNaN(search_num.value(), &nan_loop,
&not_nan_loop);
assembler->Bind(&not_nan_loop);
{
Label continue_loop(assembler), not_smi(assembler);
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
Node* element_k =
assembler->LoadFixedArrayElement(elements, index_var.value());
assembler->GotoUnless(assembler->WordIsSmi(element_k), &not_smi);
assembler->Branch(
assembler->Float64Equal(search_num.value(),
assembler->SmiToFloat64(element_k)),
&return_true, &continue_loop);
assembler->Bind(&not_smi);
assembler->GotoIf(assembler->WordNotEqual(assembler->LoadMap(element_k),
heap_number_map),
&continue_loop);
assembler->BranchIfFloat64Equal(
search_num.value(), assembler->LoadHeapNumberValue(element_k),
&return_true, &continue_loop);
assembler->Bind(&continue_loop);
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&not_nan_loop);
}
assembler->Bind(&nan_loop);
{
Label continue_loop(assembler);
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
Node* element_k =
assembler->LoadFixedArrayElement(elements, index_var.value());
assembler->GotoIf(assembler->WordIsSmi(element_k), &continue_loop);
assembler->GotoIf(assembler->WordNotEqual(assembler->LoadMap(element_k),
heap_number_map),
&continue_loop);
assembler->BranchIfFloat64IsNaN(
assembler->LoadHeapNumberValue(element_k), &return_true,
&continue_loop);
assembler->Bind(&continue_loop);
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&nan_loop);
}
}
assembler->Bind(&string_loop);
{
Label continue_loop(assembler);
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
Node* element_k =
assembler->LoadFixedArrayElement(elements, index_var.value());
assembler->GotoIf(assembler->WordIsSmi(element_k), &continue_loop);
assembler->GotoUnless(assembler->Int32LessThan(
assembler->LoadMapInstanceType(element_k),
assembler->Int32Constant(FIRST_NONSTRING_TYPE)),
&continue_loop);
// TODO(bmeurer): Consider inlining the StringEqual logic here.
Callable callable = CodeFactory::StringEqual(assembler->isolate());
Node* result =
assembler->CallStub(callable, context, search_element, element_k);
assembler->Branch(
assembler->WordEqual(assembler->BooleanConstant(true), result),
&return_true, &continue_loop);
assembler->Bind(&continue_loop);
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&string_loop);
}
assembler->Bind(&simd_loop);
{
Label continue_loop(assembler, &index_var),
loop_body(assembler, &index_var);
Node* map = assembler->LoadMap(search_element);
assembler->Goto(&loop_body);
assembler->Bind(&loop_body);
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
Node* element_k =
assembler->LoadFixedArrayElement(elements, index_var.value());
assembler->GotoIf(assembler->WordIsSmi(element_k), &continue_loop);
Node* map_k = assembler->LoadMap(element_k);
assembler->BranchIfSimd128Equal(search_element, map, element_k, map_k,
&return_true, &continue_loop);
assembler->Bind(&continue_loop);
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&loop_body);
}
}
assembler->Bind(&if_packed_doubles);
{
Label nan_loop(assembler, &index_var), not_nan_loop(assembler, &index_var),
hole_loop(assembler, &index_var), search_notnan(assembler);
Variable search_num(assembler, MachineRepresentation::kFloat64);
assembler->GotoUnless(assembler->WordIsSmi(search_element), &search_notnan);
search_num.Bind(assembler->SmiToFloat64(search_element));
assembler->Goto(&not_nan_loop);
assembler->Bind(&search_notnan);
assembler->GotoIf(assembler->WordNotEqual(
assembler->LoadMap(search_element), heap_number_map),
&return_false);
search_num.Bind(assembler->LoadHeapNumberValue(search_element));
assembler->BranchIfFloat64IsNaN(search_num.value(), &nan_loop,
&not_nan_loop);
// Search for HeapNumber
assembler->Bind(&not_nan_loop);
{
Label continue_loop(assembler);
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
Node* element_k = assembler->LoadFixedDoubleArrayElement(
elements, index_var.value(), MachineType::Float64());
assembler->BranchIfFloat64Equal(element_k, search_num.value(),
&return_true, &continue_loop);
assembler->Bind(&continue_loop);
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&not_nan_loop);
}
// Search for NaN
assembler->Bind(&nan_loop);
{
Label continue_loop(assembler);
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
Node* element_k = assembler->LoadFixedDoubleArrayElement(
elements, index_var.value(), MachineType::Float64());
assembler->BranchIfFloat64IsNaN(element_k, &return_true, &continue_loop);
assembler->Bind(&continue_loop);
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&nan_loop);
}
}
assembler->Bind(&if_holey_doubles);
{
Label nan_loop(assembler, &index_var), not_nan_loop(assembler, &index_var),
hole_loop(assembler, &index_var), search_notnan(assembler);
Variable search_num(assembler, MachineRepresentation::kFloat64);
assembler->GotoUnless(assembler->WordIsSmi(search_element), &search_notnan);
search_num.Bind(assembler->SmiToFloat64(search_element));
assembler->Goto(&not_nan_loop);
assembler->Bind(&search_notnan);
assembler->GotoIf(assembler->WordEqual(search_element, undefined),
&hole_loop);
assembler->GotoIf(assembler->WordNotEqual(
assembler->LoadMap(search_element), heap_number_map),
&return_false);
search_num.Bind(assembler->LoadHeapNumberValue(search_element));
assembler->BranchIfFloat64IsNaN(search_num.value(), &nan_loop,
&not_nan_loop);
// Search for HeapNumber
assembler->Bind(&not_nan_loop);
{
Label continue_loop(assembler);
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
if (kPointerSize == kDoubleSize) {
Node* element = assembler->LoadFixedDoubleArrayElement(
elements, index_var.value(), MachineType::Uint64());
Node* the_hole = assembler->Int64Constant(kHoleNanInt64);
assembler->GotoIf(assembler->Word64Equal(element, the_hole),
&continue_loop);
} else {
Node* element_upper = assembler->LoadFixedDoubleArrayElement(
elements, index_var.value(), MachineType::Uint32(),
kIeeeDoubleExponentWordOffset);
assembler->GotoIf(
assembler->Word32Equal(element_upper,
assembler->Int32Constant(kHoleNanUpper32)),
&continue_loop);
}
Node* element_k = assembler->LoadFixedDoubleArrayElement(
elements, index_var.value(), MachineType::Float64());
assembler->BranchIfFloat64Equal(element_k, search_num.value(),
&return_true, &continue_loop);
assembler->Bind(&continue_loop);
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&not_nan_loop);
}
// Search for NaN
assembler->Bind(&nan_loop);
{
Label continue_loop(assembler);
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
if (kPointerSize == kDoubleSize) {
Node* element = assembler->LoadFixedDoubleArrayElement(
elements, index_var.value(), MachineType::Uint64());
Node* the_hole = assembler->Int64Constant(kHoleNanInt64);
assembler->GotoIf(assembler->Word64Equal(element, the_hole),
&continue_loop);
} else {
Node* element_upper = assembler->LoadFixedDoubleArrayElement(
elements, index_var.value(), MachineType::Uint32(),
kIeeeDoubleExponentWordOffset);
assembler->GotoIf(
assembler->Word32Equal(element_upper,
assembler->Int32Constant(kHoleNanUpper32)),
&continue_loop);
}
Node* element_k = assembler->LoadFixedDoubleArrayElement(
elements, index_var.value(), MachineType::Float64());
assembler->BranchIfFloat64IsNaN(element_k, &return_true, &continue_loop);
assembler->Bind(&continue_loop);
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&nan_loop);
}
// Search for the Hole
assembler->Bind(&hole_loop);
{
assembler->GotoUnless(
assembler->Int32LessThan(index_var.value(), len_var.value()),
&return_false);
if (kPointerSize == kDoubleSize) {
Node* element = assembler->LoadFixedDoubleArrayElement(
elements, index_var.value(), MachineType::Uint64());
Node* the_hole = assembler->Int64Constant(kHoleNanInt64);
assembler->GotoIf(assembler->Word64Equal(element, the_hole),
&return_true);
} else {
Node* element_upper = assembler->LoadFixedDoubleArrayElement(
elements, index_var.value(), MachineType::Uint32(),
kIeeeDoubleExponentWordOffset);
assembler->GotoIf(
assembler->Word32Equal(element_upper,
assembler->Int32Constant(kHoleNanUpper32)),
&return_true);
}
index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
assembler->Goto(&hole_loop);
}
}
assembler->Bind(&return_true);
assembler->Return(assembler->BooleanConstant(true));
assembler->Bind(&return_false);
assembler->Return(assembler->BooleanConstant(false));
assembler->Bind(&call_runtime);
assembler->Return(assembler->CallRuntime(Runtime::kArrayIncludes_Slow,
context, array, search_element,
start_from));
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -191,8 +191,6 @@ namespace internal {
CPP(ArrayConcat) \ CPP(ArrayConcat) \
/* ES6 section 22.1.2.2 Array.isArray */ \ /* ES6 section 22.1.2.2 Array.isArray */ \
TFJ(ArrayIsArray, 2) \ TFJ(ArrayIsArray, 2) \
/* ES7 #sec-array.prototype.includes */ \
TFJ(ArrayIncludes, 3) \
CPP(ArrayPop) \ CPP(ArrayPop) \
CPP(ArrayPush) \ CPP(ArrayPush) \
CPP(ArrayShift) \ CPP(ArrayShift) \

View File

@ -471,216 +471,6 @@ Node* CodeStubAssembler::WordIsPositiveSmi(Node* a) {
IntPtrConstant(0)); IntPtrConstant(0));
} }
void CodeStubAssembler::BranchIfSameValueZero(Node* a, Node* b, Node* context,
Label* if_true, Label* if_false) {
Node* number_map = HeapNumberMapConstant();
Label a_isnumber(this), a_isnotnumber(this), b_isnumber(this), a_isnan(this),
float_not_equal(this);
// If register A and register B are identical, goto `if_true`
GotoIf(WordEqual(a, b), if_true);
// If either register A or B are Smis, goto `if_false`
GotoIf(Word32Or(WordIsSmi(a), WordIsSmi(b)), if_false);
// GotoIf(WordIsSmi(b), if_false);
Node* a_map = LoadMap(a);
Node* b_map = LoadMap(b);
Branch(WordEqual(a_map, number_map), &a_isnumber, &a_isnotnumber);
// If both register A and B are HeapNumbers, return true if they are equal,
// or if both are NaN
Bind(&a_isnumber);
{
Branch(WordEqual(b_map, number_map), &b_isnumber, if_false);
Bind(&b_isnumber);
Node* a_value = LoadHeapNumberValue(a);
Node* b_value = LoadHeapNumberValue(b);
BranchIfFloat64Equal(a_value, b_value, if_true, &float_not_equal);
Bind(&float_not_equal);
BranchIfFloat64IsNaN(a_value, &a_isnan, if_false);
Bind(&a_isnan);
BranchIfFloat64IsNaN(a_value, if_true, if_false);
}
Bind(&a_isnotnumber);
{
Label a_isstring(this), a_isnotstring(this);
Node* a_instance_type = LoadMapInstanceType(a_map);
Branch(Int32LessThan(a_instance_type, Int32Constant(FIRST_NONSTRING_TYPE)),
&a_isstring, &a_isnotstring);
Bind(&a_isstring);
{
Label b_isstring(this), b_isnotstring(this);
Node* b_instance_type = LoadInstanceType(b_map);
Branch(
Int32LessThan(b_instance_type, Int32Constant(FIRST_NONSTRING_TYPE)),
&b_isstring, if_false);
Bind(&b_isstring);
{
Callable callable = CodeFactory::StringEqual(isolate());
Node* result = CallStub(callable, context, a, b);
Branch(WordEqual(BooleanConstant(true), result), if_true, if_false);
}
}
Bind(&a_isnotstring);
{
// Check if {lhs} is a Simd128Value.
Label a_issimd128value(this);
Branch(Word32Equal(a_instance_type, Int32Constant(SIMD128_VALUE_TYPE)),
&a_issimd128value, if_false);
Bind(&a_issimd128value);
{
// Load the map of {rhs}.
BranchIfSimd128Equal(a, a_map, b, b_map, if_true, if_false);
}
}
}
}
void CodeStubAssembler::BranchIfSimd128Equal(Node* lhs, Node* lhs_map,
Node* rhs, Node* rhs_map,
Label* if_equal,
Label* if_notequal) {
Label if_mapsame(this), if_mapnotsame(this);
Branch(WordEqual(lhs_map, rhs_map), &if_mapsame, &if_mapnotsame);
Bind(&if_mapsame);
{
// Both {lhs} and {rhs} are Simd128Values with the same map, need special
// handling for Float32x4 because of NaN comparisons.
Label if_float32x4(this), if_notfloat32x4(this);
Node* float32x4_map = HeapConstant(factory()->float32x4_map());
Branch(WordEqual(lhs_map, float32x4_map), &if_float32x4, &if_notfloat32x4);
Bind(&if_float32x4);
{
// Both {lhs} and {rhs} are Float32x4, compare the lanes individually
// using a floating point comparison.
for (int offset = Float32x4::kValueOffset - kHeapObjectTag;
offset < Float32x4::kSize - kHeapObjectTag;
offset += sizeof(float)) {
// Load the floating point values for {lhs} and {rhs}.
Node* lhs_value =
Load(MachineType::Float32(), lhs, IntPtrConstant(offset));
Node* rhs_value =
Load(MachineType::Float32(), rhs, IntPtrConstant(offset));
// Perform a floating point comparison.
Label if_valueequal(this), if_valuenotequal(this);
Branch(Float32Equal(lhs_value, rhs_value), &if_valueequal,
&if_valuenotequal);
Bind(&if_valuenotequal);
Goto(if_notequal);
Bind(&if_valueequal);
}
// All 4 lanes match, {lhs} and {rhs} considered equal.
Goto(if_equal);
}
Bind(&if_notfloat32x4);
{
// For other Simd128Values we just perform a bitwise comparison.
for (int offset = Simd128Value::kValueOffset - kHeapObjectTag;
offset < Simd128Value::kSize - kHeapObjectTag;
offset += kPointerSize) {
// Load the word values for {lhs} and {rhs}.
Node* lhs_value =
Load(MachineType::Pointer(), lhs, IntPtrConstant(offset));
Node* rhs_value =
Load(MachineType::Pointer(), rhs, IntPtrConstant(offset));
// Perform a bitwise word-comparison.
Label if_valueequal(this), if_valuenotequal(this);
Branch(WordEqual(lhs_value, rhs_value), &if_valueequal,
&if_valuenotequal);
Bind(&if_valuenotequal);
Goto(if_notequal);
Bind(&if_valueequal);
}
// Bitwise comparison succeeded, {lhs} and {rhs} considered equal.
Goto(if_equal);
}
}
Bind(&if_mapnotsame);
Goto(if_notequal);
}
void CodeStubAssembler::BranchIfFastJSArray(Node* object, Node* context,
Label* if_true, Label* if_false) {
Node* int32_zero = Int32Constant(0);
Node* int32_one = Int32Constant(1);
Node* empty_elements = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
Variable last_map(this, MachineRepresentation::kTagged);
Label check_prototype(this);
// Bailout if Smi
GotoIf(WordIsSmi(object), if_false);
Node* map = LoadMap(object);
last_map.Bind(map);
// Bailout if instance type is not JS_ARRAY_TYPE
GotoIf(WordNotEqual(LoadMapInstanceType(map), Int32Constant(JS_ARRAY_TYPE)),
if_false);
Node* bit_field2 = LoadMapBitField2(map);
Node* elements_kind = BitFieldDecode<Map::ElementsKindBits>(bit_field2);
// Bailout if slow receiver elements
GotoIf(
Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
if_false);
STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == (FAST_SMI_ELEMENTS | 1));
STATIC_ASSERT(FAST_HOLEY_ELEMENTS == (FAST_ELEMENTS | 1));
STATIC_ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == (FAST_DOUBLE_ELEMENTS | 1));
// Check prototype chain if receiver does not have packed elements
Node* holey_elements = Word32And(elements_kind, int32_one);
Branch(Word32Equal(holey_elements, int32_zero), if_true, &check_prototype);
Bind(&check_prototype);
{
Label loop_body(this, &last_map);
Goto(&loop_body);
Bind(&loop_body);
Node* current_map = last_map.value();
Node* proto = LoadObjectField(current_map, Map::kPrototypeOffset);
// End loop
GotoIf(WordEqual(proto, NullConstant()), if_true);
// ASSERT: proto->IsHeapObject()
Node* proto_map = LoadMap(proto);
// Bailout if a Proxy, API Object, or JSValue wrapper found in prototype
// Because of this bailout, it's not necessary to check for interceptors or
// access checks on the prototype chain.
GotoIf(Int32LessThanOrEqual(LoadMapInstanceType(proto_map),
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
if_false);
// Bailout if prototype contains non-empty elements
GotoUnless(WordEqual(LoadElements(proto), empty_elements), if_false);
last_map.Bind(proto_map);
Goto(&loop_body);
}
}
Node* CodeStubAssembler::AllocateRawUnaligned(Node* size_in_bytes, Node* CodeStubAssembler::AllocateRawUnaligned(Node* size_in_bytes,
AllocationFlags flags, AllocationFlags flags,
Node* top_address, Node* top_address,

View File

@ -124,22 +124,6 @@ class CodeStubAssembler : public compiler::CodeAssembler {
void BranchIfToBooleanIsTrue(compiler::Node* value, Label* if_true, void BranchIfToBooleanIsTrue(compiler::Node* value, Label* if_true,
Label* if_false); Label* if_false);
void BranchIfSimd128Equal(compiler::Node* lhs, compiler::Node* lhs_map,
compiler::Node* rhs, compiler::Node* rhs_map,
Label* if_equal, Label* if_notequal);
void BranchIfSimd128Equal(compiler::Node* lhs, compiler::Node* rhs,
Label* if_equal, Label* if_notequal) {
BranchIfSimd128Equal(lhs, LoadMap(lhs), rhs, LoadMap(rhs), if_equal,
if_notequal);
}
void BranchIfSameValueZero(compiler::Node* a, compiler::Node* b,
compiler::Node* context, Label* if_true,
Label* if_false);
void BranchIfFastJSArray(compiler::Node* object, compiler::Node* context,
Label* if_true, Label* if_false);
// Load value from current frame by given offset in bytes. // Load value from current frame by given offset in bytes.
compiler::Node* LoadFromFrame(int offset, compiler::Node* LoadFromFrame(int offset,
MachineType rep = MachineType::AnyTagged()); MachineType rep = MachineType::AnyTagged());

View File

@ -2342,8 +2342,78 @@ void GenerateEqual_Simd128Value_HeapObject(
CodeStubAssembler* assembler, compiler::Node* lhs, compiler::Node* lhs_map, CodeStubAssembler* assembler, compiler::Node* lhs, compiler::Node* lhs_map,
compiler::Node* rhs, compiler::Node* rhs_map, compiler::Node* rhs, compiler::Node* rhs_map,
CodeStubAssembler::Label* if_equal, CodeStubAssembler::Label* if_notequal) { CodeStubAssembler::Label* if_equal, CodeStubAssembler::Label* if_notequal) {
assembler->BranchIfSimd128Equal(lhs, lhs_map, rhs, rhs_map, if_equal, typedef CodeStubAssembler::Label Label;
if_notequal); typedef compiler::Node Node;
// Check if {lhs} and {rhs} have the same map.
Label if_mapsame(assembler), if_mapnotsame(assembler);
assembler->Branch(assembler->WordEqual(lhs_map, rhs_map), &if_mapsame,
&if_mapnotsame);
assembler->Bind(&if_mapsame);
{
// Both {lhs} and {rhs} are Simd128Values with the same map, need special
// handling for Float32x4 because of NaN comparisons.
Label if_float32x4(assembler), if_notfloat32x4(assembler);
Node* float32x4_map =
assembler->HeapConstant(assembler->factory()->float32x4_map());
assembler->Branch(assembler->WordEqual(lhs_map, float32x4_map),
&if_float32x4, &if_notfloat32x4);
assembler->Bind(&if_float32x4);
{
// Both {lhs} and {rhs} are Float32x4, compare the lanes individually
// using a floating point comparison.
for (int offset = Float32x4::kValueOffset - kHeapObjectTag;
offset < Float32x4::kSize - kHeapObjectTag;
offset += sizeof(float)) {
// Load the floating point values for {lhs} and {rhs}.
Node* lhs_value = assembler->Load(MachineType::Float32(), lhs,
assembler->IntPtrConstant(offset));
Node* rhs_value = assembler->Load(MachineType::Float32(), rhs,
assembler->IntPtrConstant(offset));
// Perform a floating point comparison.
Label if_valueequal(assembler), if_valuenotequal(assembler);
assembler->Branch(assembler->Float32Equal(lhs_value, rhs_value),
&if_valueequal, &if_valuenotequal);
assembler->Bind(&if_valuenotequal);
assembler->Goto(if_notequal);
assembler->Bind(&if_valueequal);
}
// All 4 lanes match, {lhs} and {rhs} considered equal.
assembler->Goto(if_equal);
}
assembler->Bind(&if_notfloat32x4);
{
// For other Simd128Values we just perform a bitwise comparison.
for (int offset = Simd128Value::kValueOffset - kHeapObjectTag;
offset < Simd128Value::kSize - kHeapObjectTag;
offset += kPointerSize) {
// Load the word values for {lhs} and {rhs}.
Node* lhs_value = assembler->Load(MachineType::Pointer(), lhs,
assembler->IntPtrConstant(offset));
Node* rhs_value = assembler->Load(MachineType::Pointer(), rhs,
assembler->IntPtrConstant(offset));
// Perform a bitwise word-comparison.
Label if_valueequal(assembler), if_valuenotequal(assembler);
assembler->Branch(assembler->WordEqual(lhs_value, rhs_value),
&if_valueequal, &if_valuenotequal);
assembler->Bind(&if_valuenotequal);
assembler->Goto(if_notequal);
assembler->Bind(&if_valueequal);
}
// Bitwise comparison succeeded, {lhs} and {rhs} considered equal.
assembler->Goto(if_equal);
}
}
assembler->Bind(&if_mapnotsame);
assembler->Goto(if_notequal);
} }
// ES6 section 7.2.12 Abstract Equality Comparison // ES6 section 7.2.12 Abstract Equality Comparison

View File

@ -869,7 +869,7 @@ void CodeAssembler::Branch(Node* condition, CodeAssembler::Label* true_label,
} }
void CodeAssembler::Switch(Node* index, Label* default_label, void CodeAssembler::Switch(Node* index, Label* default_label,
const int32_t* case_values, Label** case_labels, int32_t* case_values, Label** case_labels,
size_t case_count) { size_t case_count) {
RawMachineLabel** labels = RawMachineLabel** labels =
new (zone()->New(sizeof(RawMachineLabel*) * case_count)) new (zone()->New(sizeof(RawMachineLabel*) * case_count))

View File

@ -246,7 +246,7 @@ class CodeAssembler {
void GotoUnless(Node* condition, Label* false_label); void GotoUnless(Node* condition, Label* false_label);
void Branch(Node* condition, Label* true_label, Label* false_label); void Branch(Node* condition, Label* true_label, Label* false_label);
void Switch(Node* index, Label* default_label, const int32_t* case_values, void Switch(Node* index, Label* default_label, int32_t* case_values,
Label** case_labels, size_t case_count); Label** case_labels, size_t case_count);
Node* Select(Node* condition, Node* true_value, Node* false_value, Node* Select(Node* condition, Node* true_value, Node* false_value,

View File

@ -85,8 +85,9 @@ void RawMachineAssembler::Branch(Node* condition, RawMachineLabel* true_val,
current_block_ = nullptr; current_block_ = nullptr;
} }
void RawMachineAssembler::Switch(Node* index, RawMachineLabel* default_label, void RawMachineAssembler::Switch(Node* index, RawMachineLabel* default_label,
const int32_t* case_values, int32_t* case_values,
RawMachineLabel** case_labels, RawMachineLabel** case_labels,
size_t case_count) { size_t case_count) {
DCHECK_NE(schedule()->end(), current_block_); DCHECK_NE(schedule()->end(), current_block_);

View File

@ -754,9 +754,8 @@ class RawMachineAssembler {
void Goto(RawMachineLabel* label); void Goto(RawMachineLabel* label);
void Branch(Node* condition, RawMachineLabel* true_val, void Branch(Node* condition, RawMachineLabel* true_val,
RawMachineLabel* false_val); RawMachineLabel* false_val);
void Switch(Node* index, RawMachineLabel* default_label, void Switch(Node* index, RawMachineLabel* default_label, int32_t* case_values,
const int32_t* case_values, RawMachineLabel** case_labels, RawMachineLabel** case_labels, size_t case_count);
size_t case_count);
void Return(Node* value); void Return(Node* value);
void Return(Node* v1, Node* v2); void Return(Node* v1, Node* v2);
void Return(Node* v1, Node* v2, Node* v3); void Return(Node* v1, Node* v2, Node* v3);

View File

@ -468,27 +468,6 @@ 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 // Base class for element handler implementations. Contains the
// the common logic for objects with different ElementsKinds. // the common logic for objects with different ElementsKinds.
// Subclasses must specialize method for which the element // Subclasses must specialize method for which the element
@ -1105,20 +1084,6 @@ class ElementsAccessorBase : public ElementsAccessor {
return Subclass::GetCapacityImpl(holder, backing_store); 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, static uint32_t GetIndexForEntryImpl(FixedArrayBase* backing_store,
uint32_t entry) { uint32_t entry) {
return entry; return entry;
@ -1455,102 +1420,6 @@ class DictionaryElementsAccessor
accumulator->AddKey(value, convert); 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();
Object* undefined = isolate->heap()->undefined_value();
// Scan for accessor properties. If accessors are present, then elements
// must be accessed in order via the slow path.
bool found = false;
for (int i = 0; i < capacity; ++i) {
Object* k = dictionary->KeyAt(i);
if (k == the_hole) continue;
if (k == undefined) continue;
uint32_t index;
if (!k->ToArrayIndex(&index) || index < start_from || index >= length) {
continue;
}
if (dictionary->DetailsAt(i).type() == ACCESSOR_CONSTANT) {
// Restart from beginning in slow path, otherwise we may observably
// access getters out of order
return false;
} else if (!found) {
Object* element_k = dictionary->ValueAt(i);
if (value->SameValueZero(element_k)) found = true;
}
}
*result = Just(found);
return true;
}
static Maybe<bool> IncludesValueImpl(Isolate* isolate,
Handle<JSObject> receiver,
Handle<Object> value,
uint32_t start_from, uint32_t length) {
DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
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);
}
}; };
@ -1911,156 +1780,6 @@ 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) {
DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
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 &&
static_cast<uint32_t>(elements_base->length()) < length) {
return Just(true);
}
if (start_from >= length) return Just(false);
length = std::min(static_cast<uint32_t>(elements_base->length()), length);
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;
}
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())
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: private:
// SpliceShrinkStep might modify the backing_store. // SpliceShrinkStep might modify the backing_store.
static void SpliceShrinkStep(Isolate* isolate, Handle<JSArray> receiver, static void SpliceShrinkStep(Isolate* isolate, Handle<JSArray> receiver,
@ -2406,9 +2125,9 @@ class FastHoleyDoubleElementsAccessor
// Super class for all external element arrays. // Super class for all external element arrays.
template <ElementsKind Kind, typename ctype> template<ElementsKind Kind>
class TypedElementsAccessor class TypedElementsAccessor
: public ElementsAccessorBase<TypedElementsAccessor<Kind, ctype>, : public ElementsAccessorBase<TypedElementsAccessor<Kind>,
ElementsKindTraits<Kind> > { ElementsKindTraits<Kind> > {
public: public:
explicit TypedElementsAccessor(const char* name) explicit TypedElementsAccessor(const char* name)
@ -2416,7 +2135,7 @@ class TypedElementsAccessor
ElementsKindTraits<Kind> >(name) {} ElementsKindTraits<Kind> >(name) {}
typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore; typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore;
typedef TypedElementsAccessor<Kind, ctype> AccessorClass; typedef TypedElementsAccessor<Kind> AccessorClass;
static inline void SetImpl(Handle<JSObject> holder, uint32_t entry, static inline void SetImpl(Handle<JSObject> holder, uint32_t entry,
Object* value) { Object* value) {
@ -2522,53 +2241,12 @@ class TypedElementsAccessor
*nof_items = count; *nof_items = count;
return Just(true); return Just(true);
} }
static Maybe<bool> IncludesValueImpl(Isolate* isolate,
Handle<JSObject> receiver,
Handle<Object> value,
uint32_t start_from, uint32_t length) {
DCHECK(JSObject::PrototypeHasNoElements(isolate, *receiver));
DisallowHeapAllocation no_gc;
BackingStore* elements = BackingStore::cast(receiver->elements());
if (value->IsUndefined(isolate) &&
length > static_cast<uint32_t>(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) \ #define FIXED_ELEMENTS_ACCESSOR(Type, type, TYPE, ctype, size) \
typedef TypedElementsAccessor<TYPE##_ELEMENTS, ctype> \ typedef TypedElementsAccessor<TYPE##_ELEMENTS > \
Fixed##Type##ElementsAccessor; Fixed##Type##ElementsAccessor;
TYPED_ARRAYS(FIXED_ELEMENTS_ACCESSOR) TYPED_ARRAYS(FIXED_ELEMENTS_ACCESSOR)
@ -2803,46 +2481,6 @@ class SloppyArgumentsElementsAccessor
isolate, object, store, convert, filter, list, nof_indices, isolate, object, store, convert, filter, list, nof_indices,
insertion_index); insertion_index);
} }
static Maybe<bool> IncludesValueImpl(Isolate* isolate,
Handle<JSObject> object,
Handle<Object> value,
uint32_t start_from, uint32_t length) {
DCHECK(JSObject::PrototypeHasNoElements(isolate, *object));
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);
}
}; };

View File

@ -154,12 +154,6 @@ class ElementsAccessor {
virtual uint32_t GetCapacity(JSObject* holder, virtual uint32_t GetCapacity(JSObject* holder,
FixedArrayBase* backing_store) = 0; FixedArrayBase* backing_store) = 0;
// Check an Object's own elements for an element (using SameValueZero
// semantics)
virtual Maybe<bool> IncludesValue(Isolate* isolate, Handle<JSObject> receiver,
Handle<Object> value, uint32_t start,
uint32_t length) = 0;
protected: protected:
friend class LookupIterator; friend class LookupIterator;

View File

@ -380,16 +380,6 @@ class Factory final {
} }
return NewNumber(static_cast<double>(value), pretenure); return NewNumber(static_cast<double>(value), pretenure);
} }
Handle<Object> NewNumberFromInt64(int64_t value,
PretenureFlag pretenure = NOT_TENURED) {
if (value <= std::numeric_limits<int32_t>::max() &&
value >= std::numeric_limits<int32_t>::min() &&
Smi::IsValid(static_cast<int32_t>(value))) {
return Handle<Object>(Smi::FromInt(static_cast<int32_t>(value)),
isolate());
}
return NewNumber(static_cast<double>(value), pretenure);
}
Handle<HeapNumber> NewHeapNumber(double value, Handle<HeapNumber> NewHeapNumber(double value,
MutableMode mode = IMMUTABLE, MutableMode mode = IMMUTABLE,
PretenureFlag pretenure = NOT_TENURED); PretenureFlag pretenure = NOT_TENURED);

View File

@ -1492,6 +1492,47 @@ function ArrayFill(value, start, end) {
} }
function InnerArrayIncludes(searchElement, fromIndex, array, length) {
if (length === 0) {
return false;
}
var n = TO_INTEGER(fromIndex);
var k;
if (n >= 0) {
k = n;
} else {
k = length + n;
if (k < 0) {
k = 0;
}
}
while (k < length) {
var elementK = array[k];
if (%SameValueZero(searchElement, elementK)) {
return true;
}
++k;
}
return false;
}
// ES2016 draft, section 22.1.3.11
function ArrayIncludes(searchElement, fromIndex) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.includes");
var array = TO_OBJECT(this);
var length = TO_LENGTH(array.length);
return InnerArrayIncludes(searchElement, fromIndex, array, length);
}
// ES6, draft 10-14-14, section 22.1.2.1 // ES6, draft 10-14-14, section 22.1.2.1
function ArrayFrom(arrayLike, mapfn, receiver) { function ArrayFrom(arrayLike, mapfn, receiver) {
var items = TO_OBJECT(arrayLike); var items = TO_OBJECT(arrayLike);
@ -1636,7 +1677,7 @@ utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
"find", getFunction("find", ArrayFind, 1), "find", getFunction("find", ArrayFind, 1),
"findIndex", getFunction("findIndex", ArrayFindIndex, 1), "findIndex", getFunction("findIndex", ArrayFindIndex, 1),
"fill", getFunction("fill", ArrayFill, 1), "fill", getFunction("fill", ArrayFill, 1),
"includes", getFunction("includes", null, 1) "includes", getFunction("includes", ArrayIncludes, 1),
]); ]);
utils.InstallGetter(GlobalArray, speciesSymbol, ArraySpecies); utils.InstallGetter(GlobalArray, speciesSymbol, ArraySpecies);
@ -1690,6 +1731,7 @@ utils.Export(function(to) {
to.InnerArrayFind = InnerArrayFind; to.InnerArrayFind = InnerArrayFind;
to.InnerArrayFindIndex = InnerArrayFindIndex; to.InnerArrayFindIndex = InnerArrayFindIndex;
to.InnerArrayForEach = InnerArrayForEach; to.InnerArrayForEach = InnerArrayForEach;
to.InnerArrayIncludes = InnerArrayIncludes;
to.InnerArrayIndexOf = InnerArrayIndexOf; to.InnerArrayIndexOf = InnerArrayIndexOf;
to.InnerArrayJoin = InnerArrayJoin; to.InnerArrayJoin = InnerArrayJoin;
to.InnerArrayLastIndexOf = InnerArrayLastIndexOf; to.InnerArrayLastIndexOf = InnerArrayLastIndexOf;

View File

@ -28,6 +28,7 @@ var InnerArrayFilter;
var InnerArrayFind; var InnerArrayFind;
var InnerArrayFindIndex; var InnerArrayFindIndex;
var InnerArrayForEach; var InnerArrayForEach;
var InnerArrayIncludes;
var InnerArrayIndexOf; var InnerArrayIndexOf;
var InnerArrayJoin; var InnerArrayJoin;
var InnerArrayLastIndexOf; var InnerArrayLastIndexOf;
@ -81,6 +82,7 @@ utils.Import(function(from) {
InnerArrayFind = from.InnerArrayFind; InnerArrayFind = from.InnerArrayFind;
InnerArrayFindIndex = from.InnerArrayFindIndex; InnerArrayFindIndex = from.InnerArrayFindIndex;
InnerArrayForEach = from.InnerArrayForEach; InnerArrayForEach = from.InnerArrayForEach;
InnerArrayIncludes = from.InnerArrayIncludes;
InnerArrayIndexOf = from.InnerArrayIndexOf; InnerArrayIndexOf = from.InnerArrayIndexOf;
InnerArrayJoin = from.InnerArrayJoin; InnerArrayJoin = from.InnerArrayJoin;
InnerArrayLastIndexOf = from.InnerArrayLastIndexOf; InnerArrayLastIndexOf = from.InnerArrayLastIndexOf;
@ -711,29 +713,7 @@ function TypedArrayIncludes(searchElement, fromIndex) {
var length = %_TypedArrayGetLength(this); var length = %_TypedArrayGetLength(this);
if (length === 0) return false; return InnerArrayIncludes(searchElement, fromIndex, this, length);
var n = TO_INTEGER(fromIndex);
var k;
if (n >= 0) {
k = n;
} else {
k = length + n;
if (k < 0) {
k = 0;
}
}
while (k < length) {
var elementK = this[k];
if (%SameValueZero(searchElement, elementK)) {
return true;
}
++k;
}
return false;
} }
%FunctionSetLength(TypedArrayIncludes, 1); %FunctionSetLength(TypedArrayIncludes, 1);

View File

@ -1144,20 +1144,6 @@ MUST_USE_RESULT MaybeHandle<FixedArray> JSReceiver::OwnPropertyKeys(
GetKeysConversion::kConvertToString); GetKeysConversion::kConvertToString);
} }
bool JSObject::PrototypeHasNoElements(Isolate* isolate, JSObject* object) {
DisallowHeapAllocation no_gc;
HeapObject* prototype = HeapObject::cast(object->map()->prototype());
HeapObject* null = isolate->heap()->null_value();
HeapObject* empty = isolate->heap()->empty_fixed_array();
while (prototype != null) {
Map* map = prototype->map();
if (map->instance_type() <= LAST_CUSTOM_ELEMENTS_RECEIVER) return false;
if (JSObject::cast(prototype)->elements() != empty) return false;
prototype = HeapObject::cast(map->prototype());
}
return true;
}
#define FIELD_ADDR(p, offset) \ #define FIELD_ADDR(p, offset) \
(reinterpret_cast<byte*>(p) + offset - kHeapObjectTag) (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)

View File

@ -2210,9 +2210,6 @@ class JSObject: public JSReceiver {
static bool UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate); static bool UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate);
static void InvalidatePrototypeChains(Map* map); static void InvalidatePrototypeChains(Map* map);
// Utility used by many Array builtins and runtime functions
static inline bool PrototypeHasNoElements(Isolate* isolate, JSObject* object);
// Alternative implementation of WeakFixedArray::NullCallback. // Alternative implementation of WeakFixedArray::NullCallback.
class PrototypeRegistryCompactionCallback { class PrototypeRegistryCompactionCallback {
public: public:

View File

@ -32,24 +32,18 @@ RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) {
} }
static void InstallCode(Isolate* isolate, Handle<JSObject> holder, static void InstallCode(Isolate* isolate, Handle<JSObject> holder,
const char* name, Handle<Code> code, int argc = -1) { const char* name, Handle<Code> code) {
Handle<String> key = isolate->factory()->InternalizeUtf8String(name); Handle<String> key = isolate->factory()->InternalizeUtf8String(name);
Handle<JSFunction> optimized = Handle<JSFunction> optimized =
isolate->factory()->NewFunctionWithoutPrototype(key, code); isolate->factory()->NewFunctionWithoutPrototype(key, code);
if (argc < 0) {
optimized->shared()->DontAdaptArguments(); optimized->shared()->DontAdaptArguments();
} else {
optimized->shared()->set_internal_formal_parameter_count(argc);
}
JSObject::AddProperty(holder, key, optimized, NONE); JSObject::AddProperty(holder, key, optimized, NONE);
} }
static void InstallBuiltin(Isolate* isolate, Handle<JSObject> holder, static void InstallBuiltin(Isolate* isolate, Handle<JSObject> holder,
const char* name, Builtins::Name builtin_name, const char* name, Builtins::Name builtin_name) {
int argc = -1) {
InstallCode(isolate, holder, name, InstallCode(isolate, holder, name,
handle(isolate->builtins()->builtin(builtin_name), isolate), handle(isolate->builtins()->builtin(builtin_name), isolate));
argc);
} }
RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) { RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) {
@ -69,7 +63,6 @@ RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) {
InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift); InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift);
InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice); InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice);
InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice); InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice);
InstallBuiltin(isolate, holder, "includes", Builtins::kArrayIncludes, 2);
return *holder; return *holder;
} }
@ -450,98 +443,5 @@ RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor) {
isolate, Object::ArraySpeciesConstructor(isolate, original_array)); isolate, Object::ArraySpeciesConstructor(isolate, original_array));
} }
// ES7 22.1.3.11 Array.prototype.includes
RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow) {
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<JSReceiver> object;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, object, Object::ToObject(isolate, handle(args[0], isolate)));
// Let len be ? ToLength(? Get(O, "length")).
int64_t len;
{
if (object->map()->instance_type() == JS_ARRAY_TYPE) {
uint32_t len32;
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 isolate->heap()->false_value();
// 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 isolate->heap()->false_value();
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<bool> result = elements->IncludesValue(isolate, obj, search_element,
static_cast<uint32_t>(index),
static_cast<uint32_t>(len));
MAYBE_RETURN(result, isolate->heap()->exception());
return *isolate->factory()->ToBoolean(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);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
Object::GetProperty(&it));
}
// If SameValueZero(searchElement, elementK) is true, return true.
if (search_element->SameValueZero(*element_k)) {
return isolate->heap()->true_value();
}
}
return isolate->heap()->false_value();
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -55,8 +55,7 @@ namespace internal {
F(GetCachedArrayIndex, 1, 1) \ F(GetCachedArrayIndex, 1, 1) \
F(FixedArrayGet, 2, 1) \ F(FixedArrayGet, 2, 1) \
F(FixedArraySet, 3, 1) \ F(FixedArraySet, 3, 1) \
F(ArraySpeciesConstructor, 1, 1) \ F(ArraySpeciesConstructor, 1, 1)
F(ArrayIncludes_Slow, 3, 1)
#define FOR_EACH_INTRINSIC_ATOMICS(F) \ #define FOR_EACH_INTRINSIC_ATOMICS(F) \
F(ThrowNotIntegerSharedTypedArrayError, 1, 1) \ F(ThrowNotIntegerSharedTypedArrayError, 1, 1) \

View File

@ -1,634 +0,0 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
// Ensure `Array.prototype.includes` functions correctly for numerous elements
// kinds, and various exotic receiver types,
// TODO(caitp): update kIterCount to a high enough number to trigger inlining,
// once inlining this builtin is supported
var kIterCount = 1;
var kTests = {
Array: {
FAST_ELEMENTS() {
var r = /foo/;
var s = new String("bar");
var p = new Proxy({}, {});
var o = {};
var array = [r, s, p];
assertTrue(%HasFastObjectElements(array));
assertFalse(%HasFastHoleyElements(array));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(array.includes(p));
assertFalse(array.includes(o));
}
},
FAST_HOLEY_ELEMENTS() {
var r = /foo/;
var p = new Proxy({}, {});
var o = {};
var array = [r, , p];
assertTrue(%HasFastObjectElements(array));
assertTrue(%HasFastHoleyElements(array));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(array.includes(p));
assertFalse(array.includes(o));
}
},
FAST_SMI_ELEMENTS() {
var array = [0, 88, 9999, 1, -5, 7];
assertTrue(%HasFastSmiElements(array));
assertFalse(%HasFastHoleyElements(array));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(array.includes(9999));
assertTrue(array.includes(-5));
assertFalse(array.includes(-5.00001));
assertFalse(array.includes(undefined));
assertFalse(array.includes(NaN));
}
},
FAST_HOLEY_SMI_ELEMENTS() {
var array = [49, , , 72, , , 67, -48];
assertTrue(%HasFastSmiElements(array));
assertTrue(%HasFastHoleyElements(array));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(array.includes(72));
assertTrue(array.includes(-48));
assertFalse(array.includes(72, 4));
assertTrue(array.includes(undefined));
assertFalse(array.includes(undefined, -2));
assertFalse(array.includes(NaN));
}
},
FAST_DOUBLE_ELEMENTS() {
var array = [7.00000001, -13000.89412, 73451.4124,
5824.48, 6.0000495, 48.3488, 44.0, 76.35, NaN, 78.4];
assertTrue(%HasFastDoubleElements(array));
assertFalse(%HasFastHoleyElements(array));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(array.includes(7.00000001));
assertFalse(array.includes(7.00000001, 2));
assertTrue(array.includes(NaN));
assertFalse(array.includes(NaN, -1));
assertTrue(array.includes(-13000.89412));
assertFalse(array.includes(-13000.89412, -2));
assertFalse(array.includes(undefined));
}
},
FAST_HOLEY_DOUBLE_ELEMENTS() {
var array = [7.00000001, -13000.89412, ,
5824.48, , 48.3488, , NaN, , 78.4];
assertTrue(%HasFastDoubleElements(array));
assertTrue(%HasFastHoleyElements(array));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(array.includes(7.00000001));
assertFalse(array.includes(7.00000001, 2));
assertTrue(array.includes(NaN));
assertFalse(array.includes(NaN, -2));
assertTrue(array.includes(-13000.89412));
assertFalse(array.includes(-13000.89412, -2));
assertTrue(array.includes(undefined, -2));
assertFalse(array.includes(undefined, -1));
}
},
DICTIONARY_ELEMENTS() {
var array = [];
Object.defineProperty(array, 4, { get() { return NaN; } });
Object.defineProperty(array, 7, { value: Function });
assertTrue(%HasDictionaryElements(array));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(array.includes(NaN));
assertFalse(array.includes(NaN, -3));
assertTrue(array.includes(Function));
assertTrue(array.includes(undefined));
assertFalse(array.includes(undefined, 7));
}
},
},
Object: {
FAST_ELEMENTS() {
var r = /foo/;
var s = new String("bar");
var p = new Proxy({}, {});
var o = {};
var object = { 0: r, 1: s, 2: p, length: 3 };
assertTrue(%HasFastObjectElements(object));
// TODO(caitp): JSObjects always seem to start with FAST_HOLEY_ELEMENTS
// assertFalse(%HasFastHoleyElements(object));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(Array.prototype.includes.call(object, p));
assertFalse(Array.prototype.includes.call(object, o));
}
},
FAST_HOLEY_ELEMENTS() {
var r = /foo/;
var p = new Proxy({}, {});
var o = {};
var object = { 0: r, 2: p, length: 3 };
assertTrue(%HasFastObjectElements(object));
assertTrue(%HasFastHoleyElements(object));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(Array.prototype.includes.call(object, p));
assertFalse(Array.prototype.includes.call(object, o));
}
},
FAST_SMI_ELEMENTS() {
var object = { 0: 0, 1: 88, 2: 9999, 3: 1, 4: -5, 5: 7, length: 6 };
// TODO(caitp): JSObjects always seem to start with FAST_HOLEY_ELEMENTS
// assertTrue(%HasFastSmiElements(object));
// assertFalse(%HasFastHoleyElements(object));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(Array.prototype.includes.call(object, 9999));
assertTrue(Array.prototype.includes.call(object, -5));
assertFalse(Array.prototype.includes.call(object, -5.00001));
assertFalse(Array.prototype.includes.call(object, undefined));
assertFalse(Array.prototype.includes.call(object, NaN));
}
},
FAST_HOLEY_SMI_ELEMENTS() {
var object = { 0: 49, 3: 72, 6: 67, 7: -48, length: 8 };
// TODO(caitp): JSObjects always seem to start with FAST_HOLEY_ELEMENTS
// assertTrue(%HasFastSmiElements(object));
// assertTrue(%HasFastHoleyElements(object));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(Array.prototype.includes.call(object, 72));
assertTrue(Array.prototype.includes.call(object, -48));
assertFalse(Array.prototype.includes.call(object, 72, 4));
assertTrue(Array.prototype.includes.call(object, undefined));
assertFalse(Array.prototype.includes.call(object, undefined, -2));
assertFalse(Array.prototype.includes.call(object, NaN));
}
},
FAST_DOUBLE_ELEMENTS() {
var object = { 0: 7.00000001, 1: -13000.89412, 2: 73451.4124,
3: 5824.48, 4: 6.0000495, 5: 48.3488, 6: 44.0, 7: 76.35,
8: NaN, 9: 78.4, length: 10 };
// TODO(caitp): JSObjects always seem to start with FAST_HOLEY_ELEMENTS
// assertTrue(%HasFastDoubleElements(object));
// assertFalse(%HasFastHoleyElements(object));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(Array.prototype.includes.call(object, 7.00000001));
assertFalse(Array.prototype.includes.call(object, 7.00000001, 2));
assertTrue(Array.prototype.includes.call(object, NaN));
assertFalse(Array.prototype.includes.call(object, NaN, -1));
assertTrue(Array.prototype.includes.call(object, -13000.89412));
assertFalse(Array.prototype.includes.call(object, -13000.89412, -2));
assertFalse(Array.prototype.includes.call(object, undefined));
}
},
FAST_HOLEY_DOUBLE_ELEMENTS() {
var object = { 0: 7.00000001, 1: -13000.89412, 3: 5824.48, 5: 48.3488,
7: NaN, 9: 78.4, length: 10 };
// TODO(caitp): JSObjects always seem to start with FAST_HOLEY_ELEMENTS
// assertTrue(%HasFastDoubleElements(object));
// assertTrue(%HasFastHoleyElements(object));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(Array.prototype.includes.call(object, 7.00000001));
assertFalse(Array.prototype.includes.call(object, 7.00000001, 2));
assertTrue(Array.prototype.includes.call(object, NaN));
assertFalse(Array.prototype.includes.call(object, NaN, -2));
assertTrue(Array.prototype.includes.call(object, -13000.89412));
assertFalse(Array.prototype.includes.call(object, -13000.89412, -2));
assertTrue(Array.prototype.includes.call(object, undefined, -2));
assertFalse(Array.prototype.includes.call(object, undefined, -1));
}
},
DICTIONARY_ELEMENTS() {
var object = { length: 8 };
Object.defineProperty(object, 4, { get() { return NaN; } });
Object.defineProperty(object, 7, { value: Function });
assertTrue(%HasDictionaryElements(object));
for (var i = 0; i < kIterCount; ++i) {
assertTrue(Array.prototype.includes.call(object, NaN));
assertFalse(Array.prototype.includes.call(object, NaN, -3));
assertTrue(Array.prototype.includes.call(object, Function));
assertTrue(Array.prototype.includes.call(object, undefined));
assertFalse(Array.prototype.includes.call(object, undefined, 7));
}
(function prototypeModifiedDuringAccessor() {
function O() {
return {
__proto__: {},
get 0() {
this.__proto__.__proto__ = {
get 1() {
this[2] = "c";
return "b";
}
};
return "a";
},
length: 3
};
}
// Switch to slow path when first accessor modifies the prototype
assertTrue(Array.prototype.includes.call(O(), "a"));
assertTrue(Array.prototype.includes.call(O(), "b"));
assertTrue(Array.prototype.includes.call(O(), "c"));
// Avoid switching to slow path due to avoiding the accessor
assertFalse(Array.prototype.includes.call(O(), "c", 2));
assertFalse(Array.prototype.includes.call(O(), "b", 1));
assertTrue(Array.prototype.includes.call(O(), undefined, 1));
});
},
},
String: {
FAST_STRING_ELEMENTS() {
for (var i = 0; i < kIterCount; ++i) {
assertTrue(Array.prototype.includes.call("froyo", "y"));
assertFalse(Array.prototype.includes.call("froyo", "y", -1));
assertTrue(Array.prototype.includes.call("froyo", "y", -2));
assertFalse(Array.prototype.includes.call("froyo", NaN));
assertFalse(Array.prototype.includes.call("froyo", undefined));
}
},
SLOW_STRING_ELEMENTS() {
var string = new String("froyo");
// Never accessible from A.p.includes as 'length' is not configurable
Object.defineProperty(string, 34, { value: NaN });
Object.defineProperty(string, 12, { get() { return "nope" } });
for (var i = 0; i < kIterCount; ++i) {
assertTrue(Array.prototype.includes.call("froyo", "y"));
assertFalse(Array.prototype.includes.call("froyo", "y", -1));
assertTrue(Array.prototype.includes.call("froyo", "y", -2));
assertFalse(Array.prototype.includes.call(string, NaN));
assertFalse(Array.prototype.includes.call(string, undefined));
assertFalse(Array.prototype.includes.call(string, "nope"));
}
},
},
Arguments: {
FAST_SLOPPY_ARGUMENTS_ELEMENTS() {
var args = (function(a, b) { return arguments; })("foo", NaN, "bar");
assertTrue(%HasSloppyArgumentsElements(args));
for (var i = 0; i < kIterCount; ++i) {
assertFalse(Array.prototype.includes.call(args, undefined));
assertTrue(Array.prototype.includes.call(args, NaN));
assertFalse(Array.prototype.includes.call(args, NaN, -1));
assertTrue(Array.prototype.includes.call(args, "bar", -1));
}
},
SLOW_SLOPPY_ARGUMENTS_ELEMENTS() {
var args = (function(a, a) { return arguments; })("foo", NaN, "bar");
Object.defineProperty(args, 3, { get() { return "silver"; } });
Object.defineProperty(args, "length", { value: 4 });
assertTrue(%HasSloppyArgumentsElements(args));
for (var i = 0; i < kIterCount; ++i) {
assertFalse(Array.prototype.includes.call(args, undefined));
assertTrue(Array.prototype.includes.call(args, NaN));
assertFalse(Array.prototype.includes.call(args, NaN, -2));
assertTrue(Array.prototype.includes.call(args, "bar", -2));
assertTrue(Array.prototype.includes.call(args, "silver", -1));
}
}
},
TypedArray: {
Int8Array() {
var array = new Int8Array([-129, 128,
NaN /* 0 */, +0 /* 0 */, -0 /* 0 */,
+Infinity /* 0 */, -Infinity /* 0 */,
255 /* -1 */, 127 /* 127 */, -255 /* 1 */]);
assertFalse(Array.prototype.includes.call(array, -129));
assertFalse(Array.prototype.includes.call(array, 128));
assertTrue(Array.prototype.includes.call(array, 0, 2));
assertTrue(Array.prototype.includes.call(array, 0, 3));
assertTrue(Array.prototype.includes.call(array, 0, 4));
assertTrue(Array.prototype.includes.call(array, 0, 5));
assertTrue(Array.prototype.includes.call(array, 0, 6));
assertFalse(Array.prototype.includes.call(array, 0, 7));
assertTrue(Array.prototype.includes.call(array, -1, 7));
assertFalse(Array.prototype.includes.call(array, -1, 8));
assertTrue(Array.prototype.includes.call(array, 127, 8));
assertFalse(Array.prototype.includes.call(array, 127, 9));
assertTrue(Array.prototype.includes.call(array, 1, 9));
},
Detached_Int8Array() {
var array = new Int8Array(10);
%ArrayBufferNeuter(array.buffer);
assertFalse(Array.prototype.includes.call(array, 0));
assertFalse(Array.prototype.includes.call(array, 0, 10));
},
Uint8Array() {
var array = new Uint8Array([-1, 256,
NaN /* 0 */, +0 /* 0 */, -0 /* 0 */,
+Infinity /* 0 */, -Infinity /* 0 */,
255 /* 255 */, 257 /* 1 */, -128 /* 128 */,
-2 /* 254 */]);
assertFalse(Array.prototype.includes.call(array, -1));
assertFalse(Array.prototype.includes.call(array, 256));
assertTrue(Array.prototype.includes.call(array, 0, 2));
assertTrue(Array.prototype.includes.call(array, 0, 3));
assertTrue(Array.prototype.includes.call(array, 0, 4));
assertTrue(Array.prototype.includes.call(array, 0, 5));
assertTrue(Array.prototype.includes.call(array, 0, 6));
assertFalse(Array.prototype.includes.call(array, 0, 7));
assertTrue(Array.prototype.includes.call(array, 255, 7));
assertFalse(Array.prototype.includes.call(array, 255, 8));
assertTrue(Array.prototype.includes.call(array, 1, 8));
assertFalse(Array.prototype.includes.call(array, 1, 9));
assertTrue(Array.prototype.includes.call(array, 128, 9));
assertFalse(Array.prototype.includes.call(array, 128, 10));
assertTrue(Array.prototype.includes.call(array, 254, 10));
},
Detached_Uint8Array() {
var array = new Uint8Array(10);
%ArrayBufferNeuter(array.buffer);
assertFalse(Array.prototype.includes.call(array, 0));
assertFalse(Array.prototype.includes.call(array, 0, 10));
},
Uint8ClampedArray() {
var array = new Uint8ClampedArray([-1 /* 0 */, NaN /* 0 */, 256 /* 255 */,
127.6 /* 128 */, 127.4 /* 127 */,
121.5 /* 122 */, 124.5 /* 124 */]);
assertFalse(Array.prototype.includes.call(array, -1));
assertFalse(Array.prototype.includes.call(array, 256));
assertTrue(Array.prototype.includes.call(array, 0));
assertTrue(Array.prototype.includes.call(array, 0, 1));
assertTrue(Array.prototype.includes.call(array, 255, 2));
assertTrue(Array.prototype.includes.call(array, 128, 3));
assertFalse(Array.prototype.includes.call(array, 128, 4));
assertTrue(Array.prototype.includes.call(array, 127, 4));
assertFalse(Array.prototype.includes.call(array, 127, 5));
assertTrue(Array.prototype.includes.call(array, 122, 5));
assertFalse(Array.prototype.includes.call(array, 122, 6));
assertTrue(Array.prototype.includes.call(array, 124, 6));
},
Detached_Uint8ClampedArray() {
var array = new Uint8ClampedArray(10);
%ArrayBufferNeuter(array.buffer);
assertFalse(Array.prototype.includes.call(array, 0));
assertFalse(Array.prototype.includes.call(array, 0, 10));
},
Int16Array() {
var array = new Int16Array([-32769, 32768,
NaN /* 0 */, Infinity /* 0 */,
-Infinity /* 0 */, -0 /* 0 */, +0 /* 0 */,
0x7FFFF /* -1 */, 30000 /* 30000 */,
300000 /* -27680 */]);
assertFalse(Array.prototype.includes.call(array, -32769));
assertFalse(Array.prototype.includes.call(array, 32768));
assertTrue(Array.prototype.includes.call(array, 0, 2));
assertTrue(Array.prototype.includes.call(array, 0, 3));
assertTrue(Array.prototype.includes.call(array, 0, 4));
assertTrue(Array.prototype.includes.call(array, 0, 5));
assertTrue(Array.prototype.includes.call(array, 0, 6));
assertFalse(Array.prototype.includes.call(array, 0, 7));
assertTrue(Array.prototype.includes.call(array, -1, 7));
assertFalse(Array.prototype.includes.call(array, -1, 8));
assertTrue(Array.prototype.includes.call(array, 30000, 8));
assertFalse(Array.prototype.includes.call(array, 30000, 9));
assertTrue(Array.prototype.includes.call(array, -27680, 9));
},
Detached_Int16Array() {
var array = new Int16Array(10);
%ArrayBufferNeuter(array.buffer);
assertFalse(Array.prototype.includes.call(array, 0));
assertFalse(Array.prototype.includes.call(array, 0, 10));
},
Uint16Array() {
var array = new Uint16Array([-1, 65536,
NaN /* 0 */, Infinity /* 0 */,
-Infinity /* 0 */, -0 /* 0 */, +0 /* 0 */,
0x7FFFF /* 65535 */, 300000 /* 37856 */,
3000000 /* 50880 */]);
assertFalse(Array.prototype.includes.call(array, -1));
assertFalse(Array.prototype.includes.call(array, 65536));
assertTrue(Array.prototype.includes.call(array, 0, 2));
assertTrue(Array.prototype.includes.call(array, 0, 3));
assertTrue(Array.prototype.includes.call(array, 0, 4));
assertTrue(Array.prototype.includes.call(array, 0, 5));
assertTrue(Array.prototype.includes.call(array, 0, 6));
assertFalse(Array.prototype.includes.call(array, 0, 7));
assertTrue(Array.prototype.includes.call(array, 65535, 7));
assertFalse(Array.prototype.includes.call(array, 65535, 8));
assertTrue(Array.prototype.includes.call(array, 37856, 8));
assertFalse(Array.prototype.includes.call(array, 37856, 9));
assertTrue(Array.prototype.includes.call(array, 50880, 9));
},
Detached_Uint16Array() {
var array = new Uint16Array(10);
%ArrayBufferNeuter(array.buffer);
assertFalse(Array.prototype.includes.call(array, 0));
assertFalse(Array.prototype.includes.call(array, 0, 10));
},
Int32Array() {
var array = new Int32Array([-2147483649, 2147483648,
NaN /* 0 */, Infinity /* 0 */,
-Infinity /* 0 */, -0 /* 0 */, +0 /* 0 */,
0x7FFFFFFFF /* -1 */, 4294968064 /* 768 */,
4294959447 /* -7849 */]);
assertFalse(Array.prototype.includes.call(array, -2147483649));
assertFalse(Array.prototype.includes.call(array, 2147483648));
assertTrue(Array.prototype.includes.call(array, 0.0, 2));
assertTrue(Array.prototype.includes.call(array, 0.0, 3));
assertTrue(Array.prototype.includes.call(array, 0, 4));
assertTrue(Array.prototype.includes.call(array, 0, 5));
assertTrue(Array.prototype.includes.call(array, 0.0, 6));
assertFalse(Array.prototype.includes.call(array, 0.0, 7));
assertTrue(Array.prototype.includes.call(array, -1, 7));
assertFalse(Array.prototype.includes.call(array, -1, 8));
assertTrue(Array.prototype.includes.call(array, 768, 8));
assertFalse(Array.prototype.includes.call(array, 768, 9));
assertTrue(Array.prototype.includes.call(array, -7849, 9));
},
Detached_Int32Array() {
var array = new Int32Array(10);
%ArrayBufferNeuter(array.buffer);
assertFalse(Array.prototype.includes.call(array, 0));
assertFalse(Array.prototype.includes.call(array, 0, 10));
},
Uint32Array() {
var array = new Uint32Array([-1, 4294967296,
NaN /* 0 */, Infinity /* 0 */,
-Infinity /* 0 */, -0 /* 0 */, +0 /* 0 */,
0x7FFFFFFFF /* 4294967295 */,
4294968064 /* 768 */,
4295079447 /* 112151 */]);
assertFalse(Array.prototype.includes.call(array, -1));
assertFalse(Array.prototype.includes.call(array, 4294967296));
assertTrue(Array.prototype.includes.call(array, 0.0, 2));
assertTrue(Array.prototype.includes.call(array, 0.0, 3));
assertTrue(Array.prototype.includes.call(array, 0, 4));
assertTrue(Array.prototype.includes.call(array, 0, 5));
assertTrue(Array.prototype.includes.call(array, 0.0, 6));
assertFalse(Array.prototype.includes.call(array, 0.0, 7));
assertTrue(Array.prototype.includes.call(array, 4294967295, 7));
assertFalse(Array.prototype.includes.call(array, 4294967295, 8));
assertTrue(Array.prototype.includes.call(array, 768, 8));
assertFalse(Array.prototype.includes.call(array, 768, 9));
assertTrue(Array.prototype.includes.call(array, 112151, 9));
},
Detached_Uint32Array() {
var array = new Uint32Array(10);
%ArrayBufferNeuter(array.buffer);
assertFalse(Array.prototype.includes.call(array, 0));
assertFalse(Array.prototype.includes.call(array, 0, 10));
},
Float32Array() {
var array = new Float32Array([-1, 4294967296,
NaN, Infinity /* 0 */,
-Infinity /* 0 */, -0 /* 0 */, +0 /* 0 */,
0x7FFFFFFFF /* 34359738368.0 */,
-4294968064 /* -4294968320.0 */,
4295079447 /* 4295079424.0 */]);
assertTrue(Array.prototype.includes.call(array, -1.0));
assertTrue(Array.prototype.includes.call(array, 4294967296));
assertTrue(Array.prototype.includes.call(array, NaN, 2));
assertTrue(Array.prototype.includes.call(array, Infinity, 3));
assertTrue(Array.prototype.includes.call(array, -Infinity, 4));
assertTrue(Array.prototype.includes.call(array, 0, 5));
assertTrue(Array.prototype.includes.call(array, 0, 6));
assertFalse(Array.prototype.includes.call(array, 0.0, 7));
assertTrue(Array.prototype.includes.call(array, 34359738368.0, 7));
assertFalse(Array.prototype.includes.call(array, 34359738368.0, 8));
assertTrue(Array.prototype.includes.call(array, -4294968320.0, 8));
assertFalse(Array.prototype.includes.call(array, -4294968320.0, 9));
assertTrue(Array.prototype.includes.call(array, 4295079424.0, 9));
},
Detached_Float32Array() {
var array = new Float32Array(10);
%ArrayBufferNeuter(array.buffer);
assertFalse(Array.prototype.includes.call(array, 0));
assertFalse(Array.prototype.includes.call(array, 0, 10));
},
Float64Array() {
var array = new Float64Array([-1, 4294967296,
NaN, Infinity /* 0 */,
-Infinity /* 0 */, -0 /* 0 */, +0 /* 0 */,
0x7FFFFFFFF /* 34359738367.0 */,
-4294968064 /* -4294968064.0 */,
4295079447 /* 4295079447.0 */]);
assertTrue(Array.prototype.includes.call(array, -1.0));
assertTrue(Array.prototype.includes.call(array, 4294967296));
assertTrue(Array.prototype.includes.call(array, NaN, 2));
assertTrue(Array.prototype.includes.call(array, Infinity, 3));
assertTrue(Array.prototype.includes.call(array, -Infinity, 4));
assertTrue(Array.prototype.includes.call(array, 0, 5));
assertTrue(Array.prototype.includes.call(array, 0, 6));
assertFalse(Array.prototype.includes.call(array, 0.0, 7));
assertTrue(Array.prototype.includes.call(array, 34359738367.0, 7));
assertFalse(Array.prototype.includes.call(array, 34359738367.0, 8));
assertTrue(Array.prototype.includes.call(array, -4294968064.0, 8));
assertFalse(Array.prototype.includes.call(array, -4294968064.0, 9));
assertTrue(Array.prototype.includes.call(array, 4295079447.0, 9));
},
Detached_Float64Array() {
var array = new Float32Array(10);
%ArrayBufferNeuter(array.buffer);
assertFalse(Array.prototype.includes.call(array, 0));
assertFalse(Array.prototype.includes.call(array, 0, 10));
},
}
};
function runSuites(suites) {
Object.keys(suites).forEach(suite => runSuite(suites[suite]));
function runSuite(suite) {
Object.keys(suite).forEach(test => suite[test]());
}
}
runSuites(kTests);