Commit a669705f authored by Frank Emrich's avatar Frank Emrich Committed by Commit Bot

[dict-proto] Add support for ordered property dicts, pt.3

This CL adds partial support for objects whose slow mode dictionaries
are OrderedNameDictionaries. This is the case for all slow mode objects
if V8_DICT_MODE_PROTOTYPES is enabled.

In particular, this part contains the remaining fixes to runtime code,
except for the class templating logic, which follows in a later CL.

Bug: v8:7569
Change-Id: Ib4d08d7d352125709ca916dfc75018dabf71b0cd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2540549Reviewed-by: 's avatarDominik Inführ <dinfuehr@chromium.org>
Reviewed-by: 's avatarPeter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Frank Emrich <emrich@google.com>
Cr-Commit-Position: refs/heads/master@{#71275}
parent dc45361e
......@@ -6596,27 +6596,27 @@ Local<v8::Object> v8::Object::New(Isolate* isolate) {
return Utils::ToLocal(obj);
}
Local<v8::Object> v8::Object::New(Isolate* isolate,
Local<Value> prototype_or_null,
namespace {
// TODO(v8:7569): This is a workaround for the Handle vs MaybeHandle difference
// in the return types of the different Add functions:
// OrderedNameDictionary::Add returns MaybeHandle, NameDictionary::Add returns
// Handle.
template <typename T>
i::Handle<T> ToHandle(i::Handle<T> h) {
return h;
}
template <typename T>
i::Handle<T> ToHandle(i::MaybeHandle<T> h) {
return h.ToHandleChecked();
}
template <typename Dictionary>
void AddPropertiesAndElementsToObject(i::Isolate* i_isolate,
i::Handle<Dictionary>& properties,
i::Handle<i::FixedArrayBase>& elements,
Local<Name>* names, Local<Value>* values,
size_t length) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::Object> proto = Utils::OpenHandle(*prototype_or_null);
if (!Utils::ApiCheck(proto->IsNull() || proto->IsJSReceiver(),
"v8::Object::New", "prototype must be null or object")) {
return Local<v8::Object>();
}
LOG_API(i_isolate, Object, New);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
// We assume that this API is mostly used to create objects with named
// properties, and so we default to creating a properties backing store
// large enough to hold all of them, while we start with no elements
// (see http://bit.ly/v8-fast-object-create-cpp for the motivation).
i::Handle<i::NameDictionary> properties =
i::NameDictionary::New(i_isolate, static_cast<int>(length));
i::Handle<i::FixedArrayBase> elements =
i_isolate->factory()->empty_fixed_array();
for (size_t i = 0; i < length; ++i) {
i::Handle<i::Name> name = Utils::OpenHandle(*names[i]);
i::Handle<i::Object> value = Utils::OpenHandle(*values[i]);
......@@ -6641,18 +6641,58 @@ Local<v8::Object> v8::Object::New(Isolate* isolate,
i::InternalIndex const entry = properties->FindEntry(i_isolate, name);
if (entry.is_not_found()) {
// Add the {name}/{value} pair as a new entry.
properties = i::NameDictionary::Add(i_isolate, properties, name, value,
i::PropertyDetails::Empty());
properties = ToHandle(Dictionary::Add(
i_isolate, properties, name, value, i::PropertyDetails::Empty()));
} else {
// Overwrite the {entry} with the {value}.
properties->ValueAtPut(entry, *value);
}
}
}
}
} // namespace
Local<v8::Object> v8::Object::New(Isolate* isolate,
Local<Value> prototype_or_null,
Local<Name>* names, Local<Value>* values,
size_t length) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::Object> proto = Utils::OpenHandle(*prototype_or_null);
if (!Utils::ApiCheck(proto->IsNull() || proto->IsJSReceiver(),
"v8::Object::New", "prototype must be null or object")) {
return Local<v8::Object>();
}
LOG_API(i_isolate, Object, New);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::Handle<i::FixedArrayBase> elements =
i_isolate->factory()->empty_fixed_array();
// We assume that this API is mostly used to create objects with named
// properties, and so we default to creating a properties backing store
// large enough to hold all of them, while we start with no elements
// (see http://bit.ly/v8-fast-object-create-cpp for the motivation).
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
i::Handle<i::OrderedNameDictionary> properties =
i::OrderedNameDictionary::Allocate(i_isolate, static_cast<int>(length))
.ToHandleChecked();
AddPropertiesAndElementsToObject(i_isolate, properties, elements, names,
values, length);
i::Handle<i::JSObject> obj =
i_isolate->factory()->NewSlowJSObjectWithPropertiesAndElements(
i::Handle<i::HeapObject>::cast(proto), properties, elements);
return Utils::ToLocal(obj);
} else {
i::Handle<i::NameDictionary> properties =
i::NameDictionary::New(i_isolate, static_cast<int>(length));
AddPropertiesAndElementsToObject(i_isolate, properties, elements, names,
values, length);
i::Handle<i::JSObject> obj =
i_isolate->factory()->NewSlowJSObjectWithPropertiesAndElements(
i::Handle<i::HeapObject>::cast(proto), properties, elements);
return Utils::ToLocal(obj);
}
}
Local<v8::Value> v8::NumberObject::New(Isolate* isolate, double value) {
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "src/codegen/assembler-inl.h"
#include "src/common/globals.h"
#include "src/date/date.h"
#include "src/diagnostics/disasm.h"
#include "src/diagnostics/disassembler.h"
......@@ -1641,6 +1642,11 @@ void JSObject::IncrementSpillStatistics(Isolate* isolate,
info->number_of_slow_used_properties_ += dict.NumberOfElements();
info->number_of_slow_unused_properties_ +=
dict.Capacity() - dict.NumberOfElements();
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
OrderedNameDictionary dict = property_dictionary_ordered();
info->number_of_slow_used_properties_ += dict.NumberOfElements();
info->number_of_slow_unused_properties_ +=
dict.Capacity() - dict.NumberOfElements();
} else {
NameDictionary dict = property_dictionary();
info->number_of_slow_used_properties_ += dict.NumberOfElements();
......
......@@ -2147,8 +2147,13 @@ Handle<JSObject> Factory::NewSlowJSObjectFromMap(
Handle<Map> map, int capacity, AllocationType allocation,
Handle<AllocationSite> allocation_site) {
DCHECK(map->is_dictionary_map());
Handle<NameDictionary> object_properties =
NameDictionary::New(isolate(), capacity);
Handle<HeapObject> object_properties;
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
object_properties =
OrderedNameDictionary::Allocate(isolate(), capacity).ToHandleChecked();
} else {
object_properties = NameDictionary::New(isolate(), capacity);
}
Handle<JSObject> js_object =
NewJSObjectFromMap(map, allocation, allocation_site);
js_object->set_raw_properties_or_hash(*object_properties);
......@@ -2156,8 +2161,12 @@ Handle<JSObject> Factory::NewSlowJSObjectFromMap(
}
Handle<JSObject> Factory::NewSlowJSObjectWithPropertiesAndElements(
Handle<HeapObject> prototype, Handle<NameDictionary> properties,
Handle<HeapObject> prototype, Handle<HeapObject> properties,
Handle<FixedArrayBase> elements) {
DCHECK_IMPLIES(V8_DICT_MODE_PROTOTYPES_BOOL,
properties->IsOrderedNameDictionary());
DCHECK_IMPLIES(!V8_DICT_MODE_PROTOTYPES_BOOL, properties->IsNameDictionary());
Handle<Map> object_map = isolate()->slow_object_with_object_prototype_map();
if (object_map->prototype() != *prototype) {
object_map = Map::TransitionToPrototype(isolate(), object_map, prototype);
......
......@@ -512,7 +512,7 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
// fast elements, or a NumberDictionary, in which case the resulting
// object will have dictionary elements.
Handle<JSObject> NewSlowJSObjectWithPropertiesAndElements(
Handle<HeapObject> prototype, Handle<NameDictionary> properties,
Handle<HeapObject> prototype, Handle<HeapObject> properties,
Handle<FixedArrayBase> elements);
// JS arrays are pretenured when allocated by the parser.
......
......@@ -21,6 +21,7 @@
#include "src/extensions/ignition-statistics-extension.h"
#include "src/extensions/statistics-extension.h"
#include "src/extensions/trigger-failure-extension.h"
#include "src/objects/objects.h"
#ifdef ENABLE_VTUNE_TRACEMARK
#include "src/extensions/vtunedomain-support-extension.h"
#endif // ENABLE_VTUNE_TRACEMARK
......@@ -5170,6 +5171,29 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
if (details.kind() != kData) continue;
JSObject::AddProperty(isolate(), to, key, value, details.attributes());
}
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
// Copy all keys and values in enumeration order.
Handle<OrderedNameDictionary> properties = Handle<OrderedNameDictionary>(
from->property_dictionary_ordered(), isolate());
ReadOnlyRoots roots(isolate());
for (InternalIndex entry : properties->IterateEntries()) {
Object raw_key;
if (!properties->ToKey(roots, entry, &raw_key)) continue;
DCHECK(raw_key.IsName());
Handle<Name> key(Name::cast(raw_key), isolate());
// If the property is already there we skip it.
if (PropertyAlreadyExists(isolate(), to, key)) continue;
// Set the property.
Handle<Object> value =
Handle<Object>(properties->ValueAt(entry), isolate());
DCHECK(!value->IsCell());
DCHECK(!value->IsTheHole(isolate()));
PropertyDetails details = properties->DetailsAt(entry);
DCHECK_EQ(kData, details.kind());
JSObject::AddProperty(isolate(), to, key, value, details.attributes());
}
} else {
// Copy all keys and values in enumeration order.
Handle<NameDictionary> properties =
......
......@@ -4,6 +4,7 @@
#include "src/objects/js-regexp.h"
#include "src/common/globals.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/regexp/regexp.h"
......@@ -135,7 +136,12 @@ Handle<JSRegExpResultIndices> JSRegExpResultIndices::BuildIndices(
// their corresponding capture indices.
Handle<FixedArray> names(Handle<FixedArray>::cast(maybe_names));
int num_names = names->length() >> 1;
Handle<NameDictionary> group_names = NameDictionary::New(isolate, num_names);
Handle<HeapObject> group_names;
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
group_names = isolate->factory()->NewOrderedNameDictionary(num_names);
} else {
group_names = isolate->factory()->NewNameDictionary(num_names);
}
for (int i = 0; i < num_names; i++) {
int base_offset = i * 2;
int name_offset = base_offset;
......@@ -147,8 +153,17 @@ Handle<JSRegExpResultIndices> JSRegExpResultIndices::BuildIndices(
if (!capture_indices->IsUndefined(isolate)) {
capture_indices = Handle<JSArray>::cast(capture_indices);
}
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
group_names =
OrderedNameDictionary::Add(
isolate, Handle<OrderedNameDictionary>::cast(group_names), name,
capture_indices, PropertyDetails::Empty())
.ToHandleChecked();
} else {
group_names = NameDictionary::Add(
isolate, group_names, name, capture_indices, PropertyDetails::Empty());
isolate, Handle<NameDictionary>::cast(group_names), name,
capture_indices, PropertyDetails::Empty());
}
}
// Convert group_names to a JSObject and store at the groups property of the
......
......@@ -830,7 +830,7 @@ void CommonCopyEnumKeysTo(Isolate* isolate, Handle<Dictionary> dictionary,
continue;
} else {
if (Dictionary::kIsOrderedDictionaryType) {
storage->set(properties, dictionary->ValueAt(i));
storage->set(properties, dictionary->NameAt(i));
} else {
// If the dictionary does not store elements in enumeration order,
// we need to sort it afterwards in CopyEnumKeysTo. To enable this we
......
......@@ -1365,6 +1365,17 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject js_obj,
PropertyDetails details = cell.property_details();
SetDataOrAccessorPropertyReference(details.kind(), entry, name, value);
}
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
OrderedNameDictionary dictionary = js_obj.property_dictionary_ordered();
ReadOnlyRoots roots(isolate);
for (InternalIndex i : dictionary.IterateEntries()) {
Object k = dictionary.KeyAt(i);
if (!dictionary.IsKey(roots, k)) continue;
Object value = dictionary.ValueAt(i);
PropertyDetails details = dictionary.DetailsAt(i);
SetDataOrAccessorPropertyReference(details.kind(), entry, Name::cast(k),
value);
}
} else {
NameDictionary dictionary = js_obj.property_dictionary();
ReadOnlyRoots roots(isolate);
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "src/ast/ast.h"
#include "src/common/globals.h"
#include "src/execution/arguments-inl.h"
#include "src/execution/isolate-inl.h"
#include "src/logging/counters.h"
......@@ -133,7 +134,21 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk(
}
}
} else {
Handle<NameDictionary> dict(copy->property_dictionary(isolate), isolate);
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
Handle<OrderedNameDictionary> dict(
copy->property_dictionary_ordered(isolate), isolate);
for (InternalIndex i : dict->IterateEntries()) {
Object raw = dict->ValueAt(i);
if (!raw.IsJSObject(isolate)) continue;
DCHECK(dict->KeyAt(i).IsName());
Handle<JSObject> value(JSObject::cast(raw), isolate);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value, VisitElementOrProperty(copy, value), JSObject);
if (copying) dict->ValueAtPut(i, *value);
}
} else {
Handle<NameDictionary> dict(copy->property_dictionary(isolate),
isolate);
for (InternalIndex i : dict->IterateEntries()) {
Object raw = dict->ValueAt(isolate, i);
if (!raw.IsJSObject(isolate)) continue;
......@@ -144,6 +159,7 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk(
if (copying) dict->ValueAtPut(i, *value);
}
}
}
// Assume non-arrays don't end up having elements.
if (copy->elements(isolate).length() == 0) return copy;
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "src/ast/prettyprinter.h"
#include "src/common/globals.h"
#include "src/common/message-template.h"
#include "src/debug/debug.h"
#include "src/execution/arguments-inl.h"
......@@ -365,11 +366,21 @@ RUNTIME_FUNCTION(Runtime_AddDictionaryProperty) {
DCHECK(name->IsUniqueName());
Handle<NameDictionary> dictionary(receiver->property_dictionary(), isolate);
PropertyDetails property_details(kData, NONE, PropertyCellType::kNoCell);
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
Handle<OrderedNameDictionary> dictionary(
receiver->property_dictionary_ordered(), isolate);
dictionary = OrderedNameDictionary::Add(isolate, dictionary, name, value,
property_details)
.ToHandleChecked();
receiver->SetProperties(*dictionary);
} else {
Handle<NameDictionary> dictionary(receiver->property_dictionary(), isolate);
dictionary =
NameDictionary::Add(isolate, dictionary, name, value, property_details);
receiver->SetProperties(*dictionary);
}
return *value;
}
......@@ -636,6 +647,15 @@ RUNTIME_FUNCTION(Runtime_GetProperty) {
}
} else if (!holder->HasFastProperties()) {
// Attempt dictionary lookup.
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
OrderedNameDictionary dictionary =
holder->property_dictionary_ordered();
InternalIndex entry = dictionary.FindEntry(isolate, *key);
if (entry.is_found() &&
(dictionary.DetailsAt(entry).kind() == kData)) {
return dictionary.ValueAt(entry);
}
} else {
NameDictionary dictionary = holder->property_dictionary();
InternalIndex entry = dictionary.FindEntry(isolate, key);
if ((entry.is_found()) &&
......@@ -643,6 +663,7 @@ RUNTIME_FUNCTION(Runtime_GetProperty) {
return dictionary.ValueAt(entry);
}
}
}
} else if (key_obj->IsSmi()) {
// JSObject without a name key. If the key is a Smi, check for a
// definite out-of-bounds access to elements, which is a strong indicator
......@@ -760,10 +781,19 @@ RUNTIME_FUNCTION(Runtime_ShrinkPropertyDictionary) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
Handle<OrderedNameDictionary> dictionary(
receiver->property_dictionary_ordered(), isolate);
Handle<OrderedNameDictionary> new_properties =
OrderedNameDictionary::Shrink(isolate, dictionary);
receiver->SetProperties(*new_properties);
} else {
Handle<NameDictionary> dictionary(receiver->property_dictionary(), isolate);
Handle<NameDictionary> new_properties =
NameDictionary::Shrink(isolate, dictionary);
receiver->SetProperties(*new_properties);
}
return Smi::zero();
}
......
......@@ -28,6 +28,30 @@ int AddToSetAndGetHash(Isolate* isolate, Handle<JSObject> obj,
return Smi::ToInt(obj->GetHash());
}
int GetPropertyDictionaryHash(Handle<JSObject> obj) {
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
return obj->property_dictionary_ordered().Hash();
} else {
return obj->property_dictionary().Hash();
}
}
int GetPropertyDictionaryLength(Handle<JSObject> obj) {
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
return obj->property_dictionary_ordered().length();
} else {
return obj->property_dictionary().length();
}
}
void CheckIsDictionaryModeObject(Handle<JSObject> obj) {
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
CHECK(obj->raw_properties_or_hash().IsOrderedNameDictionary());
} else {
CHECK(obj->raw_properties_or_hash().IsNameDictionary());
}
}
void CheckFastObject(Handle<JSObject> obj, int hash) {
CHECK(obj->HasFastProperties());
CHECK(obj->raw_properties_or_hash().IsPropertyArray());
......@@ -37,9 +61,9 @@ void CheckFastObject(Handle<JSObject> obj, int hash) {
void CheckDictionaryObject(Handle<JSObject> obj, int hash) {
CHECK(!obj->HasFastProperties());
CHECK(obj->raw_properties_or_hash().IsNameDictionary());
CheckIsDictionaryModeObject(obj);
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
CHECK_EQ(hash, obj->property_dictionary().Hash());
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
}
TEST(AddHashCodeToFastObjectWithoutProperties) {
......@@ -98,7 +122,8 @@ TEST(AddHashCodeToSlowObject) {
CHECK(obj->HasFastProperties());
JSObject::NormalizeProperties(isolate, obj, CLEAR_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CHECK(obj->raw_properties_or_hash().IsNameDictionary());
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CheckDictionaryObject(obj, hash);
......@@ -181,14 +206,14 @@ TEST(TransitionSlowToSlow) {
Handle<JSObject> obj = GetGlobal<JSObject>("x");
JSObject::NormalizeProperties(isolate, obj, CLEAR_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CHECK(obj->raw_properties_or_hash().IsNameDictionary());
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CHECK_EQ(hash, obj->property_dictionary().Hash());
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
int length = obj->property_dictionary().length();
int length = GetPropertyDictionaryLength(obj);
CompileRun("for(var i = 0; i < 10; i++) { x['f'+i] = i };");
CHECK(obj->property_dictionary().length() > length);
CHECK(GetPropertyDictionaryLength(obj) > length);
CheckDictionaryObject(obj, hash);
}
......@@ -201,10 +226,10 @@ TEST(TransitionSlowToFastWithoutProperties) {
isolate->factory()->NewJSObject(isolate->object_function());
JSObject::NormalizeProperties(isolate, obj, CLEAR_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CHECK(obj->raw_properties_or_hash().IsNameDictionary());
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CHECK_EQ(hash, obj->property_dictionary().Hash());
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
JSObject::MigrateSlowToFast(obj, 0, "cctest/test-hashcode");
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
......@@ -221,10 +246,10 @@ TEST(TransitionSlowToFastWithPropertyArray) {
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK(obj->raw_properties_or_hash().IsNameDictionary());
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CHECK_EQ(hash, obj->property_dictionary().Hash());
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
JSObject::MigrateSlowToFast(obj, 0, "cctest/test-hashcode");
CheckFastObject(obj, hash);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment