[cctest] adding tests for elements kind map migrations

R=mvstanton@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1368403003

Cr-Commit-Position: refs/heads/master@{#31080}
This commit is contained in:
cbruni 2015-10-02 08:14:16 -07:00 committed by Commit bot
parent 6d13610374
commit a9b84c1b2c
3 changed files with 491 additions and 11 deletions

View File

@ -122,8 +122,10 @@
'test-diy-fp.cc',
'test-double.cc',
'test-dtoa.cc',
'test-elements-kind.cc',
'test-fast-dtoa.cc',
'test-feedback-vector.cc',
'test-field-type-tracking.cc',
'test-fixed-dtoa.cc',
'test-flags.cc',
'test-func-name-inference.cc',
@ -144,7 +146,6 @@
'test-microtask-delivery.cc',
'test-mark-compact.cc',
'test-mementos.cc',
'test-migrations.cc',
'test-object-observe.cc',
'test-parsing.cc',
'test-platform.cc',

View File

@ -0,0 +1,475 @@
// Copyright 2015 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.
#include <stdlib.h>
#include <utility>
#include "test/cctest/test-api.h"
#include "src/v8.h"
#include "src/compilation-cache.h"
#include "src/execution.h"
#include "src/factory.h"
#include "src/global-handles.h"
#include "src/ic/stub-cache.h"
#include "src/objects.h"
using namespace v8::internal;
//
// Helper functions.
//
namespace {
Handle<String> MakeString(const char* str) {
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
return factory->InternalizeUtf8String(str);
}
Handle<String> MakeName(const char* str, int suffix) {
EmbeddedVector<char, 128> buffer;
SNPrintF(buffer, "%s%d", str, suffix);
return MakeString(buffer.start());
}
template <typename T, typename M>
bool EQUALS(Handle<T> left, Handle<M> right) {
if (*left == *right) return true;
return JSObject::Equals(Handle<Object>::cast(left),
Handle<Object>::cast(right))
.FromJust();
}
template <typename T, typename M>
bool EQUALS(Handle<T> left, M right) {
return EQUALS(left, handle(right));
}
template <typename T, typename M>
bool EQUALS(T left, Handle<M> right) {
return EQUALS(handle(left), right);
}
} // namespace
//
// Tests
//
TEST(JSObjectAddingProperties) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
v8::HandleScope scope(CcTest::isolate());
Handle<FixedArray> empty_fixed_array(factory->empty_fixed_array());
Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
Handle<Object> value(Smi::FromInt(42), isolate);
Handle<JSObject> object = factory->NewJSObject(function);
Handle<Map> previous_map(object->map());
CHECK_EQ(previous_map->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK(EQUALS(object->properties(), empty_fixed_array));
CHECK(EQUALS(object->elements(), empty_fixed_array));
// for the default constructor function no in-object properties are reserved
// hence adding a single property will initialize the property-array
Handle<String> name = MakeName("property", 0);
JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
.Check();
CHECK_NE(object->map(), *previous_map);
CHECK_EQ(object->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK_LE(1, object->properties()->length());
CHECK(EQUALS(object->elements(), empty_fixed_array));
}
TEST(JSObjectInObjectAddingProperties) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
v8::HandleScope scope(CcTest::isolate());
Handle<FixedArray> empty_fixed_array(factory->empty_fixed_array());
Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
int nof_inobject_properties = 10;
// force in object properties by changing the expected_nof_properties
function->shared()->set_expected_nof_properties(nof_inobject_properties);
Handle<Object> value(Smi::FromInt(42), isolate);
Handle<JSObject> object = factory->NewJSObject(function);
Handle<Map> previous_map(object->map());
CHECK_EQ(previous_map->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK(EQUALS(object->properties(), empty_fixed_array));
CHECK(EQUALS(object->elements(), empty_fixed_array));
// we have reserved space for in-object properties, hence adding up to
// |nof_inobject_properties| will not create a property store
for (int i = 0; i < nof_inobject_properties; i++) {
Handle<String> name = MakeName("property", i);
JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
.Check();
}
CHECK_NE(object->map(), *previous_map);
CHECK_EQ(object->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK(EQUALS(object->properties(), empty_fixed_array));
CHECK(EQUALS(object->elements(), empty_fixed_array));
// adding one more property will not fit in the in-object properties, thus
// creating a property store
int index = nof_inobject_properties + 1;
Handle<String> name = MakeName("property", index);
JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
.Check();
CHECK_NE(object->map(), *previous_map);
CHECK_EQ(object->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
// there must be at least 1 element in the properies store
CHECK_LE(1, object->properties()->length());
CHECK(EQUALS(object->elements(), empty_fixed_array));
}
TEST(JSObjectAddingElements) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
v8::HandleScope scope(CcTest::isolate());
Handle<String> name;
Handle<FixedArray> empty_fixed_array(factory->empty_fixed_array());
Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
Handle<Object> value(Smi::FromInt(42), isolate);
Handle<JSObject> object = factory->NewJSObject(function);
Handle<Map> previous_map(object->map());
CHECK_EQ(previous_map->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK(EQUALS(object->properties(), empty_fixed_array));
CHECK(EQUALS(object->elements(), empty_fixed_array));
// Adding an indexed element initializes the elements array
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
.Check();
// no change in elements_kind => no map transition
CHECK_EQ(object->map(), *previous_map);
CHECK_EQ(object->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK(EQUALS(object->properties(), empty_fixed_array));
CHECK_LE(1, object->elements()->length());
// Adding more consecutive elements without a change in the backing store
int non_dict_backing_store_limit = 100;
for (int i = 1; i < non_dict_backing_store_limit; i++) {
name = MakeName("", i);
JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
.Check();
}
// no change in elements_kind => no map transition
CHECK_EQ(object->map(), *previous_map);
CHECK_EQ(object->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK(EQUALS(object->properties(), empty_fixed_array));
CHECK_LE(non_dict_backing_store_limit, object->elements()->length());
// Adding an element at an very large index causes a change to
// DICTIONARY_ELEMENTS
name = MakeString("100000000");
JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
.Check();
// change in elements_kind => map transition
CHECK_NE(object->map(), *previous_map);
CHECK_EQ(object->map()->elements_kind(), DICTIONARY_ELEMENTS);
CHECK(EQUALS(object->properties(), empty_fixed_array));
CHECK_LE(non_dict_backing_store_limit, object->elements()->length());
}
TEST(JSArrayAddingProperties) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
v8::HandleScope scope(CcTest::isolate());
Handle<FixedArray> empty_fixed_array(factory->empty_fixed_array());
Handle<Object> value(Smi::FromInt(42), isolate);
Handle<JSArray> array =
factory->NewJSArray(ElementsKind::FAST_SMI_ELEMENTS, 0, 0);
Handle<Map> previous_map(array->map());
CHECK_EQ(previous_map->elements_kind(), FAST_SMI_ELEMENTS);
CHECK(EQUALS(array->properties(), empty_fixed_array));
CHECK(EQUALS(array->elements(), empty_fixed_array));
CHECK_EQ(Smi::cast(array->length())->value(), 0);
// for the default constructor function no in-object properties are reserved
// hence adding a single property will initialize the property-array
Handle<String> name = MakeName("property", 0);
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value, NONE)
.Check();
// No change in elements_kind but added property => new map
CHECK_NE(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_SMI_ELEMENTS);
CHECK_LE(1, array->properties()->length());
CHECK(EQUALS(array->elements(), empty_fixed_array));
CHECK_EQ(Smi::cast(array->length())->value(), 0);
}
TEST(JSArrayAddingElements) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
v8::HandleScope scope(CcTest::isolate());
Handle<String> name;
Handle<FixedArray> empty_fixed_array(factory->empty_fixed_array());
Handle<Object> value(Smi::FromInt(42), isolate);
Handle<JSArray> array =
factory->NewJSArray(ElementsKind::FAST_SMI_ELEMENTS, 0, 0);
Handle<Map> previous_map(array->map());
CHECK_EQ(previous_map->elements_kind(), FAST_SMI_ELEMENTS);
CHECK(EQUALS(array->properties(), empty_fixed_array));
CHECK(EQUALS(array->elements(), empty_fixed_array));
CHECK_EQ(Smi::cast(array->length())->value(), 0);
// Adding an indexed element initializes the elements array
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value, NONE)
.Check();
// no change in elements_kind => no map transition
CHECK_EQ(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_SMI_ELEMENTS);
CHECK(EQUALS(array->properties(), empty_fixed_array));
CHECK_LE(1, array->elements()->length());
CHECK_EQ(1, Smi::cast(array->length())->value());
// Adding more consecutive elements without a change in the backing store
int non_dict_backing_store_limit = 100;
for (int i = 1; i < non_dict_backing_store_limit; i++) {
name = MakeName("", i);
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value, NONE)
.Check();
}
// no change in elements_kind => no map transition
CHECK_EQ(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_SMI_ELEMENTS);
CHECK(EQUALS(array->properties(), empty_fixed_array));
CHECK_LE(non_dict_backing_store_limit, array->elements()->length());
CHECK_EQ(non_dict_backing_store_limit, Smi::cast(array->length())->value());
// Adding an element at an very large index causes a change to
// DICTIONARY_ELEMENTS
int index = 100000000;
name = MakeName("", index);
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value, NONE)
.Check();
// change in elements_kind => map transition
CHECK_NE(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), DICTIONARY_ELEMENTS);
CHECK(EQUALS(array->properties(), empty_fixed_array));
CHECK_LE(non_dict_backing_store_limit, array->elements()->length());
CHECK_LE(array->elements()->length(), index);
CHECK_EQ(index + 1, Smi::cast(array->length())->value());
}
TEST(JSArrayAddingElementsGeneralizingiFastSmiElements) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
v8::HandleScope scope(CcTest::isolate());
Handle<String> name;
Handle<Object> value_smi(Smi::FromInt(42), isolate);
Handle<Object> value_string(MakeString("value"));
Handle<Object> value_double = factory->NewNumber(3.1415);
Handle<JSArray> array =
factory->NewJSArray(ElementsKind::FAST_SMI_ELEMENTS, 0, 0);
Handle<Map> previous_map(array->map());
CHECK_EQ(previous_map->elements_kind(), FAST_SMI_ELEMENTS);
CHECK_EQ(Smi::cast(array->length())->value(), 0);
// `array[0] = smi_value` doesn't change the elements_kind
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
NONE)
.Check();
// no change in elements_kind => no map transition
CHECK_EQ(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_SMI_ELEMENTS);
CHECK_EQ(1, Smi::cast(array->length())->value());
// `delete array[0]` does not alter length, but changes the elments_kind
name = MakeString("0");
JSReceiver::DeletePropertyOrElement(array, name).Check();
CHECK_NE(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_SMI_ELEMENTS);
CHECK_EQ(1, Smi::cast(array->length())->value());
previous_map = handle(array->map());
// add a couple of elements again
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
NONE)
.Check();
name = MakeString("1");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
NONE)
.Check();
CHECK_EQ(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_SMI_ELEMENTS);
CHECK_EQ(2, Smi::cast(array->length())->value());
// Adding a string to the array changes from FAST_HOLEY_SMI to FAST_HOLEY
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_string,
NONE)
.Check();
CHECK_NE(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK_EQ(2, Smi::cast(array->length())->value());
previous_map = handle(array->map());
// We don't transition back to FAST_SMI even if we remove the string
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
NONE)
.Check();
CHECK_EQ(array->map(), *previous_map);
// Adding a double doesn't change the map either
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_double,
NONE)
.Check();
CHECK_EQ(array->map(), *previous_map);
}
TEST(JSArrayAddingElementsGeneralizingFastElements) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
v8::HandleScope scope(CcTest::isolate());
Handle<String> name;
Handle<Object> value_smi(Smi::FromInt(42), isolate);
Handle<Object> value_string(MakeString("value"));
Handle<JSArray> array =
factory->NewJSArray(ElementsKind::FAST_ELEMENTS, 0, 0);
Handle<Map> previous_map(array->map());
CHECK_EQ(previous_map->elements_kind(), FAST_ELEMENTS);
CHECK_EQ(Smi::cast(array->length())->value(), 0);
// `array[0] = smi_value` doesn't change the elements_kind
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
NONE)
.Check();
// no change in elements_kind => no map transition
CHECK_EQ(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_ELEMENTS);
CHECK_EQ(1, Smi::cast(array->length())->value());
// `delete array[0]` does not alter length, but changes the elments_kind
name = MakeString("0");
JSReceiver::DeletePropertyOrElement(array, name).Check();
CHECK_NE(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK_EQ(1, Smi::cast(array->length())->value());
previous_map = handle(array->map());
// add a couple of elements, elements_kind stays HOLEY
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_string,
NONE)
.Check();
name = MakeString("1");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
NONE)
.Check();
CHECK_EQ(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK_EQ(2, Smi::cast(array->length())->value());
}
TEST(JSArrayAddingElementsGeneralizingiFastDoubleElements) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
v8::HandleScope scope(CcTest::isolate());
Handle<String> name;
Handle<Object> value_smi(Smi::FromInt(42), isolate);
Handle<Object> value_string(MakeString("value"));
Handle<Object> value_double = factory->NewNumber(3.1415);
Handle<JSArray> array =
factory->NewJSArray(ElementsKind::FAST_SMI_ELEMENTS, 0, 0);
Handle<Map> previous_map(array->map());
// `array[0] = value_double` changes |elements_kind| to FAST_DOUBLE_ELEMENTS
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_double,
NONE)
.Check();
CHECK_NE(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_DOUBLE_ELEMENTS);
CHECK_EQ(1, Smi::cast(array->length())->value());
previous_map = handle(array->map());
// `array[1] = value_smi` doesn't alter the |elements_kind|
name = MakeString("1");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
NONE)
.Check();
CHECK_EQ(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_DOUBLE_ELEMENTS);
CHECK_EQ(2, Smi::cast(array->length())->value());
// `delete array[0]` does not alter length, but changes the elments_kind
name = MakeString("0");
JSReceiver::DeletePropertyOrElement(array, name).Check();
CHECK_NE(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_DOUBLE_ELEMENTS);
CHECK_EQ(2, Smi::cast(array->length())->value());
previous_map = handle(array->map());
// filling the hole `array[0] = value_smi` again doesn't transition back
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_double,
NONE)
.Check();
CHECK_EQ(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_DOUBLE_ELEMENTS);
CHECK_EQ(2, Smi::cast(array->length())->value());
// Adding a string to the array changes to elements_kind FAST_ELEMENTS
name = MakeString("1");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_string,
NONE)
.Check();
CHECK_NE(array->map(), *previous_map);
CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
CHECK_EQ(2, Smi::cast(array->length())->value());
previous_map = handle(array->map());
// Adding a double doesn't change the map
name = MakeString("0");
JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_double,
NONE)
.Check();
CHECK_EQ(array->map(), *previous_map);
}

