The current growth strategy for growable arrays allocates a backing array of size 2 at (empty) creation and doubles the size whenever the capacity is insufficient while adding elements.

I collected statistics for the sizes and capacities of growable arrays which are promoted to old-space or survive an old-space gc when running dart2js and Fasta. For these applications, the vast majority of arrays stay empty. More than half of the total object size of promoted backing arrays is backing for empty growable arrays.

Furthermore, since the overhead for an array is 3 words (header, type parameters and length), and object sizes are rounded up to an even number of words, we waste one word for all even-sized arrays.

This CL changes the growth strategy so that empty growable arrays are created with a shared, zero-sized array as backing, avoiding the allocation of a backing array if no elements are added. When the array needs to grow, it starts out at 3 and grows to double size plus one each time: 7, 15, 31, ...

A few places in the VM code need to handle these shared, zero-sized arrays specially. In particular, the Array::MakeArray function needs to allocate a new, empty array if its result is to be returned to Dart code.

Benchmarks suggest that the change improves memory usage by a few percent overall and does not significantly affect run time.

BUG=
R=erikcorry@google.com

Review-Url: https://codereview.chromium.org/2949803002 .
This commit is contained in:
Aske Simon Christensen 2017-06-22 10:51:53 +02:00
parent 847cd093ba
commit cec963f028
25 changed files with 126 additions and 128 deletions

View file

@ -47,10 +47,6 @@ class List<E> {
list.add(e);
}
if (growable) return list;
if (list.length == 0) {
// Avoid getting an immutable list from makeListFixedLength.
return new _List<E>(0);
}
return makeListFixedLength(list);
}

View file

