Version 2.15.0-268.8.beta
* Cherry-pick 243fb5c383416fb82b6852a83ad3c4449683858a to beta
* Cherry-pick 1ca06b953112612cd6250d2b11bb776688ec8e1a to beta
* Cherry-pick b8e419d16b10be073f080192a55aaf598321ca10 to beta
* Cherry-pick dc7e082336fc7a9c639c1d62edf4cc0ebb57ea51 to beta
* Cherry-pick 6225f46b760a5c1eebff9c4dce621e93192a886c to beta
* Cherry-pick b97737c3b53a6c043a6a1b97e61b8670ded75629 to beta
* Cherry-pick 109c317ac12a43d8a25b14bca9805276bf8c84ee to beta
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 03de374..c611f3b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -429,9 +429,12 @@
#### `dart:js_util`
-- The `js_util` methods `getProperty`, `setProperty`, `callMethod`,
- `callConstructor`, and `newObject` now support a generic type argument
- to specify the return type.
+- The `js_util` methods `setProperty`, `callMethod`, and `callConstructor` have
+ been optimized to remove checks on arguments when the checks can be elided.
+ Also, those methods, along with `getProperty` and `newObject`, now support a
+ generic type argument to specify a return type. These two changes make simple
+ `js_util` usage, like reading and writing primitive properties or calling
+ methods with simple arguments, have zero overhead.
#### `dart:web_sql`
@@ -592,6 +595,14 @@
`dart pub get/upgrade/downgrade/add/remove` that will result in the `example/`
folder dependencies to be updated after operating in the current directory.
+### Other libraries
+
+#### `package:js`
+
+- Extensions on JS interop or native `dart:html` classes can now declare
+ members as `external`. These members are equivalent to regular extension
+ members that use `js_util` to expose the underlying JavaScript.
+
## 2.14.4 - 2021-10-14
This is a patch release that fixes:
diff --git a/DEPS b/DEPS
index 18e0f7a..cb395cc 100644
--- a/DEPS
+++ b/DEPS
@@ -107,7 +107,7 @@
"dart_style_rev": "08b0294d0a500d5c02168ef57dcb8868d0c3cb48",
"dartdoc_rev" : "520e64977de7a87b2fd5be56e5c2e1a58d55bdad",
- "devtools_rev" : "8881a7caa9067471008a8e00750b161f53cdb843",
+ "devtools_rev" : "3a2f570813200e6dee141f3e7a9edcae5f31c2e8",
"jsshell_tag": "version:88.0",
"ffi_rev": "4dd32429880a57b64edaf54c9d5af8a9fa9a4ffb",
"fixnum_rev": "16d3890c6dc82ca629659da1934e412292508bba",
@@ -139,7 +139,7 @@
"pool_rev": "7abe634002a1ba8a0928eded086062f1307ccfae",
"process_rev": "56ece43b53b64c63ae51ec184b76bd5360c28d0b",
"protobuf_rev": "c1eb6cb51af39ccbaa1a8e19349546586a5c8e31",
- "pub_rev": "0035a40f25d027130c0314571da53ffafc6d973b",
+ "pub_rev": "96404e0749864c9fbf8b12e1d424e8078809e00a",
"pub_semver_rev": "a43ad72fb6b7869607581b5fedcb186d1e74276a",
"root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
"rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
index dc0030a..ac9fbb1 100644
--- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart
+++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
@@ -632,39 +632,31 @@
@override
DartObjectImpl? visitConstructorReference(ConstructorReference node) {
- var constructorTearoffResult = DartObjectImpl(
+ var constructorFunctionType = node.typeOrThrow as FunctionType;
+ var classType = constructorFunctionType.returnType as InterfaceType;
+ var typeArguments = classType.typeArguments;
+ // The result is already instantiated during resolution;
+ // [_dartObjectComputer.typeInstantiate] is unnecessary.
+ var typeElement =
+ node.constructorName.type2.name.staticElement as TypeDefiningElement;
+
+ TypeAliasElement? viaTypeAlias;
+ if (typeElement is TypeAliasElementImpl) {
+ if (constructorFunctionType.typeFormals.isNotEmpty &&
+ !typeElement.isProperRename()) {
+ // The type alias is not a proper rename of the aliased class, so
+ // the constructor tear-off is distinct from the associated
+ // constructor function of the aliased class.
+ viaTypeAlias = typeElement;
+ }
+ }
+
+ return DartObjectImpl(
typeSystem,
node.typeOrThrow,
- FunctionState(node.constructorName.staticElement),
+ FunctionState(node.constructorName.staticElement,
+ typeArguments: typeArguments, viaTypeAlias: viaTypeAlias),
);
- var typeArgumentList = node.constructorName.type2.typeArguments;
- if (typeArgumentList == null) {
- return constructorTearoffResult;
- } else {
- var typeArguments = <DartType>[];
- for (var typeArgument in typeArgumentList.arguments) {
- var object = typeArgument.accept(this);
- if (object == null) {
- return null;
- }
- var typeArgumentType = object.toTypeValue();
- if (typeArgumentType == null) {
- return null;
- }
- // TODO(srawlins): Test type alias types (`typedef i = int`) used as
- // type arguments. Possibly change implementation based on
- // canonicalization rules.
- typeArguments.add(typeArgumentType);
- }
- // The result is already instantiated during resolution;
- // [_dartObjectComputer.typeInstantiate] is unnecessary.
- return DartObjectImpl(
- typeSystem,
- node.typeOrThrow,
- FunctionState(node.constructorName.staticElement,
- typeArguments: typeArguments),
- );
- }
}
@override
diff --git a/pkg/analyzer/lib/src/dart/constant/value.dart b/pkg/analyzer/lib/src/dart/constant/value.dart
index 227abea..094b8b6 100644
--- a/pkg/analyzer/lib/src/dart/constant/value.dart
+++ b/pkg/analyzer/lib/src/dart/constant/value.dart
@@ -920,11 +920,14 @@
List<DartType> typeArguments,
) {
var functionState = _state as FunctionState;
- var element = functionState._element;
return DartObjectImpl(
typeSystem,
type,
- FunctionState(element, typeArguments: typeArguments),
+ FunctionState(
+ functionState._element,
+ typeArguments: typeArguments,
+ viaTypeAlias: functionState._viaTypeAlias,
+ ),
);
}
@@ -1245,10 +1248,22 @@
final List<DartType>? _typeArguments;
+ /// The type alias which was referenced when tearing off a constructor,
+ /// if this function is a constructor tear-off, referenced via a type alias,
+ /// and the type alias is not a proper rename for the class, and the
+ /// constructor tear-off is generic, so the tear-off cannot be considered
+ /// equivalent to tearing off the associated constructor function of the
+ /// aliased class.
+ ///
+ /// Otherwise null.
+ final TypeDefiningElement? _viaTypeAlias;
+
/// Initialize a newly created state to represent the function with the given
/// [element].
- FunctionState(this._element, {List<DartType>? typeArguments})
- : _typeArguments = typeArguments;
+ FunctionState(this._element,
+ {List<DartType>? typeArguments, TypeDefiningElement? viaTypeAlias})
+ : _typeArguments = typeArguments,
+ _viaTypeAlias = viaTypeAlias;
@override
int get hashCode => _element == null ? 0 : _element.hashCode;
@@ -1272,6 +1287,9 @@
if (typeArguments.length != otherTypeArguments.length) {
return false;
}
+ if (_viaTypeAlias != object._viaTypeAlias) {
+ return false;
+ }
for (var i = 0; i < typeArguments.length; i++) {
if (typeArguments[i] != otherTypeArguments[i]) {
return false;
@@ -1309,6 +1327,9 @@
if (element?.declaration != otherElement?.declaration) {
return BoolState.FALSE_STATE;
}
+ if (_viaTypeAlias != rightOperand._viaTypeAlias) {
+ return BoolState.FALSE_STATE;
+ }
var typeArguments = _typeArguments;
var otherTypeArguments = rightOperand._typeArguments;
if (typeArguments == null || otherTypeArguments == null) {
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 549136f..d5bc77f 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -5582,6 +5582,37 @@
}
}
+ /// Returns whether this alias is a "proper rename" of [aliasedClass], as
+ /// defined in the constructor-tearoffs specification.
+ bool isProperRename() {
+ var aliasedType_ = aliasedType;
+ if (aliasedType_ is! InterfaceType) {
+ return false;
+ }
+ var aliasedClass = aliasedType_.element;
+ var typeArguments = aliasedType_.typeArguments;
+ var typeParameterCount = typeParameters.length;
+ if (typeParameterCount != aliasedClass.typeParameters.length) {
+ return false;
+ }
+ if (typeParameterCount != typeArguments.length) {
+ return false;
+ }
+ for (var i = 0; i < typeParameterCount; i++) {
+ var bound = typeParameters[i].bound ?? library.typeProvider.dynamicType;
+ var aliasedBound = aliasedClass.typeParameters[i].bound ??
+ library.typeProvider.dynamicType;
+ if (!library.typeSystem.isSubtypeOf(bound, aliasedBound) ||
+ !library.typeSystem.isSubtypeOf(aliasedBound, bound)) {
+ return false;
+ }
+ if (typeParameters[i] != typeArguments[i].element) {
+ return false;
+ }
+ }
+ return true;
+ }
+
void setLinkedData(Reference reference, ElementLinkedData linkedData) {
this.reference = reference;
reference.element = this;
diff --git a/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
index ceecf70..4ef8323 100644
--- a/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
@@ -123,6 +123,7 @@
constructorName.staticElement = constructorElement.declaration;
constructorName.name?.staticElement = constructorElement.declaration;
node.staticType = inferred;
+ // The NamedType child of `constructorName` doesn't have a static type.
constructorName.type2.type = null;
}
} else {
@@ -132,6 +133,7 @@
} else {
node.staticType = constructorElement.type;
}
+ // The NamedType child of `constructorName` doesn't have a static type.
constructorName.type2.type = null;
}
}
diff --git a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
index f544ef9..26f0785 100644
--- a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
@@ -28,6 +28,126 @@
@reflectiveTest
class ConstantVisitorTest extends ConstantVisitorTestSupport
with ConstantVisitorTestCases {
+ test_identical_constructorReference_aliasIsNotGeneric() async {
+ await resolveTestCode('''
+class C<T> {}
+typedef MyC = C<int>;
+const a = identical(MyC.new, C<int>.new);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(true),
+ );
+ }
+
+ test_identical_constructorReference_aliasIsNotProperRename_differentBound() async {
+ await resolveTestCode('''
+class C<T> {}
+typedef MyC<T extends num> = C<T>;
+const a = identical(MyC.new, C.new);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(false),
+ );
+ }
+
+ test_identical_constructorReference_aliasIsNotProperRename_differentCount() async {
+ await resolveTestCode('''
+class C<T, U> {}
+typedef MyC<T> = C<T, int>;
+const a = identical(MyC.new, C.new);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(false),
+ );
+ }
+
+ test_identical_constructorReference_aliasIsNotProperRename_differentOrder() async {
+ await resolveTestCode('''
+class C<T, U> {}
+typedef MyC<T, U> = C<U, T>;
+const a = identical(MyC.new, C.new);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(false),
+ );
+ }
+
+ test_identical_constructorReference_aliasIsNotProperRename_instantiated() async {
+ await resolveTestCode('''
+class C<T> {}
+typedef MyC<T extends num> = C<T>;
+const a = identical(MyC<int>.new, C<int>.new);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(true),
+ );
+ }
+
+ test_identical_constructorReference_aliasIsNotProperRename_mixedInstantiations() async {
+ await resolveTestCode('''
+class C<T> {}
+typedef MyC<T extends num> = C<T>;
+const a = identical(MyC<int>.new, (MyC.new)<int>);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(false),
+ );
+ }
+
+ test_identical_constructorReference_aliasIsProperRename_instantiated() async {
+ await resolveTestCode('''
+class C<T> {}
+typedef MyC<T> = C<T>;
+const a = identical(MyC<int>.new, MyC<int>.new);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(true),
+ );
+ }
+
+ test_identical_constructorReference_aliasIsProperRename_mixedInstantiations() async {
+ await resolveTestCode('''
+class C<T> {}
+typedef MyC<T> = C<T>;
+const a = identical(MyC<int>.new, (MyC.new)<int>);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(true),
+ );
+ }
+
+ test_identical_constructorReference_aliasIsProperRename_mutualSubtypes() async {
+ await resolveTestCode('''
+class C<T> {}
+typedef MyC<T extends Object?> = C<T>;
+const a = identical(MyC<int>.new, MyC<int>.new);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(true),
+ );
+ }
+
+ test_identical_constructorReference_aliasIsProperRename_uninstantiated() async {
+ await resolveTestCode('''
+class C<T> {}
+typedef MyC<T> = C<T>;
+const a = identical(MyC.new, MyC.new);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(true),
+ );
+ }
+
test_identical_constructorReference_explicitTypeArgs_differentClasses() async {
await resolveTestCode('''
class C<T> {}
@@ -76,6 +196,19 @@
);
}
+ test_identical_constructorReference_inferredTypeArgs_sameElement() async {
+ await resolveTestCode('''
+class C<T> {}
+const C<int> Function() c1 = C.new;
+const c2 = C<int>.new;
+const a = identical(c1, c2);
+''');
+ expect(
+ _evaluateConstant('a'),
+ _boolValue(true),
+ );
+ }
+
test_identical_constructorReference_notInstantiated_differentClasses() async {
await resolveTestCode('''
class C<T> {}
diff --git a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
index 48f946f..0b2478b 100644
--- a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
@@ -85,6 +85,26 @@
);
}
+ test_alias_generic_uninstantiated_const() async {
+ await assertNoErrorsInCode('''
+class A<T, U> {
+ const A.foo();
+}
+typedef TA<T, U> = A<U, T>;
+
+const a = TA.foo;
+''');
+
+ var classElement = findElement.class_('A');
+ assertConstructorReference(
+ findNode.constructorReference('TA.foo;'),
+ classElement.getNamedConstructor('foo'),
+ classElement,
+ 'A<U, T> Function<T, U>()',
+ expectedTypeNameElement: findElement.typeAlias('TA'),
+ );
+ }
+
test_alias_generic_unnamed() async {
await assertNoErrorsInCode('''
class A<T> {
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index bed3b36..6a27317 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -150,13 +150,12 @@
}
}
- void assertConstructorElement(
- ConstructorElement? expected, ConstructorElement? actual) {
- if (expected is ConstructorMember && actual is ConstructorMember) {
- expect(expected.declaration, same(actual.declaration));
+ void assertConstructorElement(ConstructorElement? actual, Object? expected) {
+ if (actual is ConstructorMember && expected is ConstructorMember) {
+ expect(actual.declaration, same(expected.declaration));
// TODO(brianwilkerson) Compare the type arguments of the two members.
} else {
- expect(expected, same(actual));
+ assertElement(actual, expected);
}
}
@@ -168,12 +167,11 @@
PrefixElement? expectedPrefix,
Element? expectedTypeNameElement,
}) {
- var actualConstructorElement = getNodeElement(node) as ConstructorElement?;
var actualConstructorName = node.constructorName.name;
if (actualConstructorName != null) {
assertConstructorElement(
actualConstructorName.staticElement as ConstructorElement?,
- actualConstructorElement,
+ expectedConstructorElement,
);
}
@@ -182,8 +180,13 @@
var namedType = node.constructorName.type2;
expectedTypeNameElement ??= expectedClassElement;
- assertNamedType(namedType, expectedTypeNameElement, null,
- expectedPrefix: expectedPrefix);
+ assertNamedType(
+ namedType, expectedTypeNameElement,
+ // The [NamedType] child node of the [ConstructorName] should not have a
+ // static type.
+ null,
+ expectedPrefix: expectedPrefix,
+ );
}
void assertConstructors(ClassElement class_, List<String> expected) {
diff --git a/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart b/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart
index 6cc3be4..169a1b9 100644
--- a/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart
+++ b/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart
@@ -233,6 +233,7 @@
await testMapRehash();
await testMapRehash2();
await testMapRehash3();
+ await testMapRehash4();
await testSetRehash();
await testSetRehash2();
@@ -532,6 +533,27 @@
Expect.equals(before + 1, after);
}
+ Future testMapRehash4() async {
+ // This is a regression test for http://dartbug.com/47598
+ print('testMapRehash4');
+
+ // This map doesn't need rehashing
+ final graph = {'a': 1, 'b': 2, 'c': 3}..remove('b');
+ final graphCopy = (await sendReceive(graph) as Map);
+ Expect.equals(2, graphCopy.length);
+ Expect.equals(1, graphCopy['a']);
+ Expect.equals(3, graphCopy['c']);
+
+ // This map will need re-hashing due to usage of a key that has a
+ // user-defined get:hashCode.
+ final graph2 = {'a': 1, 'b': 2, const HashIncrementer(): 3}..remove('b');
+ final graph2Copy = (await sendReceive(graph2) as Map);
+ Expect.equals(2, graph2Copy.length);
+ Expect.equals(1, graph2Copy['a']);
+ --HashIncrementer.counter;
+ Expect.equals(3, graph2Copy[const HashIncrementer()]);
+ }
+
Future testSetRehash() async {
print('testSetRehash');
final obj = Object();
diff --git a/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart b/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
index 79de0c5..1a1512d 100644
--- a/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
+++ b/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
@@ -235,6 +235,7 @@
await testMapRehash();
await testMapRehash2();
await testMapRehash3();
+ await testMapRehash4();
await testSetRehash();
await testSetRehash2();
@@ -534,6 +535,27 @@
Expect.equals(before + 1, after);
}
+ Future testMapRehash4() async {
+ // This is a regression test for http://dartbug.com/47598
+ print('testMapRehash4');
+
+ // This map doesn't need rehashing
+ final graph = {'a': 1, 'b': 2, 'c': 3}..remove('b');
+ final graphCopy = (await sendReceive(graph) as Map);
+ Expect.equals(2, graphCopy.length);
+ Expect.equals(1, graphCopy['a']);
+ Expect.equals(3, graphCopy['c']);
+
+ // This map will need re-hashing due to usage of a key that has a
+ // user-defined get:hashCode.
+ final graph2 = {'a': 1, 'b': 2, const HashIncrementer(): 3}..remove('b');
+ final graph2Copy = (await sendReceive(graph2) as Map);
+ Expect.equals(2, graph2Copy.length);
+ Expect.equals(1, graph2Copy['a']);
+ --HashIncrementer.counter;
+ Expect.equals(3, graph2Copy[const HashIncrementer()]);
+ }
+
Future testSetRehash() async {
print('testSetRehash');
final obj = Object();
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index bb715ff..d348aec 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -1148,8 +1148,9 @@
auto key_value_pairs = untagged_data->data();
for (intptr_t i = 0; i < length; i += one_for_set_two_for_map) {
ObjectPtr key = key_value_pairs[i].Decompress(Base::heap_base_);
+ const bool is_deleted_entry = key == data;
if (key->IsHeapObject()) {
- if (MightNeedReHashing(key)) {
+ if (!is_deleted_entry && MightNeedReHashing(key)) {
needs_rehashing = true;
break;
}
@@ -1171,6 +1172,7 @@
if (needs_rehashing) {
to_untagged->hash_mask_ = Smi::New(0);
to_untagged->index_ = TypedData::RawCast(Object::null());
+ to_untagged->deleted_keys_ = Smi::New(0);
Base::EnqueueObjectToRehash(to);
}
@@ -1185,15 +1187,15 @@
Base::StoreCompressedPointersNoBarrier(
from, to, OFFSET_OF(UntaggedLinkedHashBase, hash_mask_),
OFFSET_OF(UntaggedLinkedHashBase, hash_mask_));
+ Base::StoreCompressedPointersNoBarrier(
+ from, to, OFFSET_OF(UntaggedLinkedHashMap, deleted_keys_),
+ OFFSET_OF(UntaggedLinkedHashMap, deleted_keys_));
}
Base::ForwardCompressedPointer(from, to,
OFFSET_OF(UntaggedLinkedHashBase, data_));
Base::StoreCompressedPointersNoBarrier(
from, to, OFFSET_OF(UntaggedLinkedHashBase, used_data_),
OFFSET_OF(UntaggedLinkedHashBase, used_data_));
- Base::StoreCompressedPointersNoBarrier(
- from, to, OFFSET_OF(UntaggedLinkedHashMap, deleted_keys_),
- OFFSET_OF(UntaggedLinkedHashMap, deleted_keys_));
}
void CopyLinkedHashMap(typename Types::LinkedHashMap from,
diff --git a/sdk/lib/_internal/vm/lib/compact_hash.dart b/sdk/lib/_internal/vm/lib/compact_hash.dart
index 14fbf46..f9b905b 100644
--- a/sdk/lib/_internal/vm/lib/compact_hash.dart
+++ b/sdk/lib/_internal/vm/lib/compact_hash.dart
@@ -372,6 +372,7 @@
_hashMask = _HashBase._indexSizeToHashMask(_index.length);
final int tmpUsed = _usedData;
_usedData = 0;
+ _deletedKeys = 0;
for (int i = 0; i < tmpUsed; i += 2) {
final key = _data[i];
if (!_HashBase._isDeleted(_data, key)) {
diff --git a/sdk/lib/isolate/isolate.dart b/sdk/lib/isolate/isolate.dart
index d439fd4..438e967 100644
--- a/sdk/lib/isolate/isolate.dart
+++ b/sdk/lib/isolate/isolate.dart
@@ -563,9 +563,9 @@
/// This operation is potentially dangerous and should be used judiciously.
/// The isolate stops operating *immediately*. It throws if the optional
/// [message] does not adhere to the limitations on what can be sent from one
- /// isolate to another. It also throws if a [finalMessagePort] is associated
- /// with an isolate spawned outside of current isolate group, spawned via
- /// [spawnUri].
+ /// isolate to another (see [SendPort.send] for more details). It also throws
+ /// if a [finalMessagePort] is associated with an isolate spawned outside of
+ /// current isolate group, spawned via [spawnUri].
///
/// If successful, a call to this method does not return. Pending `finally`
/// blocks are not executed, control flow will not go back to the event loop,
@@ -575,9 +575,9 @@
/// code will run in the isolate.)
///
/// If [finalMessagePort] is provided, and the [message] can be sent through
- /// it, then the message is sent through that port as the final operation of
- /// the current isolate. The isolate terminates immediately after
- /// that [SendPort.send] call returns.
+ /// it (see [SendPort.send] for more details), then the message is sent
+ /// through that port as the final operation of the current isolate. The
+ /// isolate terminates immediately after that [SendPort.send] call returns.
///
/// If the port is a native port -- one provided by [ReceivePort.sendPort] or
/// [RawReceivePort.sendPort] -- the system may be able to send this final
@@ -599,9 +599,10 @@
/// when sent.
abstract class SendPort implements Capability {
/// Sends an asynchronous [message] through this send port, to its
- /// corresponding `ReceivePort`.
+ /// corresponding [ReceivePort].
///
- /// The content of [message] can be:
+ /// The transitive object graph of [message] can contain the following
+ /// objects:
/// - [Null]
/// - [bool]
/// - [int]
@@ -612,14 +613,34 @@
/// - [SendPort]
/// - [Capability]
///
- /// In the special circumstances when two isolates share the same code and are
- /// running in the same process (e.g. isolates created via [Isolate.spawn]),
- /// it is also possible to send object instances (which would be copied in the
- /// process).
+ /// If the sender and receiver isolate share the same code (e.g. isolates
+ /// created via [Isolate.spawn]), the transitive object graph of [message] can
+ /// contain any object, with the following exceptions:
///
- /// The send happens immediately and doesn't block. The corresponding receive
+ /// - Objects with native resources (subclasses of e.g.
+ /// `NativeFieldWrapperClass1`). A [Socket] object for example referrs
+ /// internally to objects that have native resources attached and can
+ /// therefore not be sent.
+ /// - [ReceivePort]
+ /// - [DynamicLibrary]
+ /// - [Pointer]
+ /// - [UserTag]
+ /// - `MirrorReference`
+ ///
+ /// Apart from those exceptions any object can be sent. Objects that are
+ /// identified as immutable (e.g. strings) will be shared whereas all other
+ /// objects will be copied.
+ ///
+ /// The send happens immediately and may have a linear time cost to copy the
+ /// transtive object graph. The send itself doesn't block (i.e. doesn't wait
+ /// until the receiver has received the message). The corresponding receive
/// port can receive the message as soon as its isolate's event loop is ready
/// to deliver it, independently of what the sending isolate is doing.
+ ///
+ /// Note: Due to an implementation choice the Dart VM made for how closures
+ /// represent captured state, closures can currently capture more state than
+ /// they need, which can cause the transitive closure to be larger than
+ /// needed. Open bug to address this: http://dartbug.com/36983
void send(Object? message);
/// Tests whether [other] is a [SendPort] pointing to the same
diff --git a/tools/VERSION b/tools/VERSION
index d18afdd..53fe3f8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 15
PATCH 0
PRERELEASE 268
-PRERELEASE_PATCH 1
\ No newline at end of file
+PRERELEASE_PATCH 8
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 2facefd5..a960fa8 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -312,9 +312,11 @@
"xcodebuild/DebugX64/",
"xcodebuild/DebugX64C/",
"xcodebuild/DebugXARM64/",
+ "xcodebuild/DebugARM64/",
"xcodebuild/ProductX64/",
"xcodebuild/ProductX64C/",
"xcodebuild/ProductXARM64/",
+ "xcodebuild/ProductARM64/",
"xcodebuild/ReleaseIA32/",
"xcodebuild/ReleaseSIMARM/",
"xcodebuild/ReleaseSIMARM64/",
@@ -322,6 +324,7 @@
"xcodebuild/ReleaseX64/",
"xcodebuild/ReleaseX64C/",
"xcodebuild/ReleaseXARM64/",
+ "xcodebuild/ReleaseARM64/",
"pkg/",
"samples/",
"samples_2/",