[vm, dynamic_modules] Support extensions and extension types in dynamic interface annotator

TEST=pkg/vm/test/transformations/dynamic_interface_annotator_test.dart

Bug: b/404399018
Change-Id: I9b4baafc0c75b62f4fe15cb1191f0e31669a8538
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/416340
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov
2025-03-19 06:30:48 -07:00
committed by Commit Queue
parent 55d57541cb
commit 2ac7957077
12 changed files with 234 additions and 0 deletions
@@ -0,0 +1,16 @@
# 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.
callable:
- library: 'shared/shared.dart'
# TODO(sigmund): This should be included by default
- library: 'dart:core'
class: 'Object'
- library: 'dart:core'
class: 'int'
- library: 'dart:core'
class: 'String'
- library: 'dart:core'
class: 'pragma'
member: '_'
@@ -0,0 +1,16 @@
// 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 '../../common/testing.dart' as helper;
import 'package:expect/expect.dart';
import 'shared/shared.dart'; // ignore: unused_import
/// A dynamic module is allowed to extend a class in the dynamic interface and
/// override its members.
void main() async {
final result = (await helper.load('entry1.dart')) as String;
Expect.equals('abcdef42', result);
helper.done();
}
@@ -0,0 +1,9 @@
// 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 '../shared/shared.dart';
@pragma('dyn-module:entry-point')
Object? dynamicModuleEntrypoint() =>
'abc'.addSuffix('def') + MyExtensionType.foo(42).info.toString();
@@ -0,0 +1,11 @@
// 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.
extension type MyExtensionType.foo(int _info) {
int get info => _info;
}
extension MyExtension on String {
String addSuffix(String suffix) => this + suffix;
}
@@ -152,6 +152,16 @@ class _Annotator extends RecursiveVisitor {
c.accept(this);
}
}
for (final ext in node.extensions) {
if (ext.name[0] != '_') {
ext.accept(this);
}
}
for (final extensionType in node.extensionTypeDeclarations) {
if (extensionType.name[0] != '_') {
extensionType.accept(this);
}
}
for (final exportRef in node.additionalExports) {
exportRef.node!.accept(this);
}
@@ -207,6 +217,34 @@ class _Annotator extends RecursiveVisitor {
node.addAnnotation(ConstantExpression(pragma));
}
}
@override
void visitExtension(Extension node) {
for (final md in node.memberDescriptors) {
final member = md.memberReference?.node;
if (member != null) {
annotateMember(member as Member);
}
final tearOff = md.tearOffReference?.node;
if (tearOff != null) {
annotateMember(tearOff as Member);
}
}
}
@override
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
for (final md in node.memberDescriptors) {
final member = md.memberReference?.node;
if (member != null) {
annotateMember(member as Member);
}
final tearOff = md.tearOffReference?.node;
if (tearOff != null) {
annotateMember(tearOff as Member);
}
}
}
}
class _ImplicitExtendableAnnotator {
@@ -9,12 +9,14 @@ callable:
member: sfield9
- library: 'lib3.dart'
class: ['A', 'B']
- library: 'lib4.dart'
- library: 'prefix/lib*'
extendable:
- library: 'lib1.dart'
class: ['A', 'O', 'Q', 'S', 'T', 'V']
- library: 'lib2.dart'
- library: 'lib4.dart'
can-be-overridden:
- library: 'lib1.dart'
@@ -22,3 +24,4 @@ can-be-overridden:
- library: 'lib2.dart'
class: 'D'
member: 'build'
- library: 'lib4.dart'
@@ -0,0 +1,25 @@
// 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.
export 'lib5.dart';
extension type E3(int _info) {
int get info => _info;
}
extension type _E4(int _info) {
int get info => _info;
}
extension E5 on String {
String addSuffix1(String suffix) => this + suffix;
}
extension _E6 on String {
String addSuffix2(String suffix) => this + suffix;
}
extension on String {
String addSuffix3(String suffix) => this + suffix;
}
@@ -0,0 +1,71 @@
library;
import self as self;
import "dart:core" as core;
import "lib5.dart" as lib5;
additionalExports = (lib5::E1,
lib5::E2)
export "file:pkg/vm/testcases/transformations/dynamic_interface_annotator/lib5.dart";
extension E5 on core::String {
method addSuffix1 = self::E5|addSuffix1;
method tearoff addSuffix1 = self::E5|get#addSuffix1;
}
extension _E6 on core::String {
method addSuffix2 = self::_E6|addSuffix2;
method tearoff addSuffix2 = self::_E6|get#addSuffix2;
}
extension /* unnamed */ _extension#2 on core::String {
method addSuffix3 = self::_extension#2|addSuffix3;
method tearoff addSuffix3 = self::_extension#2|get#addSuffix3;
}
extension type E3(core::int _info) {
abstract extension-type-member representation-field get _info() → core::int;
get info = self::E3|get#info;
constructor • = self::E3|constructor#;
constructor tearoff • = self::E3|constructor#_#new#tearOff;
}
extension type _E4(core::int _info) {
abstract extension-type-member representation-field get _info() → core::int;
get info = self::_E4|get#info;
constructor • = self::_E4|constructor#;
constructor tearoff • = self::_E4|constructor#_#new#tearOff;
}
@#C3
static extension-type-member method E3|constructor#(core::int _info) → self::E3% /* erasure=core::int, declared=! */ {
lowered final self::E3% /* erasure=core::int, declared=! */ #this = _info;
return #this;
}
@#C3
static extension-type-member method E3|constructor#_#new#tearOff(core::int _info) → self::E3% /* erasure=core::int, declared=! */
return self::E3|constructor#(_info);
@#C3
static extension-type-member method E3|get#info(lowered final self::E3% /* erasure=core::int, declared=! */ #this) → core::int
return #this as{Unchecked} core::int;
static extension-type-member method _E4|constructor#(core::int _info) → self::_E4% /* erasure=core::int, declared=! */ {
lowered final self::_E4% /* erasure=core::int, declared=! */ #this = _info;
return #this;
}
static extension-type-member method _E4|constructor#_#new#tearOff(core::int _info) → self::_E4% /* erasure=core::int, declared=! */
return self::_E4|constructor#(_info);
static extension-type-member method _E4|get#info(lowered final self::_E4% /* erasure=core::int, declared=! */ #this) → core::int
return #this as{Unchecked} core::int;
@#C3
static extension-member method E5|addSuffix1(lowered final core::String #this, core::String suffix) → core::String
return #this.{core::String::+}(suffix){(core::String) → core::String};
@#C3
static extension-member method E5|get#addSuffix1(lowered final core::String #this) → (core::String) → core::String
return (core::String suffix) → core::String => self::E5|addSuffix1(#this, suffix);
static extension-member method _E6|addSuffix2(lowered final core::String #this, core::String suffix) → core::String
return #this.{core::String::+}(suffix){(core::String) → core::String};
static extension-member method _E6|get#addSuffix2(lowered final core::String #this) → (core::String) → core::String
return (core::String suffix) → core::String => self::_E6|addSuffix2(#this, suffix);
static extension-member method _extension#2|addSuffix3(lowered final core::String #this, core::String suffix) → core::String
return #this.{core::String::+}(suffix){(core::String) → core::String};
static extension-member method _extension#2|get#addSuffix3(lowered final core::String #this) → (core::String) → core::String
return (core::String suffix) → core::String => self::_extension#2|addSuffix3(#this, suffix);
constants {
#C1 = "dyn-module:callable"
#C2 = null
#C3 = core::pragma {name:#C1, options:#C2}
}
@@ -0,0 +1,11 @@
// 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.
extension type E1._(int _info) {
int get info => _info;
}
extension E2 on String {
int get lastChar => codeUnitAt(length - 1);
}
@@ -0,0 +1,32 @@
library;
import self as self;
import "dart:core" as core;
extension E2 on core::String {
get lastChar = self::E2|get#lastChar;
}
extension type E1(core::int _info) {
abstract extension-type-member representation-field get _info() → core::int;
get info = self::E1|get#info;
constructor _ = self::E1|constructor#_;
constructor tearoff _ = self::E1|constructor#_#_#tearOff;
}
@#C3
static extension-type-member method E1|constructor#_(core::int _info) → self::E1% /* erasure=core::int, declared=! */ {
lowered final self::E1% /* erasure=core::int, declared=! */ #this = _info;
return #this;
}
@#C3
static extension-type-member method E1|constructor#_#_#tearOff(core::int _info) → self::E1% /* erasure=core::int, declared=! */
return self::E1|constructor#_(_info);
@#C3
static extension-type-member method E1|get#info(lowered final self::E1% /* erasure=core::int, declared=! */ #this) → core::int
return #this as{Unchecked} core::int;
@#C3
static extension-member method E2|get#lastChar(lowered final core::String #this) → core::int
return #this.{core::String::codeUnitAt}(#this.{core::String::length}{core::int}.{core::num::-}(1){(core::num) → core::int}){(core::int) → core::int};
constants {
#C1 = "dyn-module:callable"
#C2 = null
#C3 = core::pragma {name:#C1, options:#C2}
}
@@ -5,6 +5,7 @@
import 'lib1.dart';
import 'lib2.dart';
import 'lib3.dart';
import 'lib4.dart';
import 'prefix/lib1.dart';
import 'prefix/lib2.dart';
@@ -4,6 +4,7 @@ import self as self;
import "file:pkg/vm/testcases/transformations/dynamic_interface_annotator/lib1.dart";
import "file:pkg/vm/testcases/transformations/dynamic_interface_annotator/lib2.dart";
import "file:pkg/vm/testcases/transformations/dynamic_interface_annotator/lib3.dart";
import "file:pkg/vm/testcases/transformations/dynamic_interface_annotator/lib4.dart";
import "file:pkg/vm/testcases/transformations/dynamic_interface_annotator/prefix/lib1.dart";
import "file:pkg/vm/testcases/transformations/dynamic_interface_annotator/prefix/lib2.dart";