diff --git a/pkg/analyzer/lib/src/summary2/export.dart b/pkg/analyzer/lib/src/summary2/export.dart index 0a20b673790..542214a089c 100644 --- a/pkg/analyzer/lib/src/summary2/export.dart +++ b/pkg/analyzer/lib/src/summary2/export.dart @@ -61,6 +61,7 @@ class ExportedReferenceExported extends ExportedReference { }); void addLocation(ExportLocation location) { + // This list is very small, contains on it is probably ok. if (!locations.contains(location)) { locations.add(location); } diff --git a/pkg/analyzer/lib/src/summary2/link.dart b/pkg/analyzer/lib/src/summary2/link.dart index a908e30f272..bd801f67d9e 100644 --- a/pkg/analyzer/lib/src/summary2/link.dart +++ b/pkg/analyzer/lib/src/summary2/link.dart @@ -15,6 +15,7 @@ import 'package:analyzer/src/dart/element/name_union.dart'; import 'package:analyzer/src/fine/library_manifest.dart'; import 'package:analyzer/src/summary2/bundle_writer.dart'; import 'package:analyzer/src/summary2/detach_nodes.dart'; +import 'package:analyzer/src/summary2/export.dart'; import 'package:analyzer/src/summary2/library_builder.dart'; import 'package:analyzer/src/summary2/linked_element_factory.dart'; import 'package:analyzer/src/summary2/reference.dart'; @@ -179,19 +180,47 @@ class Linker { } } - while (true) { - var hasChanges = false; + // We keep a queue of exports to propagate. + // First we loop over every reference that both exports and is exported, + // but then we only process the exports that should be propagated. + var additionalExportData = <_AdditionalExport>[]; + + void addExport(Export export, String name, ExportedReference reference) { + if (export.addToExportScope(name, reference)) { + // We've added [name] to [export.exporter]s export scope. + // We need to propagate that to anyone that exports that library. + additionalExportData + .add(_AdditionalExport(export.exporter, name, reference)); + } + } + + for (var exported in both) { + for (var export in exported.exports) { + exported.exportScope.forEach((name, reference) { + addExport(export, name, reference); + }); + } + } + + while (additionalExportData.isNotEmpty) { + var data = additionalExportData.removeLast(); + for (var export in data.exported.exports) { + addExport(export, data.name, data.reference); + } + } + + assert(() { for (var exported in both) { for (var export in exported.exports) { exported.exportScope.forEach((name, reference) { if (export.addToExportScope(name, reference)) { - hasChanges = true; + throw "Error in export calculation: Assert failed."; } }); } } - if (!hasChanges) break; - } + return true; + }(), true); for (var library in builders.values) { library.storeExportScope(); @@ -362,3 +391,11 @@ class LinkResult { required this.resolutionBytes, }); } + +class _AdditionalExport { + final LibraryBuilder exported; + final String name; + final ExportedReference reference; + + _AdditionalExport(this.exported, this.name, this.reference); +} diff --git a/tests/language/export/bigger_cyclic_helper_1.dart b/tests/language/export/bigger_cyclic_helper_1.dart new file mode 100644 index 00000000000..b9a8a9916f4 --- /dev/null +++ b/tests/language/export/bigger_cyclic_helper_1.dart @@ -0,0 +1,18 @@ +// Copyright (c) 2025, 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 "bigger_cyclic_helper_2.dart"; +export "bigger_cyclic_helper_2.dart"; + +void get2(int i) { + print("2: $i"); + if (i > 0) { + get1(i - 1); + get2(i - 1); + get3(i - 1); + get4(i - 1); + get5(i - 1); + get6(i - 1); + } +} diff --git a/tests/language/export/bigger_cyclic_helper_2.dart b/tests/language/export/bigger_cyclic_helper_2.dart new file mode 100644 index 00000000000..3857708228e --- /dev/null +++ b/tests/language/export/bigger_cyclic_helper_2.dart @@ -0,0 +1,18 @@ +// Copyright (c) 2025, 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 "bigger_cyclic_helper_3.dart"; +export "bigger_cyclic_helper_3.dart"; + +void get3(int i) { + print("3: $i"); + if (i > 0) { + get1(i - 1); + get2(i - 1); + get3(i - 1); + get4(i - 1); + get5(i - 1); + get6(i - 1); + } +} diff --git a/tests/language/export/bigger_cyclic_helper_3.dart b/tests/language/export/bigger_cyclic_helper_3.dart new file mode 100644 index 00000000000..109347f1ed4 --- /dev/null +++ b/tests/language/export/bigger_cyclic_helper_3.dart @@ -0,0 +1,18 @@ +// Copyright (c) 2025, 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 "bigger_cyclic_helper_4.dart"; +export "bigger_cyclic_helper_4.dart"; + +void get4(int i) { + print("4: $i"); + if (i > 0) { + get1(i - 1); + get2(i - 1); + get3(i - 1); + get4(i - 1); + get5(i - 1); + get6(i - 1); + } +} diff --git a/tests/language/export/bigger_cyclic_helper_4.dart b/tests/language/export/bigger_cyclic_helper_4.dart new file mode 100644 index 00000000000..00bea4e6bb8 --- /dev/null +++ b/tests/language/export/bigger_cyclic_helper_4.dart @@ -0,0 +1,18 @@ +// Copyright (c) 2025, 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 "bigger_cyclic_helper_5.dart"; +export "bigger_cyclic_helper_5.dart"; + +void get5(int i) { + print("5: $i"); + if (i > 0) { + get1(i - 1); + get2(i - 1); + get3(i - 1); + get4(i - 1); + get5(i - 1); + get6(i - 1); + } +} diff --git a/tests/language/export/bigger_cyclic_helper_5.dart b/tests/language/export/bigger_cyclic_helper_5.dart new file mode 100644 index 00000000000..4b1086aaa17 --- /dev/null +++ b/tests/language/export/bigger_cyclic_helper_5.dart @@ -0,0 +1,18 @@ +// Copyright (c) 2025, 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 "bigger_cyclic_test.dart"; +export "bigger_cyclic_test.dart"; + +void get6(int i) { + print("6: $i"); + if (i > 0) { + get1(i - 1); + get2(i - 1); + get3(i - 1); + get4(i - 1); + get5(i - 1); + get6(i - 1); + } +} diff --git a/tests/language/export/bigger_cyclic_test.dart b/tests/language/export/bigger_cyclic_test.dart new file mode 100644 index 00000000000..b321be066e4 --- /dev/null +++ b/tests/language/export/bigger_cyclic_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2025, 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 "bigger_cyclic_helper_1.dart"; +export "bigger_cyclic_helper_1.dart"; + +void main() { + get1(1); +} + +void get1(int i) { + print("1: $i"); + if (i > 0) { + get1(i - 1); + get2(i - 1); + get3(i - 1); + get4(i - 1); + get5(i - 1); + get6(i - 1); + } +}