HObjectAccess::ForMapAndOffset(initial_map,
JSObject::kElementsOffset),
empty_fixed_array);
- if (initial_map->inobject_properties() != 0) {
- HConstant* undefined = graph()->GetConstantUndefined();
- for (int i = 0; i < initial_map->inobject_properties(); i++) {
- int property_offset = initial_map->GetInObjectPropertyOffset(i);
- Add<HStoreNamedField>(receiver,
- HObjectAccess::ForMapAndOffset(initial_map, property_offset),
- undefined);
- }
- }
+ BuildInitializeInobjectProperties(receiver, initial_map);
}
// Replace the constructor function with a newly allocated receiver using
}
+void HOptimizedGraphBuilder::BuildInitializeInobjectProperties(
+ HValue* receiver, Handle<Map> initial_map) {
+ if (initial_map->inobject_properties() != 0) {
+ HConstant* undefined = graph()->GetConstantUndefined();
+ for (int i = 0; i < initial_map->inobject_properties(); i++) {
+ int property_offset = initial_map->GetInObjectPropertyOffset(i);
+ Add<HStoreNamedField>(receiver, HObjectAccess::ForMapAndOffset(
+ initial_map, property_offset),
+ undefined);
+ }
+ }
+}
+
+
HValue* HGraphBuilder::BuildAllocateEmptyArrayBuffer(HValue* byte_length) {
HAllocate* result =
BuildAllocate(Add<HConstant>(JSArrayBuffer::kSizeWithInternalFields),
Handle<JSObject> boilerplate_object,
AllocationSiteUsageContext* site_context) {
NoObservableSideEffectsScope no_effects(this);
- InstanceType instance_type = boilerplate_object->map()->instance_type();
+ Handle<Map> initial_map(boilerplate_object->map());
+ InstanceType instance_type = initial_map->instance_type();
DCHECK(instance_type == JS_ARRAY_TYPE || instance_type == JS_OBJECT_TYPE);
HType type = instance_type == JS_ARRAY_TYPE
? HType::JSArray() : HType::JSObject();
- HValue* object_size_constant = Add<HConstant>(
- boilerplate_object->map()->instance_size());
+ HValue* object_size_constant = Add<HConstant>(initial_map->instance_size());
PretenureFlag pretenure_flag = NOT_TENURED;
Handle<AllocationSite> current_site(*site_context->current(), isolate());
BuildEmitObjectHeader(boilerplate_object, object);
+ // Similarly to the elements pointer, there is no guarantee that all
+ // property allocations can get folded, so pre-initialize all in-object
+ // properties to a safe value.
+ BuildInitializeInobjectProperties(object, initial_map);
+
Handle<FixedArrayBase> elements(boilerplate_object->elements());
int elements_size = (elements->length() > 0 &&
elements->map() != isolate()->heap()->fixed_cow_array_map()) ?
}
// Copy in-object properties.
- if (boilerplate_object->map()->NumberOfFields() != 0 ||
- boilerplate_object->map()->unused_property_fields() > 0) {
+ if (initial_map->NumberOfFields() != 0 ||
+ initial_map->unused_property_fields() > 0) {
BuildEmitInObjectProperties(boilerplate_object, object, site_context,
pretenure_flag);
}
Handle<Map> new_map = Map::ReconfigureProperty(
map, 0, kData, NONE, Representation::None(), none_type, FORCE_FIELD);
- // |map| did not change.
+ // |map| did not change except marked unstable.
CHECK(!map->is_deprecated());
- CHECK(map->is_stable());
+ CHECK(!map->is_stable());
CHECK(expectations.Check(*map));
expectations.SetDataField(0, NONE, Representation::None(), none_type);
CHECK(expectations.Check(*new_map));
if (is_detached_map) {
+ CHECK(!map->is_stable());
CHECK(map->is_deprecated());
CHECK_NE(*map, *new_map);
CHECK_EQ(expected_field_type_dependency && !field_owner->is_deprecated(),
info.dependencies()->HasAborted());
} else if (expected_deprecation) {
+ CHECK(!map->is_stable());
CHECK(map->is_deprecated());
CHECK(field_owner->is_deprecated());
CHECK_NE(*map, *new_map);
} else {
CHECK(!field_owner->is_deprecated());
+ CHECK(map->is_stable()); // Map did not change, must be left stable.
CHECK_EQ(*map, *new_map);
CHECK_EQ(expected_field_type_dependency, info.dependencies()->HasAborted());
to_type, expected_representation, expected_type, expected_deprecation,
expected_field_type_dependency);
}
+
+ // Check that reconfiguration to the very same field works correctly.
+ Representation representation = from_representation;
+ Handle<HeapType> type = from_type;
+ TestGeneralizeRepresentation(-1, 2, representation, type, representation,
+ type, representation, type, false, false);
}
}
expectations.SetDataField(i, Representation::Double(), any_type);
+ CHECK(!map->is_stable());
CHECK(map->is_deprecated());
CHECK_NE(*map, *new_map);
CHECK(i == 0 || maps[i - 1]->is_deprecated());
Handle<Map> new_map =
Map::ReconfigureExistingProperty(map2, kSplitProp, kData, NONE);
- // |map2| should be left unchanged.
+ // |map2| should be left unchanged but marked unstable.
+ CHECK(!map2->is_stable());
CHECK(!map2->is_deprecated());
CHECK_NE(*map2, *new_map);
CHECK(expectations2.Check(*map2));
Handle<Map> new_map =
Map::ReconfigureExistingProperty(map2, kSplitProp, kData, NONE);
- // |map2| should be left unchanged.
+ // |map2| should be left unchanged but marked unstable.
+ CHECK(!map2->is_stable());
CHECK(!map2->is_deprecated());
CHECK_NE(*map2, *new_map);
CHECK(expectations2.Check(*map2));
struct CheckSameMap {
void Check(Handle<Map> map, Handle<Map> new_map,
const Expectations& expectations) {
+ // |map| was not reconfigured, therefore it should stay stable.
+ CHECK(map->is_stable());
CHECK(!map->is_deprecated());
CHECK_EQ(*map, *new_map);
};
+// Checks that given |map| is NOT deprecated and matches expectations.
+// |new_map| is unrelated to |map|.
+struct CheckUnrelated {
+ void Check(Handle<Map> map, Handle<Map> new_map,
+ const Expectations& expectations) {
+ CHECK(!map->is_deprecated());
+ CHECK_NE(*map, *new_map);
+ CHECK(expectations.Check(*map));
+
+ CHECK(new_map->is_stable());
+ CHECK(!new_map->is_deprecated());
+ }
+};
+
+
// Checks that given |map| is NOT deprecated, and |new_map| is a result of
// copy-generalize-all-representations.
struct CheckCopyGeneralizeAllRepresentations {
Handle<Map> new_map =
Map::ReconfigureExistingProperty(map2, kSplitProp, kData, NONE);
- // |map2| should be left unchanged.
+ // |map2| should be left unchanged but marked unstable.
+ CHECK(!map2->is_stable());
CHECK(!map2->is_deprecated());
CHECK_NE(*map2, *new_map);
CHECK(expectations2.Check(*map2));
}
+TEST(ReconfigureDataFieldAttribute_DataConstantToAccConstantAfterTargetMap) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ struct TestConfig {
+ Handle<JSFunction> js_func_;
+ Handle<AccessorPair> pair_;
+ TestConfig() {
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+ js_func_ = factory->NewFunction(factory->empty_string());
+ pair_ = CreateAccessorPair(true, true);
+ }
+
+ Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
+ Handle<Map> map) {
+ CHECK(branch_id == 1 || branch_id == 2);
+ if (branch_id == 1) {
+ return expectations.AddDataConstant(map, NONE, js_func_);
+ } else {
+ return expectations.AddAccessorConstant(map, NONE, pair_);
+ }
+ }
+
+ void UpdateExpectations(int property_index, Expectations& expectations) {}
+ };
+
+ TestConfig config;
+ // These are completely separate branches in transition tree.
+ CheckUnrelated checker;
+ TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
+}
+
+
TEST(ReconfigureDataFieldAttribute_SameAccessorConstantAfterTargetMap) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
return expectations.AddAccessorConstant(map, NONE, pair_);
}
- bool UpdateExpectations(int property_index, Expectations& expectations) {
+ void UpdateExpectations(int property_index, Expectations& expectations) {
// Two branches are "compatible" so the |map1| should NOT be deprecated.
- return false;
}
};
}
+TEST(ReconfigureDataFieldAttribute_AccConstantToDataFieldAfterTargetMap) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ struct TestConfig {
+ Handle<AccessorPair> pair_;
+ TestConfig() { pair_ = CreateAccessorPair(true, true); }
+
+ Handle<Map> AddPropertyAtBranch(int branch_id, Expectations& expectations,
+ Handle<Map> map) {
+ CHECK(branch_id == 1 || branch_id == 2);
+ if (branch_id == 1) {
+ return expectations.AddAccessorConstant(map, NONE, pair_);
+ } else {
+ Isolate* isolate = CcTest::i_isolate();
+ Handle<HeapType> any_type = HeapType::Any(isolate);
+ return expectations.AddDataField(map, NONE, Representation::Smi(),
+ any_type);
+ }
+ }
+
+ void UpdateExpectations(int property_index, Expectations& expectations) {}
+ };
+
+ TestConfig config;
+ // These are completely separate branches in transition tree.
+ CheckUnrelated checker;
+ TestReconfigureProperty_CustomPropertyAfterTargetMap(config, checker);
+}
+
+
////////////////////////////////////////////////////////////////////////////////
// A set of tests checking split map deprecation.
//
// transition tree.
CHECK(map->is_deprecated());
CHECK(!split_map->is_deprecated());
+ CHECK(map2->is_stable());
CHECK(!map2->is_deprecated());
// Fill in transition tree of |map2| so that it can't have more transitions.
Handle<Map> updated_map = Map::Update(map1);
CHECK_EQ(*map2, *updated_map);
- expectations2.SetDataField(descriptor_, representation_, heap_type_);
+ expectations2.SetDataField(descriptor_, attributes_, representation_,
+ heap_type_);
CHECK(expectations2.Check(*map2));
}
};