fix list_test for strong mode, and fix DDC List constructors

R=vsm@google.com

Review-Url: https://codereview.chromium.org/3009623002 .
This commit is contained in:
Jennifer Messerly 2017-08-25 18:32:38 -07:00
parent 343452d43f
commit 087c5c6a81
25 changed files with 661 additions and 383 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -151,6 +151,7 @@ class CodeGenerator extends Object
final ClassElement stringClass;
final ClassElement functionClass;
final ClassElement privateSymbolClass;
final PropertyAccessorElement _undefinedConstant;
ConstFieldVisitor _constants;
@ -225,7 +226,10 @@ class CodeGenerator extends Object
functionClass = _getLibrary(c, 'dart:core').getType('Function'),
privateSymbolClass =
_getLibrary(c, 'dart:_internal').getType('PrivateSymbol'),
dartJSLibrary = _getLibrary(c, 'dart:js') {
dartJSLibrary = _getLibrary(c, 'dart:js'),
_undefinedConstant =
_getLibrary(c, 'dart:_runtime').publicNamespace.get('undefined') {
assert(_undefinedConstant != null);
typeRep = new JSTypeRep(rules, types);
}
@ -595,7 +599,8 @@ class CodeGenerator extends Object
for (var declaration in unit.declarations) {
if (declaration is TopLevelVariableDeclaration) {
inferNullableTypes(declaration);
if (isInternalSdk && declaration.variables.isFinal) {
if (isInternalSdk &&
(declaration.variables.isFinal || declaration.variables.isConst)) {
_emitInternalSdkFields(declaration.variables.variables);
} else {
(fields ??= []).addAll(declaration.variables.variables);
@ -2439,25 +2444,38 @@ class CodeGenerator extends Object
for (var param in parameters.parameters) {
var jsParam = _emitSimpleIdentifier(param.identifier);
if (!options.destructureNamedParams) {
if (!options.destructureNamedParams &&
param.kind != ParameterKind.REQUIRED) {
if (param.kind == ParameterKind.NAMED) {
// Parameters will be passed using their real names, not the (possibly
// renamed) local variable.
var paramName = js.string(param.identifier.name, "'");
// TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming.
body.add(js.statement('let # = # && # in # ? #.# : #;', [
jsParam,
namedArgumentTemp,
paramName,
namedArgumentTemp,
namedArgumentTemp,
paramName,
_defaultParamValue(param),
]));
var defaultValue = _defaultParamValue(param);
if (defaultValue != null) {
// TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming.
body.add(js.statement('let # = # && # in # ? #.# : #;', [
jsParam,
namedArgumentTemp,
paramName,
namedArgumentTemp,
namedArgumentTemp,
paramName,
defaultValue,
]));
} else {
body.add(js.statement('let # = # && #.#;', [
jsParam,
namedArgumentTemp,
namedArgumentTemp,
paramName,
]));
}
} else if (param.kind == ParameterKind.POSITIONAL) {
body.add(js.statement('if (# === void 0) # = #;',
[jsParam, jsParam, _defaultParamValue(param)]));
var defaultValue = _defaultParamValue(param);
if (defaultValue != null) {
body.add(js.statement(
'if (# === void 0) # = #;', [jsParam, jsParam, defaultValue]));
}
}
}
@ -2481,12 +2499,18 @@ class CodeGenerator extends Object
JS.Expression _defaultParamValue(FormalParameter param) {
if (param is DefaultFormalParameter && param.defaultValue != null) {
return _visit(param.defaultValue);
var defaultValue = param.defaultValue;
return _isJSUndefined(defaultValue) ? null : _visit(defaultValue);
} else {
return new JS.LiteralNull();
}
}
bool _isJSUndefined(Expression expr) {
expr = expr is AsExpression ? expr.expression : expr;
return expr is Identifier && expr.staticElement == _undefinedConstant;
}
JS.Fun _emitNativeFunctionBody(MethodDeclaration node) {
String name =
getAnnotationName(node.element, isJSAnnotation) ?? node.name.name;
@ -3947,18 +3971,19 @@ class CodeGenerator extends Object
} else {
name = _visit(param.identifier);
}
var defaultValue = _defaultParamValue(param);
namedVars.add(new JS.DestructuredVariable(
name: name,
structure: structure,
defaultValue: _defaultParamValue(param)));
name: name, structure: structure, defaultValue: defaultValue));
} else {
needsOpts = true;
}
} else {
var jsParam = _visit(param);
result.add(param is DefaultFormalParameter && destructure
var defaultValue = _defaultParamValue(param);
result.add(destructure && defaultValue != null
? new JS.DestructuredVariable(
name: jsParam, defaultValue: _defaultParamValue(param))
name: jsParam, defaultValue: defaultValue)
: jsParam);
}
}
@ -4066,7 +4091,7 @@ class CodeGenerator extends Object
}
/// This is not used--we emit top-level fields as we are emitting the
/// compilation unit, see [_emitCompilationUnit].
/// compilation unit, see [visitCompilationUnit].
@override
visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
assert(false);
@ -4127,6 +4152,9 @@ class CodeGenerator extends Object
// TODO(jmesserly): it'd be nice to avoid this special case.
void _emitInternalSdkFields(List<VariableDeclaration> fields) {
for (var field in fields) {
// Skip our magic undefined constant.
var element = field.element as TopLevelVariableElement;
if (element.getter == _undefinedConstant) continue;
_moduleItems.add(annotate(
js.statement('# = #;',
[_emitTopLevelName(field.element), _visitInitializer(field)]),

View file

@ -20,8 +20,14 @@ pushd $LIBROOT > /dev/null
# script, so you could debug the output without recompiling?
echo "
let sdk = require(\"dart_sdk\");
let main = require(\"$BASENAME\").$BASENAME.main;
sdk._isolate_helper.startRootIsolate(main, []);" \
let main = require(\"./$BASENAME\").$BASENAME.main;
sdk.dart.ignoreWhitelistedErrors(false);
try {
sdk._isolate_helper.startRootIsolate(main, []);
} catch(e) {
console.error(e.toString(), sdk.dart.stackTrace(e).toString());
process.exit(1);
}" \
> $LIBROOT/$BASENAME.run.js
node $BASENAME.run.js
node $BASENAME.run.js || exit 1
popd > /dev/null

View file

@ -21,6 +21,8 @@ import 'dart:_js_helper'
Primitives,
stringJoinUnchecked;
import 'dart:_runtime' show undefined;
import 'dart:_foreign_helper' show JS;
import 'dart:_native_typed_data' show NativeUint8List;
@ -318,39 +320,39 @@ class Stopwatch {
@patch
class List<E> {
@patch
factory List([int _length]) {
factory List([int _length = undefined]) {
dynamic list;
if (_length == null) {
if (JS('bool', '# === void 0', _length)) {
list = JS('', '[]');
} else {
@notNull
var length = _length;
if (length < 0) {
var length = JS('int', '#', _length);
if (_length == null || length < 0) {
throw new ArgumentError(
"Length must be a non-negative integer: $length");
"Length must be a non-negative integer: $_length");
}
list = JSArray.markFixedList(JS('', 'new Array(#)', length));
list = JS('', 'new Array(#)', length);
JSArray.markFixedList(list);
}
return new JSArray<E>.of(list);
}
@patch
factory List.filled(int length, E fill, {bool growable: true}) {
List<E> result = new List<E>(length);
factory List.filled(@nullCheck int length, E fill, {bool growable: false}) {
var list = new JSArray<E>.of(JS('', 'new Array(#)', length));
if (length != 0 && fill != null) {
@notNull
var length = result.length;
var length = list.length;
for (int i = 0; i < length; i++) {
result[i] = fill;
list[i] = fill;
}
}
if (growable) return result;
return makeListFixedLength<E>(result);
if (!growable) JSArray.markFixedList(list);
return list;
}
@patch
factory List.from(Iterable elements, {bool growable: true}) {
List<E> list = new List<E>();
var list = new JSArray<E>.of(JS('', '[]'));
// Specialize the copy loop for the case that doesn't need a
// runtime check.
if (elements is Iterable<E>) {
@ -362,14 +364,15 @@ class List<E> {
list.add(e as E);
}
}
if (growable) return list;
return makeListFixedLength/*<E>*/(list);
if (!growable) JSArray.markFixedList(list);
return list;
}
@patch
factory List.unmodifiable(Iterable elements) {
var result = new List<E>.from(elements, growable: false);
return makeFixedListUnmodifiable/*<E>*/(result);
var list = new List<E>.from(elements);
JSArray.markUnmodifiableList(list);
return list;
}
}

View file

@ -531,12 +531,6 @@ defineExtensionMembers(type, methodNames) => JS('', '''(() => {
upgradeSig($_setterSig);
})()''');
/// Sets the type of `obj` to be `type`
setType(obj, type) {
JS('', '#.__proto__ = #.prototype', obj, type);
return obj;
}
/// Link the extension to the type it's extending as a base class.
setBaseClass(derived, base) {
JS('', '#.prototype.__proto__ = #.prototype', derived, base);

View file

@ -790,9 +790,9 @@ constList(elements, elementType) => JS('', '''(() => {
let value = map.get($elementType);
if (value) return value;
value = $setType($elements, ${getGenericClass(JSArray)}($elementType));
map.set($elementType, value);
return value;
${getGenericClass(JSArray)}($elementType).unmodifiable($elements);
map.set($elementType, elements);
return elements;
})()''');
// The following are helpers for Object methods when the receiver

View file

@ -7,6 +7,11 @@ part of dart._runtime;
/// by the Dart runtime.
// TODO(ochafik): Rewrite some of these in Dart when possible.
/// The JavaScript undefined constant.
///
/// This is initialized by DDC to JS void 0.
const undefined = null;
defineProperty(obj, name, desc) =>
JS('', 'Object.defineProperty(#, #, #)', obj, name, desc);

View file

@ -104,11 +104,23 @@ class NullError extends Interceptor implements NoSuchMethodError {
// TODO(vsm): Distinguish between null reference errors and other
// TypeErrors. We should not get non-null TypeErrors from DDC code,
// but we may from native JavaScript.
var message = JS('String', '#.message', this);
return "NullError: $message";
return "NullError: ${JS('String', '#.message', this)}";
}
}
// Note that this needs to be in interceptors.dart in order for
// it to be picked up as an extension type.
@JsPeerInterface(name: 'RangeError')
class JSRangeError extends Interceptor implements ArgumentError {
StackTrace get stackTrace => Primitives.extractStackTrace(this);
get invalidValue => null;
get name => null;
get message => JS('String', '#.message', this);
String toString() => "Invalid argument: $message";
}
// Obsolete in dart dev compiler. Added only so that the same version of
// dart:html can be used in dart2js an dev compiler.
// Warning: calls to these methods need to be removed before custom elements

View file

@ -263,7 +263,7 @@ class _Deserializer {
assert(x[0] == 'fixed');
List result = x[1];
deserializedObjects.add(result);
return new JSArray.markFixed(deserializeArrayInPlace(result));
return new JSArray.fixed(deserializeArrayInPlace(result));
}
// ['extendable', <array>].
@ -271,7 +271,7 @@ class _Deserializer {
assert(x[0] == 'extendable');
List result = x[1];
deserializedObjects.add(result);
return new JSArray.markGrowable(deserializeArrayInPlace(result));
return new JSArray.of(deserializeArrayInPlace(result));
}
// ['mutable', <array>].
@ -288,7 +288,7 @@ class _Deserializer {
List result = x[1];
deserializedObjects.add(result);
// TODO(floitsch): need to mark list as non-changeable.
return new JSArray.markFixed(deserializeArrayInPlace(result));
return new JSArray.unmodifiable(deserializeArrayInPlace(result));
}
// ['map', <key-list>, <value-list>].

View file

@ -18,33 +18,40 @@ class JSArray<E> implements List<E>, JSIndexable<E> {
* Constructor for adding type parameters to an existing JavaScript
* Array. Used for creating literal lists.
*/
factory JSArray.of(allocation) {
factory JSArray.of(list) {
// TODO(sra): Move this to core.List for better readability.
// Capture the parameterized ES6 'JSArray' class.
return JS('-dynamic', '#', dart.setType(allocation, JS('', 'JSArray')));
JS('', '#.__proto__ = JSArray.prototype', list);
return JS('-dynamic', '#', list);
}
// TODO(jmesserly): consider a fixed array subclass instead.
factory JSArray.markFixed(allocation) =>
new JSArray<E>.of(markFixedList(allocation));
factory JSArray.markGrowable(allocation) = JSArray<E>.of;
static List markFixedList(List list) {
// Functions are stored in the hidden class and not as properties in
// the object. We never actually look at the value, but only want
// to know if the property exists.
JS('void', r'#.fixed$length = Array', list);
return JS('JSFixedArray', '#', list);
factory JSArray.fixed(list) {
JS('', '#.__proto__ = JSArray.prototype', list);
JS('', r'#.fixed$length = Array', list);
return JS('-dynamic', '#', list);
}
static List markUnmodifiableList(List list) {
factory JSArray.unmodifiable(list) {
JS('', '#.__proto__ = JSArray.prototype', list);
JS('', r'#.fixed$length = Array', list);
JS('', r'#.immutable$list = Array', list);
return JS('-dynamic', '#', list);
}
static void markFixedList(list) {
// Functions are stored in the hidden class and not as properties in
// the object. We never actually look at the value, but only want
// to know if the property exists.
JS('void', r'#.fixed$length = Array', list);
JS('void', r'#.immutable$list = Array', list);
return JS('JSUnmodifiableArray', '#', list);
JS('', r'#.fixed$length = Array', list);
}
static void markUnmodifiableList(list) {
// Functions are stored in the hidden class and not as properties in
// the object. We never actually look at the value, but only want
// to know if the property exists.
JS('', r'#.fixed$length = Array', list);
JS('', r'#.immutable$list = Array', list);
}
checkMutable(reason) {

View file

@ -14,7 +14,7 @@ void expectUOE(f()) {
Expect.throws(f, (e) => e is UnsupportedError);
}
testImmutable(var list) {
testImmutable(List list) {
expectUOE(() {
list.setRange(0, 0, const []);
});

View file

@ -87,17 +87,12 @@ apply2_test: RuntimeError # Issue 29921
apply3_test: RuntimeError # Issue 29921
big_integer_arith_vm_test: RuntimeError # Issue 30170
big_integer_parsed_arith_vm_test: RuntimeError # Issue 29921
const_list_literal_test: RuntimeError # Issue 29921
const_list_remove_range_test: RuntimeError # Issue 29921
const_list_set_range_test: RuntimeError # Issue 29921
compare_to2_test: RuntimeError # Issue 30170
date_time10_test: RuntimeError # Issue 29921
growable_list_test: RuntimeError # Issue 29921
hash_set_test/01: RuntimeError # Issue 29921
iterable_reduce_test/none: RuntimeError
iterable_to_list_test/*: RuntimeError
list_test/none: RuntimeError
list_test/01: RuntimeError
list_concurrent_modify_test: RuntimeError # DDC uses ES6 array iterators so it does not issue this
nan_infinity_test/01: RuntimeError # Issue 29921
null_nosuchmethod_test: RuntimeError # Issue 29921
main_test: RuntimeError # Issue 29921
@ -181,7 +176,9 @@ integer_to_string_test/01: RuntimeError
iterable_to_set_test: RuntimeError # is-checks do not implement strong mode type system
int_parse_radix_test/01: RuntimeError
int_parse_radix_test/02: RuntimeError
list_test/*: RuntimeError # dart2js doesn't implement strong mode covariance checks
string_split_test: RuntimeError # does not return List<String>
list_concurrent_modify_test: RuntimeError # dart2js does not fully implement these
[ $compiler == dart2js && $runtime == drt && $csp && $minified ]
core_runtime_types_test: Pass, Fail # Issue 27913
@ -192,8 +189,6 @@ iterable_element_at_test/static: MissingCompileTimeError
[ $compiler == dartdevc && $runtime != none ]
error_stack_trace_test/nullThrown: RuntimeError # .stackTrace not present for exception caught from 'throw null;'
list_fill_range_test: RuntimeError # Issue 29921
list_insert_test: RuntimeError # Issue 29921
list_removeat_test: RuntimeError # Issue 29921
list_replace_range_test: RuntimeError # Issue 29921
list_set_all_test: RuntimeError # Issue 29921
@ -327,6 +322,7 @@ big_integer_parsed_mul_div_vm_test: Pass, Timeout # --no_intrinsify
string_trimlr_test/02: RuntimeError # Issue 29060
iterable_to_set_test: RuntimeError # is-checks do not implement strong mode type system
[ $compiler == precompiler || $compiler == app_jit ]
string_trimlr_test/02: RuntimeError # Issue 29060
@ -464,6 +460,7 @@ string_case_test/01: RuntimeError
[ $runtime == vm || $runtime == dart_precompiled ]
string_split_test: RuntimeError # does not return List<String>
list_test/*: RuntimeError # VM doesn't implement strong mode covariance checks
[ $compiler == dart2js && $dart2js_with_kernel && $host_checked ]
apply2_test: Crash

View file

@ -165,20 +165,4 @@ void testConcurrentModification() {
l.addAll(ci);
Expect.listEquals(new List.generate(500, (x) => x), l, "cm6");
}
{
// Adding to yourself.
var l = [1];
Expect.throws(() {
l.addAll(l);
}, (e) => e is ConcurrentModificationError, "cm7");
}
{
// Adding to yourself.
var l = [1, 2, 3];
Expect.throws(() {
l.addAll(l);
}, (e) => e is ConcurrentModificationError, "cm8");
}
}

View file

@ -0,0 +1,100 @@
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:collection";
import "dart:typed_data";
import "package:expect/expect.dart";
void main() {
// Growable lists. Initial length 0.
testConcurrentModification(new List());
testConcurrentModification(new List<int>().toList());
testConcurrentModification(new List<int>(0).toList());
testConcurrentModification(new List.filled(0, null, growable: true));
testConcurrentModification([]);
testConcurrentModification(new List.from(const []));
testConcurrentModification(new MyList([]));
testConcurrentModification(new MyList<int>([]).toList());
testConcurrentModification(new Uint8List(0).toList());
testConcurrentModification(new Int8List(0).toList());
testConcurrentModification(new Uint16List(0).toList());
testConcurrentModification(new Int16List(0).toList());
testConcurrentModification(new Uint32List(0).toList());
testConcurrentModification(new Int32List(0).toList());
testConcurrentAddSelf([]);
testConcurrentAddSelf([1, 2, 3]);
}
void testConcurrentModification(List<int> list) {
// add, removeLast.
list.clear();
list.addAll([1, 2, 3, 2, 7, 9, 9, 7, 2, 3, 2, 1]);
// Operations that change the length cause ConcurrentModificationError.
void testModification(action()) {
testIterator(int when) {
list.length = 4;
list.setAll(0, [0, 1, 2, 3]);
Expect.throws(() {
for (var element in list) {
if (element == when) action();
}
}, (e) => e is ConcurrentModificationError);
}
testForEach(int when) {
list.length = 4;
list.setAll(0, [0, 1, 2, 3]);
Expect.throws(() {
list.forEach((var element) {
if (element == when) action();
});
}, (e) => e is ConcurrentModificationError);
}
// Test the change at different points of the iteration.
testIterator(0);
testIterator(1);
testIterator(3);
testForEach(0);
testForEach(1);
testForEach(3);
}
testModification(() => list.add(5));
testModification(() => list.addAll([5, 6]));
testModification(() => list.removeLast());
for (int i = 0; i < 4; i++) {
testModification(() => list.remove(i));
testModification(() => list.removeAt(i));
testModification(() => list.removeWhere((x) => x == i));
testModification(() => list.retainWhere((x) => x != i));
testModification(() => list.insert(i, 5));
testModification(() => list.insertAll(i, [5, 6]));
testModification(() => list.removeRange(i, i + 1));
testModification(() => list.replaceRange(i, i + 1, [5, 6]));
}
}
testConcurrentAddSelf(List list) {
Expect.throws(() {
list.addAll(list);
}, (e) => e is ConcurrentModificationError, "testConcurrentAddSelf($list)");
}
class MyList<E> extends ListBase<E> {
List<E> _source;
MyList(this._source);
int get length => _source.length;
void set length(int length) {
_source.length = length;
}
E operator [](int index) => _source[index];
void operator []=(int index, E value) {
_source[index] = value;
}
}

View file

@ -22,22 +22,22 @@ void main() {
testTypedList(new Int32List(4).toList(growable: false));
// Fixed length lists, length 4.
testFixedLengthList(new List(4));
testFixedLengthList(new List(4).toList(growable: false));
testFixedLengthList((new List()..length = 4).toList(growable: false));
testFixedLengthList(<T>() => new List(4));
testFixedLengthList(<T>() => new List<T>(4).toList(growable: false));
testFixedLengthList(<T>() => (new List<T>()..length = 4).toList(growable: false));
// ListBase implementation of List.
testFixedLengthList(new MyFixedList(new List(4)));
testFixedLengthList(new MyFixedList(new List(4)).toList(growable: false));
testFixedLengthList(<T>() => new MyFixedList(new List(4)));
testFixedLengthList(<T>() => new MyFixedList<T>(new List(4)).toList(growable: false));
// Growable lists. Initial length 0.
testGrowableList(new List());
testGrowableList(new List().toList());
testGrowableList(new List(0).toList());
testGrowableList(new List.filled(0, null, growable: true));
testGrowableList([]);
testGrowableList((const []).toList());
testGrowableList(new MyList([]));
testGrowableList(new MyList([]).toList());
testGrowableList(<T>() => new List());
testGrowableList(<T>() => new List<T>().toList());
testGrowableList(<T>() => new List<T>(0).toList());
testGrowableList(<T>() => new List.filled(0, null, growable: true));
testGrowableList(<T>() => []);
testGrowableList(<T>() => new List.from(const []));
testGrowableList(<T>() => new MyList([]));
testGrowableList(<T>() => new MyList<T>([]).toList());
testTypedGrowableList(new Uint8List(0).toList());
testTypedGrowableList(new Int8List(0).toList());
@ -140,7 +140,7 @@ void testLength(int length, List list) {
(length != 0 ? Expect.isTrue : Expect.isFalse)(list.isNotEmpty);
}
void testTypedLengthInvariantOperations(List list) {
void testTypedLengthInvariantOperations(List<int> list) {
// length
Expect.equals(list.length, 4);
// operators [], []=.
@ -257,8 +257,7 @@ void testTypedLengthInvariantOperations(List list) {
// so it's moved to the method below for now.
}
void testLengthInvariantOperations(List list) {
testTypedLengthInvariantOperations(list);
void testUntypedListTests(List list) {
// Tests that need untyped lists.
list.setAll(0, [0, 1, 2, 3]);
Expect.equals(-1, list.indexOf(100));
@ -275,6 +274,14 @@ void testLengthInvariantOperations(List list) {
list[3] = 3;
Expect.equals(-1, list.indexOf(100));
Expect.equals(-1, list.lastIndexOf(100));
}
void testLengthInvariantOperations(List<int> list) {
testTypedLengthInvariantOperations(list);
Expect.throws(() {
testUntypedListTests(list);
}, (e) => e is TypeError, 'List<int> cannot store non-ints');
// Argument errors on bad indices. List is still [0, 1, 2, 3].
testArgumentError(action()) {
@ -311,21 +318,22 @@ void testLengthInvariantOperations(List list) {
testArgumentError(() => list.fillRange(4, 2));
}
void testTypedList(List list) {
void testTypedList(List<int> list) {
testTypedLengthInvariantOperations(list);
testCannotChangeLength(list);
}
void testFixedLengthList(List list) {
testLengthInvariantOperations(list);
testCannotChangeLength(list);
void testFixedLengthList(List<T> Function<T>() createList) {
testLengthInvariantOperations(createList());
testCannotChangeLength(createList());
testUntypedListTests(createList());
}
void testCannotChangeLength(List list) {
void testCannotChangeLength(List<int> list) {
isUnsupported(action()) {
Expect.throws(action, (e) => e is UnsupportedError);
}
list.setAll(0, [0, 1, 2, 3]);
isUnsupported(() => list.add(0));
isUnsupported(() => list.addAll([0]));
isUnsupported(() => list.removeLast());
@ -338,7 +346,7 @@ void testCannotChangeLength(List list) {
isUnsupported(() => list.replaceRange(0, 1, []));
}
void testTypedGrowableList(List list) {
void testTypedGrowableList(List<int> list) {
testLength(0, list);
// set length.
list.length = 4;
@ -349,18 +357,24 @@ void testTypedGrowableList(List list) {
testGrowableListOperations(list);
}
void testGrowableList(List list) {
void testGrowableList(List<T> Function<T>() createList) {
List<int> list = createList();
testLength(0, list);
// set length.
list.length = 4;
testLength(4, list);
testLengthInvariantOperations(list);
testGrowableListOperations(list);
List listDynamic = createList();
testLength(0, listDynamic);
listDynamic.length = 4;
testLength(4, listDynamic);
testUntypedListTests(listDynamic);
}
void testGrowableListOperations(List list) {
void testGrowableListOperations(List<int> list) {
// add, removeLast.
list.clear();
testLength(0, list);
@ -475,51 +489,6 @@ void testGrowableListOperations(List list) {
list.replaceRange(6, 8, []);
Expect.listEquals([1, 2, 6, 6, 5, 0, 2, 3, 2, 1], list);
// Operations that change the length cause ConcurrentModificationError.
void testConcurrentModification(action()) {
testIterator(int when) {
list.length = 4;
list.setAll(0, [0, 1, 2, 3]);
Expect.throws(() {
for (var element in list) {
if (element == when) action();
}
}, (e) => e is ConcurrentModificationError);
}
testForEach(int when) {
list.length = 4;
list.setAll(0, [0, 1, 2, 3]);
Expect.throws(() {
list.forEach((var element) {
if (element == when) action();
});
}, (e) => e is ConcurrentModificationError);
}
// Test the change at different points of the iteration.
testIterator(0);
testIterator(1);
testIterator(3);
testForEach(0);
testForEach(1);
testForEach(3);
}
testConcurrentModification(() => list.add(5));
testConcurrentModification(() => list.addAll([5, 6]));
testConcurrentModification(() => list.removeLast());
for (int i = 0; i < 4; i++) {
testConcurrentModification(() => list.remove(i));
testConcurrentModification(() => list.removeAt(i));
testConcurrentModification(() => list.removeWhere((x) => x == i));
testConcurrentModification(() => list.retainWhere((x) => x != i));
testConcurrentModification(() => list.insert(i, 5));
testConcurrentModification(() => list.insertAll(i, [5, 6]));
testConcurrentModification(() => list.removeRange(i, i + 1));
testConcurrentModification(() => list.replaceRange(i, i + 1, [5, 6]));
}
// Any operation that doesn't change the length should be safe for iteration.
testSafeConcurrentModification(action()) {
list.length = 4;

View file

@ -710,7 +710,6 @@ issue23244_test: RuntimeError # Issue 29920
lazy_static3_test: RuntimeError # Issue 29920
left_shift_test: RuntimeError # Issue 29920
list_is_test: RuntimeError # Issue 29920
list_literal3_test: RuntimeError # Issue 29920
main_test/03: MissingRuntimeError # Issue 29920
map_literal10_test: RuntimeError # Issue 29920
map_literal7_test: RuntimeError # Issue 29920

View file

@ -271,8 +271,6 @@ mirrors/library_enumeration_deferred_loading_test: RuntimeError # Issue 29922
mirrors/library_metadata_test: RuntimeError # Issue 29922
mirrors/library_uri_io_test: RuntimeError # Issue 29922
mirrors/library_uri_package_test: RuntimeError # Issue 29922
mirrors/list_constructor_test/01: RuntimeError # Issue 29922
mirrors/list_constructor_test/none: RuntimeError # Issue 29922
mirrors/local_function_is_static_test: RuntimeError # Issue 29922
mirrors/local_isolate_test: RuntimeError # Issue 29922
mirrors/metadata_constructed_constant_test: RuntimeError # Issue 29922