Skip to content

Commit

Permalink
Version 3.4.0-215.0.dev
Browse files Browse the repository at this point in the history
Merge dad57a4 into dev
  • Loading branch information
Dart CI committed Mar 8, 2024
2 parents f01c949 + dad57a4 commit df116cf
Show file tree
Hide file tree
Showing 10 changed files with 399 additions and 49 deletions.
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ vars = {

# co19 is a cipd package automatically generated for each co19 commit.
# Use tests/co19/update.sh to update this hash.
"co19_rev": "867d139b3169fc131488e893ec1133dc98cc3aa0",
"co19_rev": "6f34e53fa54396f17bcffcd307528f714a099e03",

# The internal benchmarks to use. See go/dart-benchmarks-internal
"benchmarks_internal_rev": "a7c23b2422492dcc515d1ba4abe3609b50e2a139",
Expand Down
87 changes: 63 additions & 24 deletions pkg/vm/lib/transformations/mixin_deduplication.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:kernel/ast.dart';
import 'package:kernel/type_algebra.dart';

/// De-duplication of identical mixin applications.
void transformComponent(Component component) {
Expand Down Expand Up @@ -36,44 +37,82 @@ void transformComponent(Component component) {

class _DeduplicateMixinKey {
final Class _class;
_DeduplicateMixinKey(this._class);
_DeduplicateMixinKey(this._class) {
// Mixins applications were lowered to anonymous mixin application classes.
assert(_class.mixedInType == null);
assert(_class.isAnonymousMixin);
}

@override
bool operator ==(Object other) {
if (other is _DeduplicateMixinKey) {
final thisClass = _class;
final otherClass = other._class;
if (identical(thisClass, otherClass)) {
return true;
if (other is! _DeduplicateMixinKey) return false;

final thisClass = _class;
final otherClass = other._class;
if (identical(thisClass, otherClass)) {
return true;
}

// If the shape of the two mixin application classes don't match, return
// `false` quickly.
final thisSupertype = thisClass.supertype!;
final otherSupertype = otherClass.supertype!;
if (thisSupertype.classNode != otherSupertype.classNode) return false;

final thisParameters = thisClass.typeParameters;
final otherParameters = otherClass.typeParameters;
if (thisParameters.length != otherParameters.length) return false;

final thisImplemented = thisClass.implementedTypes;
final otherImplemented = otherClass.implementedTypes;
if (thisImplemented.length != otherImplemented.length) return false;

if (thisClass.enclosingLibrary.isNonNullableByDefault !=
otherClass.enclosingLibrary.isNonNullableByDefault) return false;

// Non generic classes can use equalty compares of supertypes.
if (thisParameters.isEmpty) {
if (thisSupertype != otherSupertype) return false;
if (!listEquals(thisImplemented, otherImplemented)) return false;
}

// Generic classes must translate type parameter usages from one class to
// the other.
final substitution = Substitution.fromMap({
for (int i = 0; i < otherParameters.length; ++i)
otherParameters[i]: TypeParameterType(
thisParameters[i],
otherParameters[i].bound.nullability == Nullability.nonNullable
? Nullability.nonNullable
: Nullability.undetermined),
});
if (thisSupertype != substitution.substituteSupertype(otherSupertype)) {
return false;
}
for (int i = 0; i < thisImplemented.length; ++i) {
if (thisImplemented[i] !=
substitution.substituteSupertype(otherImplemented[i])) {
return false;
}
// Do not deduplicate parameterized mixin applications.
if (thisClass.typeParameters.isNotEmpty ||
otherClass.typeParameters.isNotEmpty) {
}
for (int i = 0; i < thisParameters.length; ++i) {
if (thisParameters[i].bound !=
substitution.substituteType(otherParameters[i].bound)) {
return false;
}
// Deduplicate mixin applications with matching supertype, mixed-in type,
// implemented interfaces and NNBD mode (CFE may add extra signature
// members depending on the NNBD mode).
return thisClass.supertype == otherClass.supertype &&
thisClass.mixedInType == otherClass.mixedInType &&
listEquals(thisClass.implementedTypes, otherClass.implementedTypes) &&
thisClass.enclosingLibrary.isNonNullableByDefault ==
otherClass.enclosingLibrary.isNonNullableByDefault;
}
return false;

return true;
}

@override
int get hashCode {
if (_class.typeParameters.isNotEmpty) {
return _class.hashCode;
}
int hash = 31;
hash = 0x3fffffff & (hash * 31 + _class.supertype.hashCode);
hash = 0x3fffffff & (hash * 31 + _class.mixedInType.hashCode);
hash = 0x3fffffff & (hash * 31 + _class.supertype!.classNode.hashCode);
for (var i in _class.implementedTypes) {
hash = 0x3fffffff & (hash * 31 + i.hashCode);
hash = 0x3fffffff & (hash * 31 + i.classNode.hashCode);
}
hash = 0x3fffffff & (hash * 31 + _class.typeParameters.length.hashCode);
return hash;
}
}
Expand Down
43 changes: 43 additions & 0 deletions pkg/vm/test/transformations/mixin_deduplication_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2024, 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:io';

import 'package:kernel/target/targets.dart';
import 'package:kernel/verifier.dart';
import 'package:test/test.dart';
import 'package:vm/modular/target/vm.dart' show VmTarget;
import 'package:vm/transformations/mixin_deduplication.dart'
show transformComponent;

import '../common_test_utils.dart';

final String pkgVmDir = Platform.script.resolve('../..').toFilePath();

runTestCase(Uri source) async {
final target = VmTarget(new TargetFlags(soundNullSafety: true));
final component =
await compileTestCaseToKernelProgram(source, target: target);
transformComponent(component);
verifyComponent(
target, VerificationStage.afterGlobalTransformations, component);

final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);
compareResultWithExpectationsFile(source, actual);
}

main() {
group('mixin-deduplication', () {
final testCasesDir =
Directory(pkgVmDir + '/testcases/transformations/mixin_deduplication');

for (var entry in testCasesDir
.listSync(recursive: true, followLinks: false)
.reversed) {
if (entry.path.endsWith(".dart")) {
test(entry.path, () => runTestCase(entry.uri));
}
}
});
}
35 changes: 35 additions & 0 deletions pkg/vm/testcases/transformations/mixin_deduplication/generic.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2024, 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.

class B1 {}

mixin M1<T> {}

class SA1<T> extends B1 with M1<T> {}

class SA2<T> extends B1 with M1<T> {}

class SA3<T> extends B1 with M1<T?> {}

class B2<T> {}

mixin M2 {}

class SB1<T> extends B2<T> with M2 {}

class SB2<T> extends B2<T> with M2 {}

class SB3<T> extends B2<T?> with M2 {}

class B3<T> {}

mixin M3 {}

class SC1<T extends Object> extends B3<T> with M2 {}

class SC2<T extends Object> extends B3<T> with M2 {}

class SC3<T extends Object?> extends B3<T> with M2 {}

main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
library #lib;
import self as self;
import "dart:core" as core;

class B1 extends core::Object {
synthetic constructor •() → self::B1
: super core::Object::•()
;
}
abstract class M1<T extends core::Object? = dynamic> extends core::Object /*isMixinDeclaration*/ {
}
abstract class _SA1&B1&M1<T extends core::Object? = dynamic> extends self::B1 implements self::M1<self::_SA1&B1&M1::T%> /*isAnonymousMixin,isEliminatedMixin*/ {
synthetic constructor •() → self::_SA1&B1&M1<self::_SA1&B1&M1::T%>
: super self::B1::•()
;
}
class SA1<T extends core::Object? = dynamic> extends self::_SA1&B1&M1<self::SA1::T%> {
synthetic constructor •() → self::SA1<self::SA1::T%>
: super self::_SA1&B1&M1::•()
;
}
class SA2<T extends core::Object? = dynamic> extends self::_SA1&B1&M1<self::SA2::T%> {
synthetic constructor •() → self::SA2<self::SA2::T%>
: super self::_SA1&B1&M1::•()
;
}
abstract class _SA3&B1&M1<T extends core::Object? = dynamic> extends self::B1 implements self::M1<self::_SA3&B1&M1::T?> /*isAnonymousMixin,isEliminatedMixin*/ {
synthetic constructor •() → self::_SA3&B1&M1<self::_SA3&B1&M1::T%>
: super self::B1::•()
;
}
class SA3<T extends core::Object? = dynamic> extends self::_SA3&B1&M1<self::SA3::T%> {
synthetic constructor •() → self::SA3<self::SA3::T%>
: super self::_SA3&B1&M1::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
abstract class M2 extends core::Object /*isMixinDeclaration*/ {
}
abstract class _SB1&B2&M2<T extends core::Object? = dynamic> extends self::B2<self::_SB1&B2&M2::T%> implements self::M2 /*isAnonymousMixin,isEliminatedMixin*/ {
synthetic constructor •() → self::_SB1&B2&M2<self::_SB1&B2&M2::T%>
: super self::B2::•()
;
}
class SB1<T extends core::Object? = dynamic> extends self::_SB1&B2&M2<self::SB1::T%> {
synthetic constructor •() → self::SB1<self::SB1::T%>
: super self::_SB1&B2&M2::•()
;
}
class SB2<T extends core::Object? = dynamic> extends self::_SB1&B2&M2<self::SB2::T%> {
synthetic constructor •() → self::SB2<self::SB2::T%>
: super self::_SB1&B2&M2::•()
;
}
abstract class _SB3&B2&M2<T extends core::Object? = dynamic> extends self::B2<self::_SB3&B2&M2::T?> implements self::M2 /*isAnonymousMixin,isEliminatedMixin*/ {
synthetic constructor •() → self::_SB3&B2&M2<self::_SB3&B2&M2::T%>
: super self::B2::•()
;
}
class SB3<T extends core::Object? = dynamic> extends self::_SB3&B2&M2<self::SB3::T%> {
synthetic constructor •() → self::SB3<self::SB3::T%>
: super self::_SB3&B2&M2::•()
;
}
class B3<T extends core::Object? = dynamic> extends core::Object {
synthetic constructor •() → self::B3<self::B3::T%>
: super core::Object::•()
;
}
abstract class M3 extends core::Object /*isMixinDeclaration*/ {
}
abstract class _SC1&B3&M2<T extends core::Object> extends self::B3<self::_SC1&B3&M2::T> implements self::M2 /*isAnonymousMixin,isEliminatedMixin*/ {
synthetic constructor •() → self::_SC1&B3&M2<self::_SC1&B3&M2::T>
: super self::B3::•()
;
}
class SC1<T extends core::Object> extends self::_SC1&B3&M2<self::SC1::T> {
synthetic constructor •() → self::SC1<self::SC1::T>
: super self::_SC1&B3&M2::•()
;
}
class SC2<T extends core::Object> extends self::_SC1&B3&M2<self::SC2::T> {
synthetic constructor •() → self::SC2<self::SC2::T>
: super self::_SC1&B3&M2::•()
;
}
abstract class _SC3&B3&M2<T extends core::Object?> extends self::B3<self::_SC3&B3&M2::T%> implements self::M2 /*isAnonymousMixin,isEliminatedMixin*/ {
synthetic constructor •() → self::_SC3&B3&M2<self::_SC3&B3&M2::T%>
: super self::B3::•()
;
}
class SC3<T extends core::Object?> extends self::_SC3&B3&M2<self::SC3::T%> {
synthetic constructor •() → self::SC3<self::SC3::T%>
: super self::_SC3&B3&M2::•()
;
}
static method main() → dynamic {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2024, 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.

class B1 {}

mixin M1<T> {}

class SA1 extends B1 with M1<int> {}

class SA2 extends B1 with M1<int> {}

class SA3 extends B1 with M1<String> {}

class B2<T> {}

mixin M2 {}

class SB1 extends B2<int> with M2 {}

class SB2 extends B2<int> with M2 {}

class SB3 extends B2<String> with M2 {}

main() {}
Loading

0 comments on commit df116cf

Please sign in to comment.