[dyn_modules] dynamically-callable pragmas in annotator and TFA.
This change introduces the `dyn-module:dynamically-callable` and `dyn-module:implicitly-dynamically-callable` entrypoint pragmas. It changes the annotator to attach these pragmas to kernel nodes based on the dynamic-interface and updates TFA to treat these nodes as reachable and not elegible for tree-shaking. On a later change, the pragma will be read by the VM to verify that dynamic modules can only call dynamically members that were exposed as dynamically callable in the dynamic interface.. TEST=pkg/vm/test/ Bug: b/448095881 Change-Id: I4a8dbe0614e23d8b921e83323eacf1a1b1171ab9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/495660 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Sigmund Cherem <sigmund@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
d75661c6d8
commit
14517e3735
@@ -10,16 +10,23 @@ import 'package:vm/transformations/pragma.dart';
|
||||
|
||||
import 'collector.dart';
|
||||
|
||||
bool _isDefault(ParsedEntryPointPragma pragma) =>
|
||||
pragma.type == PragmaEntryPointType.Default ||
|
||||
pragma.type == PragmaEntryPointType.DynamicallyCallable;
|
||||
|
||||
bool _allowsCall(ParsedEntryPointPragma pragma) =>
|
||||
pragma.type == PragmaEntryPointType.Default ||
|
||||
pragma.type == PragmaEntryPointType.DynamicallyCallable ||
|
||||
pragma.type == PragmaEntryPointType.CallOnly;
|
||||
|
||||
bool _allowsGet(ParsedEntryPointPragma pragma) =>
|
||||
pragma.type == PragmaEntryPointType.Default ||
|
||||
pragma.type == PragmaEntryPointType.DynamicallyCallable ||
|
||||
pragma.type == PragmaEntryPointType.GetterOnly;
|
||||
|
||||
bool _allowsSet(ParsedEntryPointPragma pragma) =>
|
||||
pragma.type == PragmaEntryPointType.Default ||
|
||||
pragma.type == PragmaEntryPointType.DynamicallyCallable ||
|
||||
pragma.type == PragmaEntryPointType.SetterOnly;
|
||||
|
||||
EntryPointShimCollector visitLibrary(
|
||||
@@ -69,13 +76,14 @@ class EntryPointShimVisitor extends RecursiveVisitor {
|
||||
(p) =>
|
||||
p.type != PragmaEntryPointType.Extendable &&
|
||||
p.type != PragmaEntryPointType.ImplicitlyExtendable &&
|
||||
p.type != PragmaEntryPointType.CanBeOverridden,
|
||||
p.type != PragmaEntryPointType.CanBeOverridden &&
|
||||
p.type != PragmaEntryPointType.CanBeUsedAsType,
|
||||
);
|
||||
if (pragmas.isEmpty) return null;
|
||||
var pragma = pragmas.first;
|
||||
if (pragma.type != PragmaEntryPointType.Default) {
|
||||
if (!_isDefault(pragma)) {
|
||||
for (final p in pragmas.skip(1)) {
|
||||
if (p.type == PragmaEntryPointType.Default) {
|
||||
if (_isDefault(p)) {
|
||||
pragma = p;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ import 'pragma.dart'
|
||||
kDynModuleCanBeUsedAsTypePragmaName,
|
||||
kDynModuleImplicitlyCallablePragmaName,
|
||||
kDynModuleImplicitlyExtendablePragmaName,
|
||||
kDynModuleDynamicallyCallablePragmaName,
|
||||
kDynModuleImplicitlyDynamicallyCallablePragmaName,
|
||||
kDynModuleCanBeOverriddenImplicitlyPragmaName;
|
||||
|
||||
void annotateComponent(
|
||||
@@ -129,6 +131,29 @@ void annotateComponent(
|
||||
annotateInstanceMembers: false,
|
||||
logger: logger,
|
||||
);
|
||||
logger?.setActiveSection('dynamically-callable');
|
||||
final dynamicallyCallableAnnotator = annotateNodes(
|
||||
spec.dynamicallyCallable,
|
||||
kDynModuleDynamicallyCallablePragmaName,
|
||||
baseUri,
|
||||
coreTypes,
|
||||
annotateClasses: false,
|
||||
annotateFinalClasses: false,
|
||||
annotateExtensionTypes: false,
|
||||
annotateStaticMembers: false,
|
||||
annotateInstanceMembers: true,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
final dynamicallyCallableImplicitUsesAnnotator = _ImplicitUsesAnnotator(
|
||||
pragmaConstant(
|
||||
coreTypes,
|
||||
kDynModuleImplicitlyDynamicallyCallablePragmaName,
|
||||
),
|
||||
dynamicallyCallableAnnotator.annotatedClasses,
|
||||
dynamicallyCallableAnnotator.annotatedMembers,
|
||||
);
|
||||
dynamicallyCallableImplicitUsesAnnotator.annotateDispatchTargets(component);
|
||||
}
|
||||
|
||||
InstanceConstant pragmaConstant(CoreTypes coreTypes, String pragmaName) {
|
||||
|
||||
@@ -36,6 +36,10 @@ const kDynModuleCallablePragmaName = "dyn-module:callable";
|
||||
const kDynModuleImplicitlyCallablePragmaName = "dyn-module:implicitly-callable";
|
||||
const kDynModuleCanBeUsedAsTypePragmaName = "dyn-module:can-be-used-as-type";
|
||||
const kDynModuleEntryPointPragmaName = "dyn-module:entry-point";
|
||||
const kDynModuleDynamicallyCallablePragmaName =
|
||||
"dyn-module:dynamically-callable";
|
||||
const kDynModuleImplicitlyDynamicallyCallablePragmaName =
|
||||
"dyn-module:implicitly-dynamically-callable";
|
||||
|
||||
abstract class ParsedPragma {}
|
||||
|
||||
@@ -48,6 +52,7 @@ enum PragmaEntryPointType {
|
||||
SetterOnly,
|
||||
CallOnly,
|
||||
CanBeUsedAsType,
|
||||
DynamicallyCallable,
|
||||
}
|
||||
|
||||
enum PragmaRecognizedType { AsmIntrinsic, GraphIntrinsic, Other }
|
||||
@@ -253,6 +258,11 @@ class ConstantPragmaAnnotationParser implements PragmaAnnotationParser {
|
||||
case kDynModuleCallablePragmaName:
|
||||
case kDynModuleImplicitlyCallablePragmaName:
|
||||
return getEntryPointTypeFromOptions(options, pragmaName);
|
||||
case kDynModuleDynamicallyCallablePragmaName:
|
||||
case kDynModuleImplicitlyDynamicallyCallablePragmaName:
|
||||
return const ParsedEntryPointPragma(
|
||||
PragmaEntryPointType.DynamicallyCallable,
|
||||
);
|
||||
case kDynModuleEntryPointPragmaName:
|
||||
return const ParsedDynModuleEntryPointPragma();
|
||||
case kVmSharedPragmaName:
|
||||
|
||||
@@ -626,7 +626,10 @@ final class _DispatchableInvocation extends _Invocation {
|
||||
|
||||
if (selector.callKind != CallKind.PropertyGet) {
|
||||
if (selector is DynamicSelector) {
|
||||
typeFlowAnalysis._methodsAndSettersCalledDynamically.add(target);
|
||||
typeFlowAnalysis.recordMemberCalledDynamically(
|
||||
target,
|
||||
isGetter: false,
|
||||
);
|
||||
} else if (selector is VirtualSelector) {
|
||||
typeFlowAnalysis._calledViaThis.add(target);
|
||||
} else {
|
||||
@@ -634,7 +637,10 @@ final class _DispatchableInvocation extends _Invocation {
|
||||
}
|
||||
} else {
|
||||
if (selector is DynamicSelector) {
|
||||
typeFlowAnalysis._gettersCalledDynamically.add(target);
|
||||
typeFlowAnalysis.recordMemberCalledDynamically(
|
||||
target,
|
||||
isGetter: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -907,11 +913,10 @@ final class _DispatchableInvocation extends _Invocation {
|
||||
// the mismatch in the number or names of arguments,
|
||||
// it still participates in the dynamic lookup.
|
||||
// So mark it as called dynamically so its signature is preserved.
|
||||
if (selector.callKind != CallKind.PropertyGet) {
|
||||
typeFlowAnalysis._methodsAndSettersCalledDynamically.add(target);
|
||||
} else {
|
||||
typeFlowAnalysis._gettersCalledDynamically.add(target);
|
||||
}
|
||||
typeFlowAnalysis.recordMemberCalledDynamically(
|
||||
target,
|
||||
isGetter: selector.callKind == CallKind.PropertyGet,
|
||||
);
|
||||
}
|
||||
|
||||
_ReceiverTypeBuilder _getReceiverTypeBuilder(
|
||||
@@ -2333,6 +2338,15 @@ class TypeFlowAnalysis
|
||||
_tearOffTaken.add(target);
|
||||
}
|
||||
|
||||
@override
|
||||
void recordMemberCalledDynamically(Member target, {required bool isGetter}) {
|
||||
if (isGetter) {
|
||||
_gettersCalledDynamically.add(target);
|
||||
} else {
|
||||
_methodsAndSettersCalledDynamically.add(target);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Procedure getClosureCallMethod(Closure closure) =>
|
||||
_callMethodByClosure[closure] ??= _createCallMethod(closure);
|
||||
|
||||
@@ -41,6 +41,9 @@ abstract class EntryPointsListener {
|
||||
/// Record the fact that given member is torn off.
|
||||
void recordTearOff(Member target) {}
|
||||
|
||||
/// Record the fact that given member is called dynamically.
|
||||
void recordMemberCalledDynamically(Member target, {required bool isGetter});
|
||||
|
||||
/// Artificial call method corresponding to the given [closure].
|
||||
Procedure getClosureCallMethod(Closure closure);
|
||||
|
||||
@@ -202,6 +205,22 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PragmaEntryPointType.DynamicallyCallable:
|
||||
if (proc.isGetter) {
|
||||
addSelector(CallKind.PropertyGet);
|
||||
entryPoints.recordMemberCalledDynamically(proc, isGetter: true);
|
||||
} else if (proc.isSetter) {
|
||||
addSelector(CallKind.PropertySet);
|
||||
entryPoints.recordMemberCalledDynamically(proc, isGetter: false);
|
||||
} else {
|
||||
addSelector(CallKind.Method);
|
||||
entryPoints.recordMemberCalledDynamically(proc, isGetter: false);
|
||||
if (!proc.isFactory) {
|
||||
addSelector(CallKind.PropertyGet);
|
||||
entryPoints.recordMemberCalledDynamically(proc, isGetter: true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PragmaEntryPointType.Extendable:
|
||||
case PragmaEntryPointType.ImplicitlyExtendable:
|
||||
throw "Error: only class can be extendable";
|
||||
@@ -287,6 +306,14 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
|
||||
addSelector(CallKind.PropertySet);
|
||||
}
|
||||
break;
|
||||
case PragmaEntryPointType.DynamicallyCallable:
|
||||
addSelector(CallKind.PropertyGet);
|
||||
entryPoints.recordMemberCalledDynamically(field, isGetter: true);
|
||||
if (field.hasSetter) {
|
||||
addSelector(CallKind.PropertySet);
|
||||
entryPoints.recordMemberCalledDynamically(field, isGetter: false);
|
||||
}
|
||||
break;
|
||||
case PragmaEntryPointType.CallOnly:
|
||||
throw "Error: 'call' is not a valid entry-point pragma annotation "
|
||||
"argument for the field $field.\n$_referenceToDocumentation";
|
||||
|
||||
@@ -624,6 +624,9 @@ class _EntryPointsListenerImpl implements EntryPointsListener {
|
||||
@override
|
||||
void addDynamicallyExtendableClass(Class c) {}
|
||||
|
||||
@override
|
||||
void recordMemberCalledDynamically(Member target, {required bool isGetter}) {}
|
||||
|
||||
@override
|
||||
Field getRecordPositionalField(RecordShape shape, int pos) =>
|
||||
throw 'Unsupported operation';
|
||||
|
||||
@@ -72,6 +72,9 @@ class FakeEntryPointsListener implements EntryPointsListener {
|
||||
@override
|
||||
void recordTearOff(Member target) {}
|
||||
|
||||
@override
|
||||
void recordMemberCalledDynamically(Member target, {required bool isGetter}) {}
|
||||
|
||||
@override
|
||||
Procedure getClosureCallMethod(Closure closure) => closure.createCallMethod();
|
||||
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -33,3 +33,22 @@ can-be-overridden:
|
||||
class: 'D'
|
||||
member: 'build'
|
||||
- library: 'lib4.dart'
|
||||
|
||||
dynamically-callable:
|
||||
- library: 'lib6.dart'
|
||||
class: ['A', 'B', 'D']
|
||||
- library: 'lib6.dart'
|
||||
class: E
|
||||
member: dcMethod5
|
||||
- library: 'lib6.dart'
|
||||
class: E
|
||||
member: 'get:dcGetter3'
|
||||
- library: 'lib6.dart'
|
||||
class: E
|
||||
member: 'set:dcSetter3'
|
||||
- library: 'lib6.dart'
|
||||
class: E
|
||||
member: dcField7
|
||||
- library: 'lib6.dart'
|
||||
class: E
|
||||
member: dcField8
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2026, 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 A {
|
||||
void dcMethod1() {}
|
||||
void dcMethod2(String x, int y) {}
|
||||
int get dcGetter1 => 1;
|
||||
void set dcSetter1(int value) {}
|
||||
static void staticMethod1() {}
|
||||
|
||||
A();
|
||||
factory A.factory() => A();
|
||||
}
|
||||
|
||||
class A2 extends A {
|
||||
@override
|
||||
void dcMethod1() {
|
||||
print('1');
|
||||
}
|
||||
}
|
||||
|
||||
class A3 implements A {
|
||||
@override
|
||||
void dcMethod1() {
|
||||
print('1');
|
||||
}
|
||||
|
||||
@override
|
||||
void dcMethod2(String x, int y) {}
|
||||
@override
|
||||
int get dcGetter1 => 1;
|
||||
@override
|
||||
void set dcSetter1(int value) {}
|
||||
}
|
||||
|
||||
class B {
|
||||
final int dcField1 = 1;
|
||||
int dcField2 = 1;
|
||||
|
||||
void dcMethod3() {}
|
||||
void dcMethod4(String x, int y) {}
|
||||
int get dcGetter2 => 1;
|
||||
void set dcSetter2(int value) {}
|
||||
}
|
||||
|
||||
class C {
|
||||
int _dcPrivateMethod1() => 1;
|
||||
}
|
||||
|
||||
mixin D on C {
|
||||
int method1() => (this as dynamic)._dcPrivateMethod1();
|
||||
}
|
||||
|
||||
class E {
|
||||
void dcMethod5() {}
|
||||
int get dcGetter3 => 1;
|
||||
void set dcSetter3(int value) {}
|
||||
final int dcField7 = 1;
|
||||
int dcField8 = 1;
|
||||
int dcField9 = 1; // unexposed
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A extends core::Object {
|
||||
constructor •() → self::A
|
||||
: super core::Object::•()
|
||||
;
|
||||
@#C3
|
||||
method dcMethod1() → void {}
|
||||
@#C3
|
||||
method dcMethod2(core::String x, core::int y) → void {}
|
||||
@#C3
|
||||
get dcGetter1() → core::int
|
||||
return 1;
|
||||
@#C3
|
||||
set dcSetter1(core::int value) → void {}
|
||||
static method staticMethod1() → void {}
|
||||
static factory factory() → self::A
|
||||
return new self::A::•();
|
||||
}
|
||||
class A2 extends self::A {
|
||||
synthetic constructor •() → self::A2
|
||||
: super self::A::•()
|
||||
;
|
||||
@#C4
|
||||
@#C6
|
||||
method dcMethod1() → void {
|
||||
core::print("1");
|
||||
}
|
||||
}
|
||||
class A3 extends core::Object implements self::A {
|
||||
synthetic constructor •() → self::A3
|
||||
: super core::Object::•()
|
||||
;
|
||||
@#C4
|
||||
@#C6
|
||||
method dcMethod1() → void {
|
||||
core::print("1");
|
||||
}
|
||||
@#C4
|
||||
@#C6
|
||||
method dcMethod2(core::String x, core::int y) → void {}
|
||||
@#C4
|
||||
@#C6
|
||||
get dcGetter1() → core::int
|
||||
return 1;
|
||||
@#C4
|
||||
@#C6
|
||||
set dcSetter1(core::int value) → void {}
|
||||
}
|
||||
class B extends core::Object {
|
||||
@#C3
|
||||
final field core::int dcField1 = 1;
|
||||
@#C3
|
||||
field core::int dcField2 = 1;
|
||||
synthetic constructor •() → self::B
|
||||
: super core::Object::•()
|
||||
;
|
||||
@#C3
|
||||
method dcMethod3() → void {}
|
||||
@#C3
|
||||
method dcMethod4(core::String x, core::int y) → void {}
|
||||
@#C3
|
||||
get dcGetter2() → core::int
|
||||
return 1;
|
||||
@#C3
|
||||
set dcSetter2(core::int value) → void {}
|
||||
}
|
||||
class C extends core::Object {
|
||||
synthetic constructor •() → self::C
|
||||
: super core::Object::•()
|
||||
;
|
||||
method _dcPrivateMethod1() → core::int
|
||||
return 1;
|
||||
}
|
||||
abstract class D extends self::C /*isMixinDeclaration*/ {
|
||||
@#C3
|
||||
method method1() → core::int
|
||||
return (this as dynamic){dynamic}._dcPrivateMethod1() as{TypeError,ForDynamic} core::int;
|
||||
}
|
||||
class E extends core::Object {
|
||||
@#C3
|
||||
final field core::int dcField7 = 1;
|
||||
@#C3
|
||||
field core::int dcField8 = 1;
|
||||
field core::int dcField9 = 1;
|
||||
synthetic constructor •() → self::E
|
||||
: super core::Object::•()
|
||||
;
|
||||
@#C3
|
||||
method dcMethod5() → void {}
|
||||
@#C3
|
||||
get dcGetter3() → core::int
|
||||
return 1;
|
||||
@#C3
|
||||
set dcSetter3(core::int value) → void {}
|
||||
}
|
||||
constants {
|
||||
#C1 = "dyn-module:dynamically-callable"
|
||||
#C2 = null
|
||||
#C3 = core::pragma {name:#C1, options:#C2}
|
||||
#C4 = core::_Override {}
|
||||
#C5 = "dyn-module:implicitly-dynamically-callable"
|
||||
#C6 = core::pragma {name:#C5, options:#C2}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import 'lib1.dart';
|
||||
import 'lib2.dart';
|
||||
import 'lib3.dart';
|
||||
import 'lib4.dart';
|
||||
import 'lib6.dart';
|
||||
import 'prefix/lib1.dart';
|
||||
import 'prefix/lib2.dart';
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import "file:pkg/vm/testcases/transformations/dynamic_interface_annotator/lib1.d
|
||||
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/lib6.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";
|
||||
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
// Test for @pragma('dyn-module:dynamically-callable').
|
||||
|
||||
abstract class A {
|
||||
@pragma('dyn-module:dynamically-callable')
|
||||
Object method1(); // Not eliminated
|
||||
|
||||
@pragma('dyn-module:dynamically-callable')
|
||||
Object method2() => 42; // Not eliminated
|
||||
|
||||
Object method3() => 1; // Eliminated
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
@pragma('dyn-module:implicitly-dynamically-callable')
|
||||
int method1() => 42; // Not eliminated
|
||||
|
||||
Object method4() => 1; // Eliminated
|
||||
}
|
||||
|
||||
class C implements A {
|
||||
@pragma('dyn-module:implicitly-dynamically-callable')
|
||||
int method1() => 42; // Not eliminated
|
||||
|
||||
@pragma('dyn-module:implicitly-dynamically-callable')
|
||||
int method2() => 42; // Not eliminated
|
||||
|
||||
Object method3() => 2; // Eliminated
|
||||
|
||||
@pragma('dyn-module:dynamically-callable')
|
||||
Object method5() => 42; // Not eliminated
|
||||
|
||||
Object method6() => 1; // Eliminated
|
||||
}
|
||||
|
||||
abstract class D {
|
||||
Object method7(); // Eliminated
|
||||
}
|
||||
|
||||
class E implements D {
|
||||
@pragma('dyn-module:dynamically-callable')
|
||||
int get field1 => 0;
|
||||
|
||||
int method7() => 3; // Eliminated
|
||||
}
|
||||
|
||||
abstract class F {
|
||||
Object method8(); // Eliminated
|
||||
}
|
||||
|
||||
class G implements F {
|
||||
@pragma('dyn-module:implicitly-dynamically-callable')
|
||||
int field1 = 0; // Not eliminated
|
||||
|
||||
int field2 = 0; // Eliminated
|
||||
|
||||
int method8() => 4; // Eliminated
|
||||
|
||||
@pragma('dyn-module:dynamically-callable')
|
||||
int method9() => 42; // Eliminated
|
||||
|
||||
@pragma('dyn-module:dynamically-callable')
|
||||
int get getter2 => 0; // Not eliminated
|
||||
|
||||
@pragma('dyn-module:dynamically-callable')
|
||||
void set setter2(int v) {} // Not eliminated
|
||||
|
||||
int field3 = 0; // Eliminated
|
||||
|
||||
int get getter4 => 0; // Eliminated
|
||||
|
||||
void set setter4(int v) {} // Eliminated
|
||||
}
|
||||
|
||||
main() {}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
library #lib;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
abstract class A extends core::Object {
|
||||
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2]
|
||||
@#C3
|
||||
abstract method method1() → core::Object;
|
||||
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4]
|
||||
@#C3
|
||||
abstract method method2() → core::Object;
|
||||
}
|
||||
abstract class B extends core::Object {
|
||||
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6]
|
||||
@#C5
|
||||
abstract method method1() → core::int;
|
||||
}
|
||||
abstract class C extends core::Object {
|
||||
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7,getterSelectorId:8]
|
||||
@#C5
|
||||
abstract method method1() → core::int;
|
||||
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:9,getterSelectorId:10]
|
||||
@#C5
|
||||
abstract method method2() → core::int;
|
||||
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:11,getterSelectorId:12]
|
||||
@#C3
|
||||
abstract method method5() → core::Object;
|
||||
}
|
||||
abstract class E extends core::Object {
|
||||
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:13]
|
||||
@#C3
|
||||
abstract get field1() → core::int;
|
||||
}
|
||||
abstract class G extends core::Object {
|
||||
|
||||
[@vm.unreachable.metadata=]
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:14,getterSelectorId:15]
|
||||
@#C5
|
||||
field core::int field1;
|
||||
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:16,getterSelectorId:17]
|
||||
@#C3
|
||||
abstract method method9() → core::int;
|
||||
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:18]
|
||||
@#C3
|
||||
abstract get getter2() → core::int;
|
||||
|
||||
[@vm.procedure-attributes.metadata=getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:19]
|
||||
@#C3
|
||||
abstract set setter2(core::int v) → void;
|
||||
}
|
||||
|
||||
[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
|
||||
static method main() → dynamic {}
|
||||
constants {
|
||||
#C1 = "dyn-module:dynamically-callable"
|
||||
#C2 = null
|
||||
#C3 = core::pragma {name:#C1, options:#C2}
|
||||
#C4 = "dyn-module:implicitly-dynamically-callable"
|
||||
#C5 = core::pragma {name:#C4, options:#C2}
|
||||
}
|
||||
Reference in New Issue
Block a user