@ -16,7 +16,7 @@ DEFINE_NATIVE_ENTRY(GrowableList_allocate, 2) {
const TypeArguments& type_arguments =
TypeArguments::CheckedHandle(arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Array, data, arguments->NativeArgAt(1));
if (data.Length() <= 0) {
if (data.Length() < 0) {
Exceptions::ThrowRangeError("length",
Integer::Handle(Integer::New(data.Length())),
0, // This is the limit the user sees.
@ -82,7 +82,7 @@ DEFINE_NATIVE_ENTRY(GrowableList_setData, 2) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Array, data, arguments->NativeArgAt(1));
ASSERT(data.Length() > 0);
ASSERT(data.Length() >= 0);
array.SetData(data);
return Object::null();
}
@ -91,7 +91,7 @@ DEFINE_NATIVE_ENTRY(GrowableList_setData, 2) {
DEFINE_NATIVE_ENTRY(Internal_makeListFixedLength, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(GrowableObjectArray, array,
arguments->NativeArgAt(0));
return Array::MakeArray(array);
return Array::MakeFixedLength(array, /* unique = */ true);
}

View file

@ -87,10 +87,8 @@ class _GrowableList<T> extends ListBase<T> {
return result;
}
static const int _kDefaultCapacity = 2;
factory _GrowableList(int length) {
var data = new _List((length == 0) ? _kDefaultCapacity : length);
var data = _allocateData(length);
var result = new _GrowableList<T>.withData(data);
if (length > 0) {
result._setLength(length);
@ -99,7 +97,7 @@ class _GrowableList<T> extends ListBase<T> {
}
factory _GrowableList.withCapacity(int capacity) {
var data = new _List((capacity == 0) ? _kDefaultCapacity : capacity);
var data = _allocateData(capacity);
return new _GrowableList<T>.withData(data);
}
@ -112,16 +110,6 @@ class _GrowableList<T> extends ListBase<T> {
void set length(int new_length) {
int old_capacity = _capacity;
int new_capacity = new_length;
if (new_length == 0) {
// Ensure that we use _kDefaultCapacity only when the old_capacity
// is greater than _kDefaultCapacity otherwise we end up growing the
// the array.
if (old_capacity < _kDefaultCapacity) {
new_capacity = old_capacity;
} else {
new_capacity = _kDefaultCapacity;
}
}
if (new_capacity > old_capacity) {
_grow(new_capacity);
_setLength(new_length);
@ -152,14 +140,10 @@ class _GrowableList<T> extends ListBase<T> {
void operator []=(int index, T value) native "GrowableList_setIndexed";
// The length of this growable array. It is always less than or equal to the
// length of the object array, which itself is always greater than 0, so that
// grow() does not have to check for a zero length object array before
// doubling its size.
void add(T value) {
var len = length;
if (len == _capacity) {
_grow(len * 2);
_grow(_nextCapacity(len));
}
_setLength(len + 1);
this[len] = value;
@ -178,7 +162,7 @@ class _GrowableList<T> extends ListBase<T> {
var newLen = len + iterLen;
if (newLen > cap) {
do {
cap *= 2;
cap = _nextCapacity(cap);
} while (newLen > cap);
_grow(cap);
}
@ -204,7 +188,7 @@ class _GrowableList<T> extends ListBase<T> {
if (this.length != newLen) throw new ConcurrentModificationError(this);
len = newLen;
}
_grow(_capacity * 2);
_grow(_nextCapacity(_capacity));
} while (true);
}
@ -232,20 +216,36 @@ class _GrowableList<T> extends ListBase<T> {
;
}
void _grow(int new_capacity) {
var new_data = new _List(new_capacity);
for (int i = 0; i < length; i++) {
new_data[i] = this[i];
// Shared array used as backing for new empty growable arrays.
static final _List _emptyList = new _List(0);
static _List _allocateData(int capacity) {
if (capacity == 0) {
// Use shared empty list as backing.
return _emptyList;
}
_setData(new_data);
// Round up size to the next odd number, since this is free
// because of alignment requirements of the GC.
return new _List(capacity | 1);
}
// Grow from 0 to 3, and then double + 1.
int _nextCapacity(int old_capacity) => (old_capacity * 2) | 3;
void _grow(int new_capacity) {
var newData = _allocateData(new_capacity);
for (int i = 0; i < length; i++) {
newData[i] = this[i];
}
_setData(newData);
}
void _shrink(int new_capacity, int new_length) {
var new_data = new _List(new_capacity);
var newData = _allocateData(new_capacity);
for (int i = 0; i < new_length; i++) {
new_data[i] = this[i];
newData[i] = this[i];
}
_setData(new_data);
_setData(newData);
}
// Iterable interface.

View file

@ -173,9 +173,9 @@ const StackTrace& GetCurrentStackTrace(int skip_frames) {
const GrowableObjectArray& pc_offset_list =
GrowableObjectArray::Handle(GrowableObjectArray::New());
AppendFrames(code_list, pc_offset_list, skip_frames);
const Array& code_array = Array::Handle(Array::MakeArray(code_list));
const Array& code_array = Array::Handle(Array::MakeFixedLength(code_list));
const Array& pc_offset_array =
Array::Handle(Array::MakeArray(pc_offset_list));
Array::Handle(Array::MakeFixedLength(pc_offset_list));
const StackTrace& stacktrace =
StackTrace::Handle(StackTrace::New(code_array, pc_offset_array));
return stacktrace;

View file

@ -2429,7 +2429,7 @@ void ClassFinalizer::ApplyMixinMembers(const Class& cls) {
cloned_funcs.Add(func);
}
}
functions = Array::MakeArray(cloned_funcs);
functions = Array::MakeFixedLength(cloned_funcs);
cls.SetFunctions(functions);
// Now clone the fields from the mixin class. There should be no
@ -2664,7 +2664,8 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
const Class& mixin_cls = Class::Handle(mixin_type.type_class());
CreateForwardingConstructors(cls, mixin_cls, cloned_funcs);
const Array& functions = Array::Handle(Array::MakeArray(cloned_funcs));
const Array& functions =
Array::Handle(Array::MakeFixedLength(cloned_funcs));
cls.SetFunctions(functions);
}
// Every class should have at least a constructor, unless it is a top level

View file

@ -3472,7 +3472,7 @@ class MintDeserializationCluster : public DeserializationCluster {
}
}
const Array& constants_array =
Array::Handle(zone, Array::MakeArray(new_constants));
Array::Handle(zone, Array::MakeFixedLength(new_constants));
const Class& mint_cls =
Class::Handle(zone, Isolate::Current()->object_store()->mint_class());
mint_cls.set_constants(constants_array);

View file

@ -73,7 +73,7 @@ RawArray* StackMapTableBuilder::FinalizeStackMaps(const Code& code) {
if (num_entries == 0) {
return Object::empty_array().raw();
}
return Array::MakeArray(list_);
return Array::MakeFixedLength(list_);
}
@ -406,7 +406,7 @@ RawArray* CodeSourceMapBuilder::InliningIdToFunction() {
if (inlined_functions_.Length() == 0) {
return Object::empty_array().raw();
}
return Array::MakeArray(inlined_functions_);
return Array::MakeFixedLength(inlined_functions_);
}

View file

@ -5786,7 +5786,7 @@ DART_EXPORT Dart_Handle Dart_GetImportsOfScheme(Dart_Handle scheme) {
}
}
return Api::NewHandle(T, Array::MakeArray(result));
return Api::NewHandle(T, Array::MakeFixedLength(result));
}

View file

@ -1375,8 +1375,9 @@ RawObject* ActivationFrame::Evaluate(const String& expr,
if (function().is_static()) {
const Class& cls = Class::Handle(function().Owner());
return cls.Evaluate(expr, Array::Handle(Array::MakeArray(param_names)),
Array::Handle(Array::MakeArray(param_values)));
return cls.Evaluate(expr,
Array::Handle(Array::MakeFixedLength(param_names)),
Array::Handle(Array::MakeFixedLength(param_values)));
} else {
const Object& receiver = Object::Handle(GetReceiver());
const Class& method_cls = Class::Handle(function().origin());
@ -1386,8 +1387,8 @@ RawObject* ActivationFrame::Evaluate(const String& expr,
}
const Instance& inst = Instance::Cast(receiver);
return inst.Evaluate(method_cls, expr,
Array::Handle(Array::MakeArray(param_names)),
Array::Handle(Array::MakeArray(param_values)));
Array::Handle(Array::MakeFixedLength(param_names)),
Array::Handle(Array::MakeFixedLength(param_values)));
}
UNREACHABLE();
return Object::null();
@ -3175,7 +3176,7 @@ RawArray* Debugger::GetInstanceFields(const Instance& obj) {
}
cls = cls.SuperClass();
}
return Array::MakeArray(field_list);
return Array::MakeFixedLength(field_list);
}
@ -3195,7 +3196,7 @@ RawArray* Debugger::GetStaticFields(const Class& cls) {
field_list.Add(field_value);
}
}
return Array::MakeArray(field_list);
return Array::MakeFixedLength(field_list);
}
@ -3243,7 +3244,7 @@ RawArray* Debugger::GetLibraryFields(const Library& lib) {
const GrowableObjectArray& field_list =
GrowableObjectArray::Handle(GrowableObjectArray::New(8));
CollectLibraryFields(field_list, lib, String::Handle(zone), true);
return Array::MakeArray(field_list);
return Array::MakeFixedLength(field_list);
}
@ -3272,7 +3273,7 @@ RawArray* Debugger::GetGlobalFields(const Library& lib) {
CollectLibraryFields(field_list, imported, prefix_name, false);
}
}
return Array::MakeArray(field_list);
return Array::MakeFixedLength(field_list);
}

View file

@ -796,7 +796,7 @@ DART_EXPORT Dart_Handle Dart_ScriptGetTokenInfo(intptr_t library_id,
const GrowableObjectArray& info =
GrowableObjectArray::Handle(script.GenerateLineNumberArray());
return Api::NewHandle(T, Array::MakeArray(info));
return Api::NewHandle(T, Array::MakeFixedLength(info));
}
@ -929,7 +929,7 @@ DART_EXPORT Dart_Handle Dart_GetLibraryImports(intptr_t library_id) {
import_list.Add(Smi::Handle(Smi::New(imported.index())));
}
}
return Api::NewHandle(T, Array::MakeArray(import_list));
return Api::NewHandle(T, Array::MakeFixedLength(import_list));
}

