JSObject::AddProperty(to, key, constant, details.attributes());
break;
}
+ case ACCESSOR_FIELD:
+ UNREACHABLE();
case CALLBACKS: {
Handle<Name> key(descs->GetKey(i));
LookupIterator it(to, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
isolate());
}
PropertyDetails details = properties->DetailsAt(i);
+ DCHECK_EQ(DATA, details.kind());
JSObject::AddProperty(to, key, value, details.attributes());
}
}
for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
PropertyDetails details = descs->GetDetails(i);
DCHECK(details.type() == CALLBACKS); // Only accessors are expected.
- PropertyDetails d = PropertyDetails(details.attributes(), CALLBACKS, i + 1);
+ PropertyDetails d(details.attributes(), CALLBACKS, i + 1);
Handle<Name> name(descs->GetKey(i));
Handle<Object> value(descs->GetCallbacksObject(i), isolate());
Handle<PropertyCell> cell = NewPropertyCell(value);
DescriptorArray* descs = js_obj->map()->instance_descriptors();
int real_size = js_obj->map()->NumberOfOwnDescriptors();
for (int i = 0; i < real_size; i++) {
- switch (descs->GetType(i)) {
- case FIELD: {
- Representation r = descs->GetDetails(i).representation();
+ PropertyDetails details = descs->GetDetails(i);
+ switch (details.location()) {
+ case IN_OBJECT: {
+ Representation r = details.representation();
if (r.IsSmi() || r.IsDouble()) break;
- int index = descs->GetFieldIndex(i);
Name* k = descs->GetKey(i);
- if (index < js_obj->map()->inobject_properties()) {
- Object* value = js_obj->InObjectPropertyAt(index);
- if (k != heap_->hidden_string()) {
- SetPropertyReference(
- js_obj, entry,
- k, value,
- NULL,
- js_obj->GetInObjectPropertyOffset(index));
- } else {
- TagObject(value, "(hidden properties)");
- SetInternalReference(
- js_obj, entry,
- "hidden_properties", value,
- js_obj->GetInObjectPropertyOffset(index));
- }
+ FieldIndex field_index = FieldIndex::ForDescriptor(js_obj->map(), i);
+ Object* value = js_obj->RawFastPropertyAt(field_index);
+ int field_offset =
+ field_index.is_inobject() ? field_index.offset() : -1;
+
+ if (k != heap_->hidden_string()) {
+ SetDataOrAccessorPropertyReference(details.kind(), js_obj, entry, k,
+ value, NULL, field_offset);
} else {
- FieldIndex field_index =
- FieldIndex::ForDescriptor(js_obj->map(), i);
- Object* value = js_obj->RawFastPropertyAt(field_index);
- if (k != heap_->hidden_string()) {
- SetPropertyReference(js_obj, entry, k, value);
- } else {
- TagObject(value, "(hidden properties)");
- SetInternalReference(js_obj, entry, "hidden_properties", value);
- }
+ TagObject(value, "(hidden properties)");
+ SetInternalReference(js_obj, entry, "hidden_properties", value,
+ field_offset);
}
break;
}
- case CONSTANT:
- SetPropertyReference(
- js_obj, entry,
- descs->GetKey(i), descs->GetConstant(i));
- break;
- case CALLBACKS:
- ExtractAccessorPairProperty(
- js_obj, entry,
- descs->GetKey(i), descs->GetValue(i));
+ case IN_DESCRIPTOR:
+ SetDataOrAccessorPropertyReference(details.kind(), js_obj, entry,
+ descs->GetKey(i),
+ descs->GetValue(i));
break;
}
}
SetInternalReference(js_obj, entry, "hidden_properties", value);
continue;
}
- if (ExtractAccessorPairProperty(js_obj, entry, k, value)) continue;
- SetPropertyReference(js_obj, entry, Name::cast(k), value);
+ PropertyDetails details = dictionary->DetailsAt(i);
+ SetDataOrAccessorPropertyReference(details.kind(), js_obj, entry,
+ Name::cast(k), value);
}
}
}
}
-bool V8HeapExplorer::ExtractAccessorPairProperty(
- JSObject* js_obj, int entry, Object* key, Object* callback_obj) {
- if (!callback_obj->IsAccessorPair()) return false;
+void V8HeapExplorer::ExtractAccessorPairProperty(JSObject* js_obj, int entry,
+ Name* key,
+ Object* callback_obj,
+ int field_offset) {
+ if (!callback_obj->IsAccessorPair()) return;
AccessorPair* accessors = AccessorPair::cast(callback_obj);
+ SetPropertyReference(js_obj, entry, key, accessors, NULL, field_offset);
Object* getter = accessors->getter();
if (!getter->IsOddball()) {
- SetPropertyReference(js_obj, entry, Name::cast(key), getter, "get %s");
+ SetPropertyReference(js_obj, entry, key, getter, "get %s");
}
Object* setter = accessors->setter();
if (!setter->IsOddball()) {
- SetPropertyReference(js_obj, entry, Name::cast(key), setter, "set %s");
+ SetPropertyReference(js_obj, entry, key, setter, "set %s");
}
- return true;
}
}
+void V8HeapExplorer::SetDataOrAccessorPropertyReference(
+ PropertyKind kind, JSObject* parent_obj, int parent_entry,
+ Name* reference_name, Object* child_obj, const char* name_format_string,
+ int field_offset) {
+ if (kind == ACCESSOR) {
+ ExtractAccessorPairProperty(parent_obj, parent_entry, reference_name,
+ child_obj, field_offset);
+ } else {
+ SetPropertyReference(parent_obj, parent_entry, reference_name, child_obj,
+ name_format_string, field_offset);
+ }
+}
+
+
void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
int parent_entry,
Name* reference_name,
void ExtractFixedArrayReferences(int entry, FixedArray* array);
void ExtractClosureReferences(JSObject* js_obj, int entry);
void ExtractPropertyReferences(JSObject* js_obj, int entry);
- bool ExtractAccessorPairProperty(JSObject* js_obj, int entry,
- Object* key, Object* callback_obj);
+ void ExtractAccessorPairProperty(JSObject* js_obj, int entry, Name* key,
+ Object* callback_obj, int field_offset = -1);
void ExtractElementReferences(JSObject* js_obj, int entry);
void ExtractInternalReferences(JSObject* js_obj, int entry);
Object* child,
const char* name_format_string = NULL,
int field_offset = -1);
+ void SetDataOrAccessorPropertyReference(PropertyKind kind,
+ JSObject* parent_obj, int parent,
+ Name* reference_name, Object* child,
+ const char* name_format_string = NULL,
+ int field_offset = -1);
+
void SetUserGlobalReference(Object* user_global);
void SetRootGcRootsReference();
void SetGcRootsReference(VisitorSynchronization::SyncTag tag);
property_details_ = descriptors->GetDetails(number_);
}
has_property_ = true;
- switch (property_details_.type()) {
- case v8::internal::CONSTANT:
- case v8::internal::FIELD:
+ switch (property_details_.kind()) {
+ case v8::internal::DATA:
return DATA;
- case v8::internal::CALLBACKS:
+ case v8::internal::ACCESSOR:
return ACCESSOR;
}
case ACCESSOR:
os << " (field at offset " << index.property_index() << ")\n";
break;
}
+ case ACCESSOR_FIELD: {
+ FieldIndex index = FieldIndex::ForDescriptor(map(), i);
+ os << " (accessor at offset " << index.property_index() << ")\n";
+ break;
+ }
case CONSTANT:
os << Brief(descs->GetConstant(i)) << " (constant)\n";
break;
case CALLBACKS:
- os << Brief(descs->GetCallbacksObject(i)) << " (callback)\n";
+ os << Brief(descs->GetCallbacksObject(i)) << " (callbacks)\n";
break;
}
}
os << " (transition to Object.observe)";
} else {
PropertyDetails details = GetTargetDetails(key, target);
- switch (details.type()) {
- case FIELD: {
- os << " (transition to field)";
- break;
- }
- case CONSTANT:
- os << " (transition to constant " << Brief(GetTargetValue(i)) << ")";
- break;
- case CALLBACKS:
- os << " (transition to callback " << Brief(GetTargetValue(i)) << ")";
- break;
+ os << " (transition to ";
+ if (details.location() == IN_DESCRIPTOR) {
+ os << "immutable ";
+ }
+ os << (details.kind() == DATA ? "data" : "accessor");
+ if (details.location() == IN_DESCRIPTOR) {
+ os << " " << Brief(GetTargetValue(i));
}
- os << ", attrs: " << details.attributes();
+ os << "), attrs: " << details.attributes();
}
os << " -> " << Brief(target) << "\n";
}
DescriptorArray* new_descriptors = new_map->instance_descriptors();
PropertyDetails new_details = new_descriptors->GetDetails(i);
- if (old_details.attributes() != new_details.attributes() ||
- !old_details.representation().fits_into(new_details.representation())) {
+ DCHECK_EQ(old_details.kind(), new_details.kind());
+ DCHECK_EQ(old_details.attributes(), new_details.attributes());
+ if (!old_details.representation().fits_into(new_details.representation())) {
return MaybeHandle<Map>();
}
- PropertyType new_type = new_details.type();
- PropertyType old_type = old_details.type();
Object* new_value = new_descriptors->GetValue(i);
Object* old_value = old_descriptors->GetValue(i);
- switch (new_type) {
- case FIELD:
- if ((old_type == FIELD &&
- !HeapType::cast(old_value)->NowIs(HeapType::cast(new_value))) ||
- (old_type == CONSTANT &&
- !HeapType::cast(new_value)->NowContains(old_value)) ||
- (old_type == CALLBACKS &&
- !HeapType::Any()->Is(HeapType::cast(new_value)))) {
- return MaybeHandle<Map>();
+ switch (new_details.type()) {
+ case FIELD: {
+ PropertyType old_type = old_details.type();
+ if (old_type == FIELD) {
+ if (!HeapType::cast(old_value)->NowIs(HeapType::cast(new_value))) {
+ return MaybeHandle<Map>();
+ }
+ } else {
+ DCHECK(old_type == CONSTANT);
+ if (!HeapType::cast(new_value)->NowContains(old_value)) {
+ return MaybeHandle<Map>();
+ }
}
break;
+ }
+ case ACCESSOR_FIELD:
+ DCHECK(HeapType::Any()->Is(HeapType::cast(new_value)));
+ break;
case CONSTANT:
case CALLBACKS:
- if (old_type != new_type || old_value != new_value) {
+ if (old_details.location() == IN_OBJECT || old_value != new_value) {
return MaybeHandle<Map>();
}
break;
Handle<DescriptorArray> descs(map->instance_descriptors());
for (int i = 0; i < real_size; i++) {
PropertyDetails details = descs->GetDetails(i);
+ Handle<Name> key(descs->GetKey(i));
switch (details.type()) {
case CONSTANT: {
- Handle<Name> key(descs->GetKey(i));
Handle<Object> value(descs->GetConstant(i), isolate);
PropertyDetails d(details.attributes(), FIELD, i + 1);
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
case FIELD: {
- Handle<Name> key(descs->GetKey(i));
FieldIndex index = FieldIndex::ForDescriptor(*map, i);
Handle<Object> value;
if (object->IsUnboxedDoubleField(index)) {
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
+ case ACCESSOR_FIELD: {
+ FieldIndex index = FieldIndex::ForDescriptor(*map, i);
+ Handle<Object> value(object->RawFastPropertyAt(index), isolate);
+ PropertyDetails d(details.attributes(), CALLBACKS, i + 1);
+ dictionary = NameDictionary::Add(dictionary, key, value, d);
+ break;
+ }
case CALLBACKS: {
- Handle<Name> key(descs->GetKey(i));
Handle<Object> value(descs->GetCallbacksObject(i), isolate);
PropertyDetails d(details.attributes(), CALLBACKS, i + 1);
dictionary = NameDictionary::Add(dictionary, key, value, d);
value->FitsRepresentation(details.representation()));
return GetConstant(descriptor) == value;
+ case ACCESSOR_FIELD:
case CALLBACKS:
return false;
}
int descriptor = transition->LastAdded();
DCHECK(descriptors->GetKey(descriptor)->Equals(*name));
- DCHECK_EQ(CALLBACKS, descriptors->GetDetails(descriptor).type());
+ DCHECK_EQ(ACCESSOR, descriptors->GetDetails(descriptor).kind());
DCHECK_EQ(attributes, descriptors->GetDetails(descriptor).attributes());
Handle<Object> maybe_pair(descriptors->GetValue(descriptor), isolate);
enum PropertyType {
FIELD = (IN_OBJECT << 1) | DATA,
CONSTANT = (IN_DESCRIPTOR << 1) | DATA,
+ ACCESSOR_FIELD = (IN_OBJECT << 1) | ACCESSOR,
CALLBACKS = (IN_DESCRIPTOR << 1) | ACCESSOR
};
// Outputs PropertyDetails as a dictionary details.
std::ostream& operator<<(std::ostream& os, const PropertyDetails& details) {
os << "(";
- switch (details.type()) {
- case FIELD:
- os << "normal: ";
- break;
- case CALLBACKS:
- os << "callbacks: ";
- break;
- case CONSTANT:
- UNREACHABLE();
- break;
+ if (details.location() == IN_DESCRIPTOR) {
+ os << "immutable ";
}
- return os << " dictionary_index: " << details.dictionary_index()
+ os << (details.kind() == DATA ? "data" : "accessor");
+ return os << ", dictionary_index: " << details.dictionary_index()
<< ", attrs: " << details.attributes() << ")";
}
const FastPropertyDetails& details_fast) {
const PropertyDetails& details = details_fast.details;
os << "(";
- switch (details.type()) {
- case CONSTANT:
- os << "constant: p: " << details.pointer();
- break;
- case FIELD:
- os << "field: " << details.representation().Mnemonic()
- << ", field_index: " << details.field_index()
- << ", p: " << details.pointer();
- break;
- case CALLBACKS:
- os << "callbacks: p: " << details.pointer();
- break;
+ if (details.location() == IN_DESCRIPTOR) {
+ os << "immutable ";
}
- return os << ", attrs: " << details.attributes() << ")";
+ os << (details.kind() == DATA ? "data" : "accessor");
+ if (details.location() == IN_OBJECT) {
+ os << ": " << details.representation().Mnemonic()
+ << ", field_index: " << details.field_index();
+ }
+ return os << ", p: " << details.pointer()
+ << ", attrs: " << details.attributes() << ")";
}
}
+TEST(FastCaseRedefinedAccessors) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun(
+ "var obj1 = {};\n"
+ "Object.defineProperty(obj1, 'prop', { "
+ " get: function() { return 42; },\n"
+ " set: function(value) { return this.prop_ = value; },\n"
+ " configurable: true,\n"
+ " enumerable: true,\n"
+ "});\n"
+ "Object.defineProperty(obj1, 'prop', { "
+ " get: function() { return 153; },\n"
+ " set: function(value) { return this.prop_ = value; },\n"
+ " configurable: true,\n"
+ " enumerable: true,\n"
+ "});\n");
+ v8::Local<v8::Object> js_global =
+ env->Global()->GetPrototype().As<v8::Object>();
+ i::Handle<i::JSObject> js_obj1 =
+ v8::Utils::OpenHandle(*js_global->Get(v8_str("obj1")).As<v8::Object>());
+ USE(js_obj1);
+
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("fastCaseAccessors"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ CHECK_NE(NULL, global);
+ const v8::HeapGraphNode* obj1 =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
+ CHECK_NE(NULL, obj1);
+ const v8::HeapGraphNode* func;
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get prop");
+ CHECK_NE(NULL, func);
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set prop");
+ CHECK_NE(NULL, func);
+}
+
+
TEST(SlowCaseAccessors) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
}
+static void CheckPropertyDetailsFieldsConsistency(PropertyType type,
+ PropertyKind kind,
+ PropertyLocation location) {
+ int type_value = PropertyDetails::TypeField::encode(type);
+ int kind_location_value = PropertyDetails::KindField::encode(kind) |
+ PropertyDetails::LocationField::encode(location);
+ CHECK_EQ(type_value, kind_location_value);
+}
+
+
+TEST(PropertyDetailsFieldsConsistency) {
+ CheckPropertyDetailsFieldsConsistency(FIELD, DATA, IN_OBJECT);
+ CheckPropertyDetailsFieldsConsistency(CONSTANT, DATA, IN_DESCRIPTOR);
+ CheckPropertyDetailsFieldsConsistency(ACCESSOR_FIELD, ACCESSOR, IN_OBJECT);
+ CheckPropertyDetailsFieldsConsistency(CALLBACKS, ACCESSOR, IN_DESCRIPTOR);
+}
+
+
TEST(TransitionArray_SimpleFieldTransitions) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());