View File

@ -5,6 +5,8 @@
#include <stdlib.h>
#include <utility>
#include "test/cctest/test-api.h"
#include "src/v8.h"
#include "src/compilation-cache.h"
@ -13,7 +15,6 @@
#include "src/global-handles.h"
#include "src/ic/stub-cache.h"
#include "src/macro-assembler.h"
#include "test/cctest/cctest.h"
using namespace v8::internal;
@ -286,7 +287,8 @@ class Expectations {
Handle<String> name = MakeName("prop", property_index);
return Map::CopyWithField(map, name, heap_type, attributes, representation,
INSERT_TRANSITION).ToHandleChecked();
INSERT_TRANSITION)
.ToHandleChecked();
}
Handle<Map> AddDataConstant(Handle<Map> map, PropertyAttributes attributes,
@ -297,7 +299,8 @@ class Expectations {
Handle<String> name = MakeName("prop", property_index);
return Map::CopyWithConstant(map, name, value, attributes,
INSERT_TRANSITION).ToHandleChecked();
INSERT_TRANSITION)
.ToHandleChecked();
}
Handle<Map> TransitionToDataField(Handle<Map> map,
@ -1583,7 +1586,8 @@ TEST(ReconfigurePropertySplitMapTransitionsOverflow) {
CHECK(TransitionArray::CanHaveMoreTransitions(map2));
Handle<String> name = MakeName("foo", i);
Map::CopyWithField(map2, name, any_type, NONE, Representation::Smi(),
INSERT_TRANSITION).ToHandleChecked();
INSERT_TRANSITION)
.ToHandleChecked();
}
CHECK(!TransitionArray::CanHaveMoreTransitions(map2));
@ -1751,8 +1755,8 @@ TEST(ElementsKindTransitionFromMapNotOwningDescriptor) {
// ownership.
CHECK(map->owns_descriptors());
Map::CopyWithField(map, MakeString("foo"), any_type, NONE,
Representation::Smi(),
INSERT_TRANSITION).ToHandleChecked();
Representation::Smi(), INSERT_TRANSITION)
.ToHandleChecked();
CHECK(!map->owns_descriptors());
return Map::CopyAsElementsKind(map, DICTIONARY_ELEMENTS,
@ -1809,8 +1813,8 @@ TEST(ForObservedTransitionFromMapNotOwningDescriptor) {
// ownership.
CHECK(map->owns_descriptors());
Map::CopyWithField(map, MakeString("foo"), any_type, NONE,
Representation::Smi(),
INSERT_TRANSITION).ToHandleChecked();
Representation::Smi(), INSERT_TRANSITION)
.ToHandleChecked();
CHECK(!map->owns_descriptors());
return Map::CopyForObserved(map);
@ -1886,8 +1890,8 @@ TEST(PrototypeTransitionFromMapNotOwningDescriptor) {
// ownership.
CHECK(map->owns_descriptors());
Map::CopyWithField(map, MakeString("foo"), any_type, NONE,
Representation::Smi(),
INSERT_TRANSITION).ToHandleChecked();
Representation::Smi(), INSERT_TRANSITION)
.ToHandleChecked();
CHECK(!map->owns_descriptors());
return Map::TransitionToPrototype(map, prototype_, REGULAR_PROTOTYPE);