View file

@ -761,7 +761,7 @@ void Exceptions::CreateAndThrowTypeError(TokenPosition location,
}
}
}
const Array& arr = Array::Handle(zone, Array::MakeArray(pieces));
const Array& arr = Array::Handle(zone, Array::MakeFixedLength(pieces));
const String& error_msg = String::Handle(zone, String::ConcatAll(arr));
args.SetAt(3, error_msg);

View file

@ -81,8 +81,8 @@ intptr_t GCSweeper::SweepLargePage(HeapPage* page) {
words_to_end = (raw_obj->Size() >> kWordSizeLog2);
}
#ifdef DEBUG
// String::MakeExternal and Array::MakeArray create trailing filler objects,
// but they are always unreachable. Verify that they are not marked.
// String::MakeExternal and Array::MakeFixedLength create trailing filler
// objects, but they are always unreachable. Verify that they are not marked.
uword current = RawObject::ToAddr(raw_obj) + raw_obj->Size();
uword end = page->object_end();
while (current < end) {

View file

@ -168,7 +168,7 @@ namespace dart {
V(_List, []=, ObjectArraySetIndexed, Dynamic, 0x51691f4c) \
V(_GrowableList, .withData, GrowableArray_Allocate, GrowableObjectArray, \
0x3468a26f) \
V(_GrowableList, add, GrowableArray_add, Dynamic, 0x19eaa9aa) \
V(_GrowableList, add, GrowableArray_add, Dynamic, 0x1ce3b4f8) \
V(_RegExp, _ExecuteMatch, RegExp_ExecuteMatch, Dynamic, 0x380184b1) \
V(_RegExp, _ExecuteMatchSticky, RegExp_ExecuteMatchSticky, Dynamic, \
0x79b8f955) \
@ -378,7 +378,7 @@ namespace dart {
V(_TypedList, get:length, TypedDataLength, 0x20915079) \
V(_GrowableList, get:length, GrowableArrayLength, 0x18dd1255) \
V(_GrowableList, get:_capacity, GrowableArrayCapacity, 0x2e044a01) \
V(_GrowableList, add, GrowableListAdd, 0x19eaa9aa) \
V(_GrowableList, add, GrowableListAdd, 0x1ce3b4f8) \
V(_GrowableList, removeLast, GrowableListRemoveLast, 0x3daaaca4) \
V(_StringBase, get:length, StringBaseLength, 0x2a2c8f72) \
V(ListIterator, moveNext, ListIteratorMoveNext, 0x7ead154d) \
@ -558,7 +558,7 @@ class MethodRecognizer : public AllStatic {
V(_GrowableListWithData, _GrowableList, .withData, kGrowableObjectArrayCid, \
0x3468a26f) \
V(_GrowableListFactory, _GrowableList, ., kGrowableObjectArrayCid, \
0x3bd724aa) \
0x7c4346ab) \
V(_Int8ArrayFactory, Int8List, ., kTypedDataInt8ArrayCid, 0x165876c2) \
V(_Uint8ArrayFactory, Uint8List, ., kTypedDataUint8ArrayCid, 0x52988118) \
V(_Uint8ClampedArrayFactory, Uint8ClampedList, ., \

View file

@ -124,7 +124,7 @@ DART_EXPORT Dart_Handle Dart_GetFunctionNames(Dart_Handle target) {
return Api::NewError(
"%s expects argument 'target' to be a class or library.", CURRENT_FUNC);
}
return Api::NewHandle(T, Array::MakeArray(names));
return Api::NewHandle(T, Array::MakeFixedLength(names));
}
@ -336,7 +336,7 @@ DART_EXPORT Dart_Handle Dart_LibraryGetClassNames(Dart_Handle library) {
name = cls.UserVisibleName();
names.Add(name);
}
return Api::NewHandle(T, Array::MakeArray(names));
return Api::NewHandle(T, Array::MakeFixedLength(names));
}

View file

@ -3028,7 +3028,7 @@ bool Class::ApplyPatch(const Class& patch, Error* error) const {
// Preserve the original implicit constructor.
new_functions.Add(orig_implicit_ctor);
}
Array& new_list = Array::Handle(Array::MakeArray(new_functions));
Array& new_list = Array::Handle(Array::MakeFixedLength(new_functions));
SetFunctions(new_list);
// Merge the two list of fields. Raise an error when duplicates are found or
@ -3092,7 +3092,7 @@ static RawString* BuildClosureSource(const Array& formal_params,
src_pieces.Add(Symbols::RParenArrow());
src_pieces.Add(expr);
src_pieces.Add(Symbols::Semicolon());
return String::ConcatAll(Array::Handle(Array::MakeArray(src_pieces)));
return String::ConcatAll(Array::Handle(Array::MakeFixedLength(src_pieces)));
}
@ -8693,7 +8693,7 @@ RawString* TokenStream::GenerateSource(TokenPosition start_pos,
prev = curr;
curr = next;
}
const Array& source = Array::Handle(Array::MakeArray(literals));
const Array& source = Array::Handle(Array::MakeFixedLength(literals));
return String::ConcatAll(source);
}
@ -9414,7 +9414,7 @@ intptr_t Script::GetTokenLineUsingLineStarts(
}
tkit.Advance();
}
line_starts_array = Array::MakeArray(line_starts_list);
line_starts_array = Array::MakeFixedLength(line_starts_list);
set_line_starts(line_starts_array);
}
@ -10662,7 +10662,7 @@ RawArray* Library::LoadedScripts() const {
}
// Create the array of scripts and cache it in loaded_scripts_.
const Array& scripts_array = Array::Handle(Array::MakeArray(scripts));
const Array& scripts_array = Array::Handle(Array::MakeFixedLength(scripts));
StorePointer(&raw_ptr()->loaded_scripts_, scripts_array.raw());
}
return loaded_scripts();
@ -22120,19 +22120,30 @@ RawArray* Array::Grow(const Array& source,
}
RawArray* Array::MakeArray(const GrowableObjectArray& growable_array) {
RawArray* Array::MakeFixedLength(const GrowableObjectArray& growable_array,
bool unique) {
ASSERT(!growable_array.IsNull());
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
intptr_t used_len = growable_array.Length();
// Get the type arguments and prepare to copy them.
const TypeArguments& type_arguments =
TypeArguments::Handle(growable_array.GetTypeArguments());
if ((used_len == 0) && (type_arguments.IsNull())) {
// This is a raw List (as in no type arguments), so we can return the
// simple empty array.
return Object::empty_array().raw();
if (used_len == 0) {
if (type_arguments.IsNull() && !unique) {
// This is a raw List (as in no type arguments), so we can return the
// simple empty array.
return Object::empty_array().raw();
}
// The backing array may be a shared instance, or may not have correct
// type parameters. Create a new empty array.
Heap::Space space = thread->IsMutatorThread() ? Heap::kNew : Heap::kOld;
Array& array = Array::Handle(zone, Array::New(0, space));
array.SetTypeArguments(type_arguments);
return array.raw();
}
intptr_t capacity_len = growable_array.Capacity();
Zone* zone = Thread::Current()->zone();
const Array& array = Array::Handle(zone, growable_array.data());
array.SetTypeArguments(type_arguments);
intptr_t capacity_size = Array::InstanceSize(capacity_len);
@ -22211,8 +22222,8 @@ RawImmutableArray* ImmutableArray::New(intptr_t len, Heap::Space space) {
void GrowableObjectArray::Add(const Object& value, Heap::Space space) const {
ASSERT(!IsNull());
if (Length() == Capacity()) {
// TODO(Issue 2500): Need a better growth strategy.
intptr_t new_capacity = (Capacity() == 0) ? 4 : Capacity() * 2;
// Grow from 0 to 3, and then double + 1.
intptr_t new_capacity = (Capacity() * 2) | 3;
if (new_capacity <= Capacity()) {
Exceptions::ThrowOOM();
UNREACHABLE();
@ -22249,7 +22260,9 @@ RawObject* GrowableObjectArray::RemoveLast() const {
RawGrowableObjectArray* GrowableObjectArray::New(intptr_t capacity,
Heap::Space space) {
const Array& data = Array::Handle(Array::New(capacity, space));
RawArray* raw_data = (capacity == 0) ? Object::empty_array().raw()
: Array::New(capacity, space);
const Array& data = Array::Handle(raw_data);
return New(data, space);
}

View file

@ -7656,7 +7656,10 @@ class Array : public Instance {
// object or a regular Object so that it can be traversed during garbage
// collection. The backing array of the original Growable Object Array is
// set to an empty array.
static RawArray* MakeArray(const GrowableObjectArray& growable_array);
// If the unique parameter is false, the function is allowed to return
// a shared Array instance.
static RawArray* MakeFixedLength(const GrowableObjectArray& growable_array,
bool unique = false);
RawArray* Slice(intptr_t start,
intptr_t count,
@ -7770,8 +7773,6 @@ class GrowableObjectArray : public Instance {
// reusing the type argument vector of the instantiator.
ASSERT(value.IsNull() || ((value.Length() >= 1) && value.IsInstantiated() &&
value.IsCanonical()));
const Array& contents = Array::Handle(data());
contents.SetTypeArguments(value);
StorePointer(&raw_ptr()->type_arguments_, value.raw());
}
@ -7822,7 +7823,7 @@ class GrowableObjectArray : public Instance {
return &(DataArray()->data()[index]);
}
static const int kDefaultInitialCapacity = 4;
static const int kDefaultInitialCapacity = 0;
FINAL_HEAP_OBJECT_IMPLEMENTATION(GrowableObjectArray, Instance);
friend class Array;

View file

@ -2148,7 +2148,7 @@ ISOLATE_UNIT_TEST_CASE(GrowableObjectArray) {
EXPECT(value.Equals(expected_value));
}
// Test the MakeArray functionality to make sure the resulting array
// Test the MakeFixedLength functionality to make sure the resulting array
// object is properly setup.
// 1. Should produce an array of length 2 and a left over int8 array.
Array& new_array = Array::Handle();
@ -2165,7 +2165,7 @@ ISOLATE_UNIT_TEST_CASE(GrowableObjectArray) {
array.Add(value);
}
used_size = Array::InstanceSize(array.Length());
new_array = Array::MakeArray(array);
new_array = Array::MakeFixedLength(array);
addr = RawObject::ToAddr(new_array.raw());
obj = RawObject::FromAddr(addr);
EXPECT(obj.IsArray());
@ -2186,7 +2186,7 @@ ISOLATE_UNIT_TEST_CASE(GrowableObjectArray) {
array.Add(value);
}
used_size = Array::InstanceSize(array.Length());
new_array = Array::MakeArray(array);
new_array = Array::MakeFixedLength(array);
addr = RawObject::ToAddr(new_array.raw());
obj = RawObject::FromAddr(addr);
EXPECT(obj.IsArray());
@ -2207,7 +2207,7 @@ ISOLATE_UNIT_TEST_CASE(GrowableObjectArray) {
array.Add(value);
}
used_size = Array::InstanceSize(array.Length());
new_array = Array::MakeArray(array);
new_array = Array::MakeFixedLength(array);
addr = RawObject::ToAddr(new_array.raw());
obj = RawObject::FromAddr(addr);
EXPECT(obj.IsArray());
@ -2229,7 +2229,7 @@ ISOLATE_UNIT_TEST_CASE(GrowableObjectArray) {
Heap* heap = Isolate::Current()->heap();
heap->CollectAllGarbage();
intptr_t capacity_before = heap->CapacityInWords(Heap::kOld);
new_array = Array::MakeArray(array);
new_array = Array::MakeFixedLength(array);
EXPECT_EQ(1, new_array.Length());
heap->CollectAllGarbage();
intptr_t capacity_after = heap->CapacityInWords(Heap::kOld);

View file

@ -289,7 +289,7 @@ HeapPage* PageSpace::AllocateLargePage(intptr_t size, HeapPage::PageType type) {
large_pages_ = page;
IncreaseCapacityInWords(page_size_in_words);
// Only one object in this page (at least until String::MakeExternal or
// Array::MakeArray is called).
// Array::MakeFixedLength is called).
page->set_object_end(page->object_start() + size);
return page;
}

View file

@ -1336,7 +1336,7 @@ RawArray* Parser::EvaluateMetadata() {
const Instance& val = EvaluateConstExpr(expr_pos, expr);
meta_values.Add(val, Heap::kOld);
}
return Array::MakeArray(meta_values);
return Array::MakeFixedLength(meta_values);
}
@ -4990,7 +4990,7 @@ void Parser::ParseClassDefinition(const Class& cls) {
}
CheckConstructors(&members);
// Need to compute this here since MakeArray() will clear the
// Need to compute this here since MakeFixedLength() will clear the
// functions array in members.
const bool need_implicit_constructor =
!members.has_constructor() && !cls.is_patch();
@ -5750,7 +5750,7 @@ void Parser::ParseInterfaceList(const Class& cls) {
}
all_interfaces.Add(interface, Heap::kOld);
} while (CurrentToken() == Token::kCOMMA);
cls_interfaces = Array::MakeArray(all_interfaces);
cls_interfaces = Array::MakeFixedLength(all_interfaces);
cls.set_interfaces(cls_interfaces);
}
@ -5776,8 +5776,8 @@ RawAbstractType* Parser::ParseMixins(const AbstractType& super_type) {
}
mixin_types.Add(mixin_type, Heap::kOld);
} while (CurrentToken() == Token::kCOMMA);
return MixinAppType::New(super_type,
Array::Handle(Z, Array::MakeArray(mixin_types)));
return MixinAppType::New(
super_type, Array::Handle(Z, Array::MakeFixedLength(mixin_types)));
}
@ -6306,7 +6306,7 @@ void Parser::ParseLibraryImportExport(const Object& tl_owner,
}
// Check if this conditional line overrides the default import.
const String& key = String::Handle(String::ConcatAll(
Array::Handle(Array::MakeArray(pieces)), allocation_space_));
Array::Handle(Array::MakeFixedLength(pieces)), allocation_space_));
const String& value =
(valueNode == NULL)
? Symbols::True()
@ -6365,10 +6365,10 @@ void Parser::ParseLibraryImportExport(const Object& tl_owner,
}
}
if (show_list.Length() > 0) {
show_names = Array::MakeArray(show_list);
show_names = Array::MakeFixedLength(show_list);
}
if (hide_list.Length() > 0) {
hide_names = Array::MakeArray(hide_list);
hide_names = Array::MakeFixedLength(hide_list);
}
}
ExpectSemicolon();
@ -10413,7 +10413,7 @@ AstNode* Parser::ParseTryStatement(String* label_name) {
CatchClauseNode* catch_clause = new (Z) CatchClauseNode(
handler_pos, catch_handler_list,
Array::ZoneHandle(Z, Array::MakeArray(handler_types)), context_var,
Array::ZoneHandle(Z, Array::MakeFixedLength(handler_types)), context_var,
exception_var, stack_trace_var,
is_async ? saved_exception_var : exception_var,
is_async ? saved_stack_trace_var : stack_trace_var,
@ -11774,7 +11774,7 @@ ArgumentListNode* Parser::ParseActualParameters(
ExpectToken(Token::kRPAREN);
SetAllowFunctionLiterals(saved_mode);
if (named_argument_seen) {
arguments->set_names(Array::Handle(Z, Array::MakeArray(names)));
arguments->set_names(Array::Handle(Z, Array::MakeFixedLength(names)));
}
return arguments;
}

View file

@ -1772,7 +1772,7 @@ void Precompiler::DropFunctions() {
}
if (retained_functions.Length() > 0) {
functions = Array::MakeArray(retained_functions);
functions = Array::MakeFixedLength(retained_functions);
cls.SetFunctions(functions);
} else {
cls.SetFunctions(Object::empty_array());
@ -1834,7 +1834,7 @@ void Precompiler::DropFields() {
}
if (retained_fields.Length() > 0) {
fields = Array::MakeArray(retained_fields);
fields = Array::MakeFixedLength(retained_fields);
cls.SetFields(fields);
} else {
cls.SetFields(Object::empty_array());
@ -1988,7 +1988,7 @@ void Precompiler::TraceTypesFromRetainedClasses() {
intptr_t cid = cls.id();
if ((cid == kMintCid) || (cid == kBigintCid) || (cid == kDoubleCid)) {
// Constants stored as a plain list, no rehashing needed.
constants = Array::MakeArray(retained_constants);
constants = Array::MakeFixedLength(retained_constants);
cls.set_constants(constants);
} else {
// Rehash.

View file

@ -1027,24 +1027,9 @@ TEST_CASE(Profiler_ArrayAllocation) {
Profile profile(isolate);
AllocationFilter filter(isolate->main_port(), array_class.id());
profile.Build(thread, &filter, Profile::kNoTags);
// We should still only have one allocation sample.
EXPECT_EQ(1, profile.sample_count());
ProfileTrieWalker walker(&profile);
walker.Reset(Profile::kExclusiveCode);
EXPECT(walker.Down());
EXPECT_STREQ("DRT_AllocateArray", walker.CurrentName());
EXPECT(walker.Down());
EXPECT_STREQ("[Stub] AllocateArray", walker.CurrentName());
EXPECT(walker.Down());
EXPECT_STREQ("new _List", walker.CurrentName());
EXPECT(walker.Down());
EXPECT_STREQ("new _GrowableList", walker.CurrentName());
EXPECT(walker.Down());
EXPECT_STREQ("new List._internal", walker.CurrentName());
EXPECT(walker.Down());
EXPECT_STREQ("bar", walker.CurrentName());
EXPECT(!walker.Down());
// We should have no allocation samples, since empty
// growable lists use a shared backing.
EXPECT_EQ(0, profile.sample_count());
}
}

View file

@ -199,7 +199,7 @@ intptr_t RawObject::SizeFromClass() const {
uword tags = ptr()->tags_;
intptr_t tags_size = SizeTag::decode(tags);
if ((class_id == kArrayCid) && (instance_size > tags_size && tags_size > 0)) {
// TODO(22501): Array::MakeArray could be in the process of shrinking
// TODO(22501): Array::MakeFixedLength could be in the process of shrinking
// the array (see comment therein), having already updated the tags but not
// yet set the new length. Wait a millisecond and try again.
int retries_remaining = 1000; // ... but not forever.

View file

@ -427,11 +427,11 @@ class RawObject {
intptr_t result = SizeTag::decode(tags);
if (result != 0) {
#if defined(DEBUG)
// TODO(22501) Array::MakeArray has a race with this code: we might have
// loaded tags field and then MakeArray could have updated it leading
// to inconsistency between SizeFromClass() and SizeTag::decode(tags).
// We are working around it by reloading tags_ and recomputing
// size from tags.
// TODO(22501) Array::MakeFixedLength has a race with this code: we might
// have loaded tags field and then MakeFixedLength could have updated it
// leading to inconsistency between SizeFromClass() and
// SizeTag::decode(tags). We are working around it by reloading tags_ and
// recomputing size from tags.
const intptr_t size_from_class = SizeFromClass();
if ((result > size_from_class) && (GetClassId() == kArrayCid) &&
(ptr()->tags_ != tags)) {

View file

@ -2353,8 +2353,9 @@ static bool Evaluate(Thread* thread, JSONStream* js) {
if (BuildScope(thread, js, names, values)) {
return true;
}
const Array& names_array = Array::Handle(zone, Array::MakeArray(names));
const Array& values_array = Array::Handle(zone, Array::MakeArray(values));
const Array& names_array = Array::Handle(zone, Array::MakeFixedLength(names));
const Array& values_array =
Array::Handle(zone, Array::MakeFixedLength(values));
const String& expr_str = String::Handle(zone, String::New(expr));
ObjectIdRing::LookupResult lookup_result;

View file

@ -80,17 +80,17 @@ static RawArray* Eval(Dart_Handle lib, const char* expr) {
Zone* zone = Thread::Current()->zone();
const GrowableObjectArray& value =
Api::UnwrapGrowableObjectArrayHandle(zone, expr_val);
const Array& result = Array::Handle(Array::MakeArray(value));
const Array& result = Array::Handle(Array::MakeFixedLength(value));
GrowableObjectArray& growable = GrowableObjectArray::Handle();
growable ^= result.At(4);
// Append dummy isolate id to parameter values.
growable.Add(dummy_isolate_id);
Array& array = Array::Handle(Array::MakeArray(growable));
Array& array = Array::Handle(Array::MakeFixedLength(growable));
result.SetAt(4, array);
growable ^= result.At(5);
// Append dummy isolate id to parameter values.
growable.Add(dummy_isolate_id);
array = Array::MakeArray(growable);
array = Array::MakeFixedLength(growable);
result.SetAt(5, array);
return result.raw();
}