Commit 399f36b5 authored by franzih's avatar franzih Committed by Commit bot

[runtime] Allocate space for computed property names.

Allocate space in the backing store for computed property names.

The property backing store was pre-allocated for the constant
properties up to the first non-constant (computed name) property.
To use lowering for storing data properties in literals
with computed property names effectively, a fast store is needed, i.e.,
available space in the property backing store for properties
with computed names.

backing_store_size is the number of all properties (including
computed names, but without __proto__)
that is calculated in the ast and passed to the runtime function that allocates
the property backing store. backing_store_size and
constant_properties constitute a BoilerplateDescription.

backing_store_size might be slightly too high because computed names
can evaluate to the same name, but that should be a rare
case so over-allocating is OK.

If a property is __proto__, we don't store it as a regular
property, because the map changes. Keep track of
has_seen_proto in the parser to calculate the
backing store size correctly.

BUG=v8:5625

Review-Url: https://codereview.chromium.org/2632503003
Cr-Commit-Position: refs/heads/master@{#42576}
parent 24c050e8
......@@ -582,9 +582,9 @@ void ObjectLiteral::InitDepthAndFlags() {
void ObjectLiteral::BuildConstantProperties(Isolate* isolate) {
if (!constant_properties_.is_null()) return;
// Allocate a fixed array to hold all the constant properties.
Handle<FixedArray> constant_properties =
isolate->factory()->NewFixedArray(boilerplate_properties_ * 2, TENURED);
Handle<BoilerplateDescription> constant_properties =
isolate->factory()->NewBoilerplateDescription(
boilerplate_properties_, properties()->length(), has_seen_proto());
int position = 0;
for (int i = 0; i < properties()->length(); i++) {
......
......@@ -1402,6 +1402,9 @@ class ObjectLiteral final : public MaterializedLiteral {
bool has_rest_property() const {
return HasRestPropertyField::decode(bit_field_);
}
bool has_seen_proto() const {
return HasSeenProtoPropertyField::decode(bit_field_);
}
// Decide if a property should be in the object boilerplate.
static bool IsBoilerplateProperty(Property* property);
......@@ -1473,7 +1476,7 @@ class ObjectLiteral final : public MaterializedLiteral {
friend class AstNodeFactory;
ObjectLiteral(ZoneList<Property*>* properties, int literal_index,
uint32_t boilerplate_properties, int pos,
uint32_t boilerplate_properties, bool has_seen_proto, int pos,
bool has_rest_property)
: MaterializedLiteral(literal_index, pos, kObjectLiteral),
boilerplate_properties_(boilerplate_properties),
......@@ -1481,7 +1484,8 @@ class ObjectLiteral final : public MaterializedLiteral {
bit_field_ |= FastElementsField::encode(false) |
HasElementsField::encode(false) |
MayStoreDoublesField::encode(false) |
HasRestPropertyField::encode(has_rest_property);
HasRestPropertyField::encode(has_rest_property) |
HasSeenProtoPropertyField::encode(has_seen_proto);
}
static int parent_num_ids() { return MaterializedLiteral::num_ids(); }
......@@ -1499,6 +1503,8 @@ class ObjectLiteral final : public MaterializedLiteral {
: public BitField<bool, HasElementsField::kNext, 1> {};
class HasRestPropertyField
: public BitField<bool, MayStoreDoublesField::kNext, 1> {};
class HasSeenProtoPropertyField
: public BitField<bool, HasRestPropertyField::kNext, 1> {};
};
......@@ -3337,10 +3343,11 @@ class AstNodeFactory final BASE_EMBEDDED {
ObjectLiteral* NewObjectLiteral(
ZoneList<ObjectLiteral::Property*>* properties, int literal_index,
uint32_t boilerplate_properties, int pos, bool has_rest_property) {
uint32_t boilerplate_properties, bool has_seen_proto, int pos,
bool has_rest_property) {
return new (zone_)
ObjectLiteral(properties, literal_index, boilerplate_properties, pos,
has_rest_property);
ObjectLiteral(properties, literal_index, boilerplate_properties,
has_seen_proto, pos, has_rest_property);
}
ObjectLiteral::Property* NewObjectLiteralProperty(
......
......@@ -391,7 +391,7 @@ bool AccessInfoFactory::ComputePropertyAccessInfo(
// Don't search on the prototype when storing in literals
if (access_mode == AccessMode::kStoreInLiteral) {
return false;
return LookupTransition(receiver_map, name, holder, access_info);
}
// Don't lookup private symbols on the prototype chain.
......
......@@ -1137,7 +1137,8 @@ JSNativeContextSpecialization::BuildPropertyAccess(
value = effect = graph()->NewNode(simplified()->LoadField(field_access),
storage, effect, control);
} else {
DCHECK_EQ(AccessMode::kStore, access_mode);
DCHECK(access_mode == AccessMode::kStore ||
access_mode == AccessMode::kStoreInLiteral);
switch (field_representation) {
case MachineRepresentation::kFloat64: {
value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
......
......@@ -185,6 +185,31 @@ Handle<FixedArray> Factory::NewUninitializedFixedArray(int size) {
FixedArray);
}
Handle<BoilerplateDescription> Factory::NewBoilerplateDescription(
int boilerplate, int all_properties, bool has_seen_proto) {
DCHECK_GE(all_properties, boilerplate);
int backing_store_size = all_properties - (has_seen_proto ? 1 : 0);
DCHECK_GE(backing_store_size, 0);
bool has_different_size_backing_store = boilerplate != backing_store_size;
// Space for name and value for every boilerplate property.
int size = 2 * boilerplate;
if (has_different_size_backing_store) {
// An extra entry for the backing store size.
size++;
}
Handle<BoilerplateDescription> description =
Handle<BoilerplateDescription>::cast(NewFixedArray(size, TENURED));
if (has_different_size_backing_store) {
DCHECK((boilerplate != all_properties) || has_seen_proto);
description->set_backing_store_size(isolate(), backing_store_size);
}
return description;
}
Handle<FixedArrayBase> Factory::NewFixedDoubleArray(int size,
PretenureFlag pretenure) {
......
......@@ -48,6 +48,12 @@ class V8_EXPORT_PRIVATE Factory final {
// Allocates an uninitialized fixed array. It must be filled by the caller.
Handle<FixedArray> NewUninitializedFixedArray(int size);
// Allocates a fixed array for name-value pairs of boilerplate properties and
// calculates the number of properties we need to store in the backing store.
Handle<BoilerplateDescription> NewBoilerplateDescription(int boilerplate,
int all_properties,
bool has_seen_proto);
// Allocate a new uninitialized fixed double array.
// The function returns a pre-allocated empty fixed array for capacity = 0,
// so the return type must be the general fixed array class.
......
......@@ -196,6 +196,8 @@ bool HeapObject::IsFixedArray() const {
instance_type == TRANSITION_ARRAY_TYPE;
}
bool HeapObject::IsBoilerplateDescription() const { return IsFixedArray(); }
// External objects are not extensible, so the map check is enough.
bool HeapObject::IsExternal() const {
return map() == GetHeap()->external_map();
......@@ -615,6 +617,7 @@ bool Object::IsMinusZero() const {
CAST_ACCESSOR(AbstractCode)
CAST_ACCESSOR(ArrayList)
CAST_ACCESSOR(BoilerplateDescription)
CAST_ACCESSOR(Bool16x8)
CAST_ACCESSOR(Bool32x4)
CAST_ACCESSOR(Bool8x16)
......
......@@ -9835,6 +9835,45 @@ void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
}
}
Object* BoilerplateDescription::name(int index) const {
// get() already checks for out of bounds access, but we do not want to allow
// access to the last element, if it is the number of properties.
DCHECK_NE(size(), index);
return get(2 * index);
}
Object* BoilerplateDescription::value(int index) const {
return get(2 * index + 1);
}
int BoilerplateDescription::size() const {
DCHECK_EQ(0, (length() - (this->has_number_of_properties() ? 1 : 0)) % 2);
// Rounding is intended.
return length() / 2;
}
int BoilerplateDescription::backing_store_size() const {
if (has_number_of_properties()) {
// If present, the last entry contains the number of properties.
return Smi::cast(this->get(length() - 1))->value();
}
// If the number is not given explicitly, we assume there are no
// properties with computed names.
return size();
}
void BoilerplateDescription::set_backing_store_size(Isolate* isolate,
int backing_store_size) {
DCHECK(has_number_of_properties());
DCHECK_NE(size(), backing_store_size);
Handle<Object> backing_store_size_obj =
isolate->factory()->NewNumberFromInt(backing_store_size);
set(length() - 1, *backing_store_size_obj);
}
bool BoilerplateDescription::has_number_of_properties() const {
return length() % 2 != 0;
}
#ifdef DEBUG
bool FixedArray::IsEqualTo(FixedArray* other) {
......
......@@ -1058,6 +1058,7 @@ template <class C> inline bool Is(Object* obj);
V(DependentCode) \
V(HandlerTable) \
V(FixedArray) \
V(BoilerplateDescription) \
V(FixedDoubleArray) \
V(WeakFixedArray) \
V(ArrayList) \
......@@ -2914,6 +2915,29 @@ class FixedArray: public FixedArrayBase {
DISALLOW_IMPLICIT_CONSTRUCTORS(FixedArray);
};
// BoilerplateDescription is a list of properties consisting of name value
// pairs. In addition to the properties, it provides the projected number
// of properties in the backing store. This number includes properties with
// computed names that are not
// in the list.
class BoilerplateDescription : public FixedArray {
public:
Object* name(int index) const;
Object* value(int index) const;
// The number of boilerplate properties.
int size() const;
// Number of boilerplate properties and properties with computed names.
int backing_store_size() const;
void set_backing_store_size(Isolate* isolate, int backing_store_size);
DECLARE_CAST(BoilerplateDescription)
private:
bool has_number_of_properties() const;
};
// FixedDoubleArray describes fixed-sized arrays with element type double.
class FixedDoubleArray: public FixedArrayBase {
......@@ -6345,7 +6369,6 @@ class Map: public HeapObject {
static const int kInstanceTypeAndBitFieldOffset =
kInstanceAttributesOffset + 0;
static const int kBitField2Offset = kInstanceAttributesOffset + 2;
static const int kUnusedPropertyFieldsByte = 3;
static const int kUnusedPropertyFieldsOffset = kInstanceAttributesOffset + 3;
STATIC_ASSERT(kInstanceTypeAndBitFieldOffset ==
......
......@@ -2552,6 +2552,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral(
typename Types::ObjectPropertyList properties =
impl()->NewObjectPropertyList(4);
int number_of_boilerplate_properties = 0;
bool has_seen_proto = false;
bool has_computed_names = false;
bool has_rest_property = false;
ObjectLiteralChecker checker(this);
......@@ -2574,10 +2576,14 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral(
has_rest_property = true;
}
// Count CONSTANT or COMPUTED properties to maintain the enumeration order.
if (!has_computed_names && impl()->IsBoilerplateProperty(property)) {
if (!impl()->IsBoilerplateProperty(property)) {
has_seen_proto = true;
} else if (!has_computed_names) {
// Count CONSTANT or COMPUTED properties to maintain the enumeration
// order.
number_of_boilerplate_properties++;
}
properties->Add(property, zone());
if (peek() != Token::RBRACE) {
......@@ -2593,8 +2599,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral(
int literal_index = function_state_->NextMaterializedLiteralIndex();
return factory()->NewObjectLiteral(properties, literal_index,
number_of_boilerplate_properties, pos,
has_rest_property);
number_of_boilerplate_properties,
has_seen_proto, pos, has_rest_property);
}
template <typename Impl>
......
......@@ -595,7 +595,8 @@ class PreParserFactory {
}
PreParserExpression NewObjectLiteral(PreParserExpressionList properties,
int literal_index,
int boilerplate_properties, int pos,
int boilerplate_properties,
bool has_seen_proto, int pos,
bool has_rest_property) {
return PreParserExpression::ObjectLiteral(properties.variables_);
}
......
......@@ -15,13 +15,13 @@ namespace v8 {
namespace internal {
static Handle<Map> ComputeObjectLiteralMap(
Handle<Context> context, Handle<FixedArray> constant_properties,
Handle<Context> context,
Handle<BoilerplateDescription> boilerplate_description,
bool* is_result_from_cache) {
int properties_length = constant_properties->length();
int number_of_properties = properties_length / 2;
int number_of_properties = boilerplate_description->backing_store_size();
for (int p = 0; p != properties_length; p += 2) {
Object* key = constant_properties->get(p);
for (int index = 0; index < boilerplate_description->size(); index++) {
Object* key = boilerplate_description->name(index);
uint32_t element_index = 0;
if (key->ToArrayIndex(&element_index)) {
// An index key does not require space in the property backing store.
......@@ -35,11 +35,12 @@ static Handle<Map> ComputeObjectLiteralMap(
MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate(
Isolate* isolate, Handle<LiteralsArray> literals,
Handle<FixedArray> constant_properties);
Handle<BoilerplateDescription> boilerplate_description);
MUST_USE_RESULT static MaybeHandle<Object> CreateObjectLiteralBoilerplate(
Isolate* isolate, Handle<LiteralsArray> literals,
Handle<FixedArray> constant_properties, bool should_have_fast_elements) {
Handle<BoilerplateDescription> boilerplate_description,
bool should_have_fast_elements) {
Handle<Context> context = isolate->native_context();
// In case we have function literals, we want the object to be in
......@@ -47,7 +48,7 @@ MUST_USE_RESULT static MaybeHandle<Object> CreateObjectLiteralBoilerplate(
// maps with constant functions can't be shared if the functions are
// not the same (which is the common case).
bool is_result_from_cache = false;
Handle<Map> map = ComputeObjectLiteralMap(context, constant_properties,
Handle<Map> map = ComputeObjectLiteralMap(context, boilerplate_description,
&is_result_from_cache);
PretenureFlag pretenure_flag =
......@@ -60,26 +61,27 @@ MUST_USE_RESULT static MaybeHandle<Object> CreateObjectLiteralBoilerplate(
if (!should_have_fast_elements) JSObject::NormalizeElements(boilerplate);
// Add the constant properties to the boilerplate.
int length = constant_properties->length();
int length = boilerplate_description->size();
bool should_transform =
!is_result_from_cache && boilerplate->HasFastProperties();
bool should_normalize = should_transform;
if (should_normalize) {
// TODO(verwaest): We might not want to ever normalize here.
JSObject::NormalizeProperties(boilerplate, KEEP_INOBJECT_PROPERTIES,
length / 2, "Boilerplate");
JSObject::NormalizeProperties(boilerplate, KEEP_INOBJECT_PROPERTIES, length,
"Boilerplate");
}
// TODO(verwaest): Support tracking representations in the boilerplate.
for (int index = 0; index < length; index += 2) {
Handle<Object> key(constant_properties->get(index + 0), isolate);
Handle<Object> value(constant_properties->get(index + 1), isolate);
if (value->IsFixedArray()) {
// The value contains the constant_properties of a
for (int index = 0; index < length; index++) {
Handle<Object> key(boilerplate_description->name(index), isolate);
Handle<Object> value(boilerplate_description->value(index), isolate);
if (value->IsBoilerplateDescription()) {
// The value contains the boilerplate properties of a
// simple object or array literal.
Handle<FixedArray> array = Handle<FixedArray>::cast(value);
Handle<BoilerplateDescription> boilerplate =
Handle<BoilerplateDescription>::cast(value);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value, CreateLiteralBoilerplate(isolate, literals, array),
Object);
isolate, value,
CreateLiteralBoilerplate(isolate, literals, boilerplate), Object);
}
MaybeHandle<Object> maybe_result;
uint32_t element_index = 0;
......@@ -161,15 +163,16 @@ static MaybeHandle<Object> CreateArrayLiteralBoilerplate(
copied_elements_values = fixed_array_values_copy;
FOR_WITH_HANDLE_SCOPE(
isolate, int, i = 0, i, i < fixed_array_values->length(), i++, {
if (fixed_array_values->get(i)->IsFixedArray()) {
// The value contains the constant_properties of a
if (fixed_array_values->get(i)->IsBoilerplateDescription()) {
// The value contains the boilerplate properties of a
// simple object or array literal.
Handle<FixedArray> fa(
FixedArray::cast(fixed_array_values->get(i)));
Handle<BoilerplateDescription> boilerplate(
BoilerplateDescription::cast(fixed_array_values->get(i)));
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
CreateLiteralBoilerplate(isolate, literals, fa), Object);
CreateLiteralBoilerplate(isolate, literals, boilerplate),
Object);
fixed_array_values_copy->set(i, *result);
}
});
......@@ -184,15 +187,17 @@ static MaybeHandle<Object> CreateArrayLiteralBoilerplate(
MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate(
Isolate* isolate, Handle<LiteralsArray> literals,
Handle<FixedArray> array) {
Handle<BoilerplateDescription> array) {
Handle<HeapObject> elements = CompileTimeValue::GetElements(array);
switch (CompileTimeValue::GetLiteralType(array)) {
case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS: {
Handle<FixedArray> props = Handle<FixedArray>::cast(elements);
Handle<BoilerplateDescription> props =
Handle<BoilerplateDescription>::cast(elements);
return CreateObjectLiteralBoilerplate(isolate, literals, props, true);
}
case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS: {
Handle<FixedArray> props = Handle<FixedArray>::cast(elements);
Handle<BoilerplateDescription> props =
Handle<BoilerplateDescription>::cast(elements);
return CreateObjectLiteralBoilerplate(isolate, literals, props, false);
}
case CompileTimeValue::ARRAY_LITERAL: {
......@@ -231,7 +236,8 @@ RUNTIME_FUNCTION(Runtime_CreateObjectLiteral) {
DCHECK_EQ(4, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, closure, 0);
CONVERT_SMI_ARG_CHECKED(literals_index, 1);
CONVERT_ARG_HANDLE_CHECKED(FixedArray, constant_properties, 2);
CONVERT_ARG_HANDLE_CHECKED(BoilerplateDescription, boilerplate_description,
2);
CONVERT_SMI_ARG_CHECKED(flags, 3);
Handle<LiteralsArray> literals(closure->literals(), isolate);
bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0;
......@@ -248,7 +254,8 @@ RUNTIME_FUNCTION(Runtime_CreateObjectLiteral) {
Handle<Object> raw_boilerplate;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, raw_boilerplate,
CreateObjectLiteralBoilerplate(isolate, literals, constant_properties,
CreateObjectLiteralBoilerplate(isolate, literals,
boilerplate_description,
should_have_fast_elements));
boilerplate = Handle<JSObject>::cast(raw_boilerplate);
......
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