Add 'external-effect' pragma support to all the backends.
Call sites targeting a procedure annotated with `external-effect` will not produce any code, including the argument which will not be evaluated. However, the single parameter will be treated as 'live' for the purposes of any global analysis the backends do. This is useful for things like protobuf shaking where a user may want to retain certain protobuf messages without actually emitting the code that retains those messages. Today this functionality is available internally in the vm and wasm SDK libraries. dart2js has similar functionality represented via the opaqueTrue and opaqueFalse booleans (which will cause conditional branches to get shaken after analysis). This will replace dart2js's opaque(True/False). This also adds validation to the frontend to ensure a method annotated with 'external-effect' is well-formed. Change-Id: If1c4096673e655c58fe7638840a16125003e7809 Tested: Backend tests for codegen were added. A frontend test was added for the validation. A language test was added to confirm the behavior. Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/476020 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Nate Biggs <natebiggs@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
d29b88d152
commit
3d2d6492c1
@@ -608,7 +608,8 @@ class JsInteropChecks extends RecursiveVisitor {
|
||||
final uri = member.enclosingLibrary.importUri;
|
||||
return uri.isScheme('dart') &&
|
||||
_pathsWithAllowedDartExternalUsage.contains(uri.path) ||
|
||||
_allowedNativeTestPatterns.any(uri.path.contains);
|
||||
_allowedNativeTestPatterns.any(uri.path.contains) ||
|
||||
(member is Procedure && member.hasExternalEffectPragma);
|
||||
}
|
||||
|
||||
/// Assumes given [member] is not JS interop, and reports an error if
|
||||
|
||||
@@ -318,3 +318,12 @@ argument type. It should be either `external` or return its argument (for backwa
|
||||
Compiler replaces `weakRef(foo)` expression with either `foo` if method `foo()` is used and retained during
|
||||
tree shaking, or `null` if `foo()` is only used through weak references.
|
||||
Target `foo` should be a constant tearoff of a static method without arguments.
|
||||
|
||||
### Declaring an external effect method
|
||||
|
||||
```dart
|
||||
@pragma('external-effect')
|
||||
external void effect(Object? o);
|
||||
```
|
||||
|
||||
Declares a special static external method `effect` which the compiler will treat as live code when performing any analysis of the program. For example, a type referenced from this call that would otherwise be tree-shaken will no longer be tree-shaken. To reduce code size the call itself (and its arguments) are dropped in the compiled output though.
|
||||
|
||||
@@ -2407,7 +2407,8 @@ class KernelNativeMemberResolver {
|
||||
// js_interop_checks when `native` and `external` can be disambiguated.
|
||||
if (!hasNativeBody &&
|
||||
node.isExternal &&
|
||||
!_nativeBasicData.isJsInteropMember(_elementMap.getMember(node))) {
|
||||
!_nativeBasicData.isJsInteropMember(_elementMap.getMember(node)) &&
|
||||
!(node is ir.Procedure && node.hasExternalEffectPragma)) {
|
||||
// TODO(johnniwinther): Should we change dart:html and friends to use
|
||||
// `external` instead of the native body syntax?
|
||||
_elementMap.reporter.reportErrorMessage(
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
import 'package:_js_interop_checks/src/js_interop.dart'
|
||||
show getDartJSInteropJSName;
|
||||
// ignore: implementation_imports
|
||||
import 'package:front_end/src/api_prototype/external_effect.dart'
|
||||
as ir
|
||||
show ExternalEffect;
|
||||
// ignore: implementation_imports
|
||||
import 'package:front_end/src/api_prototype/static_weak_references.dart'
|
||||
as ir
|
||||
show StaticWeakReferences;
|
||||
@@ -5063,6 +5067,10 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
|
||||
stack.add(graph.addConstantNull(closedWorld));
|
||||
return;
|
||||
}
|
||||
if (ir.ExternalEffect.isExternalEffect(node)) {
|
||||
stack.add(graph.addConstantNull(closedWorld));
|
||||
return;
|
||||
}
|
||||
ir.Procedure target = node.target;
|
||||
final sourceInformation = _sourceInformationBuilder.buildCall(node, node);
|
||||
final function = _elementMap.getMember(target) as FunctionEntity;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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.
|
||||
|
||||
@pragma('external-effect')
|
||||
external void externalEffect(Object? o);
|
||||
|
||||
void nonExternalEffect(Object? o) {}
|
||||
|
||||
int use(int i) {
|
||||
print(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
/*member: main:function() {
|
||||
A.print(1);
|
||||
A.print(2);
|
||||
}*/
|
||||
void main() {
|
||||
externalEffect(use(0));
|
||||
print(1);
|
||||
nonExternalEffect(use(2));
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:front_end/src/api_prototype/external_effect.dart'
|
||||
show ExternalEffect;
|
||||
import 'package:kernel/ast.dart' hide Component, FunctionDeclaration;
|
||||
import 'package:kernel/ast.dart' as ast show Component, FunctionDeclaration;
|
||||
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
||||
@@ -1089,11 +1091,6 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
'reachabilityFence',
|
||||
);
|
||||
|
||||
late Procedure nativeEffect = libraryIndex.getTopLevelProcedure(
|
||||
'dart:_internal',
|
||||
'_nativeEffect',
|
||||
);
|
||||
|
||||
late Procedure iterableIterator = libraryIndex.getProcedure(
|
||||
'dart:core',
|
||||
'Iterable',
|
||||
@@ -4006,6 +4003,11 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
|
||||
@override
|
||||
void visitStaticInvocation(StaticInvocation node) {
|
||||
if (ExternalEffect.isExternalEffect(node)) {
|
||||
// Skip over AST of the argument, return null.
|
||||
asm.emitPushNull();
|
||||
return;
|
||||
}
|
||||
if (node.isConst) {
|
||||
_genPushConstExpr(node);
|
||||
return;
|
||||
@@ -4018,10 +4020,6 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
assert(args.named.isEmpty);
|
||||
_generateNode(args.positional.single);
|
||||
return;
|
||||
} else if (target == nativeEffect) {
|
||||
// Skip over AST of the argument, return null.
|
||||
asm.emitPushNull();
|
||||
return;
|
||||
} else if (target == ffiCall) {
|
||||
assert(args.named.isEmpty);
|
||||
_generateFfiCall(args.positional.single);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
import 'dart:collection' show LinkedHashMap;
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:front_end/src/api_prototype/external_effect.dart'
|
||||
show ExternalEffect;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/type_environment.dart';
|
||||
import 'package:wasm_builder/wasm_builder.dart' as w;
|
||||
@@ -1625,6 +1627,9 @@ abstract class AstCodeGenerator
|
||||
StaticInvocation node,
|
||||
w.ValueType expectedType,
|
||||
) {
|
||||
if (ExternalEffect.isExternalEffect(node)) {
|
||||
return voidMarker;
|
||||
}
|
||||
w.ValueType? intrinsicResult = intrinsifier.generateStaticIntrinsic(node);
|
||||
if (intrinsicResult != null) return intrinsicResult;
|
||||
|
||||
|
||||
@@ -286,7 +286,6 @@ enum StaticIntrinsic {
|
||||
setIdentityHashField('dart:_object_helper', null, 'setIdentityHashField'),
|
||||
unsafeCast('dart:_internal', null, 'unsafeCast'),
|
||||
unsafeCastOpaque('dart:_internal', null, 'unsafeCastOpaque'),
|
||||
nativeEffect('dart:_internal', null, '_nativeEffect'),
|
||||
floatToIntBits('dart:_internal', null, 'floatToIntBits'),
|
||||
intBitsToFloat('dart:_internal', null, 'intBitsToFloat'),
|
||||
doubleToIntBits('dart:_internal', null, 'doubleToIntBits'),
|
||||
@@ -1440,9 +1439,6 @@ class Intrinsifier {
|
||||
// Just evaluate the operand and let the context convert it to the
|
||||
// expected type.
|
||||
return codeGen.translateExpression(operand, typeOfExp(operand));
|
||||
case StaticIntrinsic.nativeEffect:
|
||||
// Ignore argument
|
||||
return translator.voidMarker;
|
||||
case StaticIntrinsic.floatToIntBits:
|
||||
codeGen.translateExpression(
|
||||
node.arguments.positional.single,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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.
|
||||
|
||||
// functionFilter=main
|
||||
// compilerOption=--no-inlining
|
||||
// compilerOption=--no-minify
|
||||
|
||||
@pragma('external-effect')
|
||||
external void externalEffect(Object? o);
|
||||
|
||||
void nonExternalEffect(Object? o) {}
|
||||
|
||||
int use(int i) {
|
||||
print(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
@pragma('wasm:never-inline')
|
||||
void main() {
|
||||
externalEffect(use(0));
|
||||
print(1);
|
||||
nonExternalEffect(use(2));
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
(module $module0
|
||||
(type $#Top (struct
|
||||
(field $field0 i32)))
|
||||
(type $BoxedInt (sub final $#Top (struct
|
||||
(field $field0 i32)
|
||||
(field $value i64))))
|
||||
(global $1 (ref $BoxedInt)
|
||||
(i32.const 59)
|
||||
(i64.const 1)
|
||||
(struct.new $BoxedInt))
|
||||
(func $"main <noInline>"
|
||||
global.get $1
|
||||
call $print
|
||||
i32.const 59
|
||||
i64.const 2
|
||||
struct.new $BoxedInt
|
||||
call $print
|
||||
)
|
||||
(func $print (param $var0 (ref $#Top)) <...>)
|
||||
)
|
||||
@@ -11,6 +11,8 @@ import 'package:_js_interop_checks/src/js_interop.dart'
|
||||
show getDartJSInteropJSName, hasDartJSInteropAnnotation;
|
||||
import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart'
|
||||
show ExtensionIndex;
|
||||
import 'package:front_end/src/api_prototype/external_effect.dart'
|
||||
show ExternalEffect;
|
||||
import 'package:front_end/src/api_unstable/ddc.dart';
|
||||
import 'package:js_shared/synced/embedded_names.dart' show JsGetName, JsBuiltin;
|
||||
import 'package:kernel/class_hierarchy.dart';
|
||||
@@ -3644,8 +3646,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
||||
_currentUri = savedUri;
|
||||
_staticTypeContext.leaveMember(p);
|
||||
|
||||
if (_options.dynamicModule &&
|
||||
p.annotations.any((a) => _isEntrypointPragma(a, _coreTypes))) {
|
||||
if (_options.dynamicModule && _isDynamicModuleEntryPoint(p, _coreTypes)) {
|
||||
if (_dynamicEntrypoint == null) {
|
||||
if (p.function.requiredParameterCount > 0) {
|
||||
// TODO(sigmund): this error should be caught by a kernel checker that
|
||||
@@ -6684,6 +6685,9 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
||||
@override
|
||||
js_ast.Expression visitStaticInvocation(StaticInvocation node) {
|
||||
var target = node.target;
|
||||
if (ExternalEffect.isExternalEffect(node)) {
|
||||
return js_ast.LiteralNull();
|
||||
}
|
||||
if (isInlineJS(target)) return _emitInlineJSCode(node) as js_ast.Expression;
|
||||
if (target.isFactory) return _emitFactoryInvocation(node);
|
||||
|
||||
@@ -9275,12 +9279,6 @@ class _SwitchLabelState {
|
||||
///
|
||||
/// Used to denote the entrypoint method of a dynamic module.
|
||||
// TODO(sigmund): move to package:kernel.
|
||||
bool _isEntrypointPragma(Expression expression, CoreTypes coreTypes) {
|
||||
if (expression is! ConstantExpression) return false;
|
||||
final value = expression.constant;
|
||||
if (value is! InstanceConstant) return false;
|
||||
if (value.classReference != coreTypes.pragmaClass.reference) return false;
|
||||
final name = value.fieldValues[coreTypes.pragmaName.fieldReference];
|
||||
if (name is! StringConstant) return false;
|
||||
return name.value == 'dyn-module:entry-point';
|
||||
bool _isDynamicModuleEntryPoint(Procedure p, CoreTypes coreTypes) {
|
||||
return hasPragma(p, 'dyn-module:entry-point', coreTypes);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import 'package:_js_interop_checks/src/js_interop.dart'
|
||||
show getDartJSInteropJSName, hasDartJSInteropAnnotation;
|
||||
import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart'
|
||||
show ExtensionIndex;
|
||||
import 'package:front_end/src/api_prototype/external_effect.dart'
|
||||
show ExternalEffect;
|
||||
import 'package:front_end/src/api_unstable/ddc.dart';
|
||||
import 'package:js_shared/synced/embedded_names.dart' show JsGetName, JsBuiltin;
|
||||
import 'package:kernel/class_hierarchy.dart';
|
||||
@@ -4249,8 +4251,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
||||
_currentUri = savedUri;
|
||||
_staticTypeContext.leaveMember(p);
|
||||
|
||||
if (_options.dynamicModule &&
|
||||
p.annotations.any((a) => _isEntrypointPragma(a, _coreTypes))) {
|
||||
if (_options.dynamicModule && _isDynamicModuleEntryPoint(p, _coreTypes)) {
|
||||
if (_dynamicEntrypoint == null) {
|
||||
if (p.function.requiredParameterCount > 0) {
|
||||
// TODO(sigmund): this error should be caught by a kernel checker that
|
||||
@@ -7377,6 +7378,9 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
||||
@override
|
||||
js_ast.Expression visitStaticInvocation(StaticInvocation node) {
|
||||
var target = node.target;
|
||||
if (ExternalEffect.isExternalEffect(node)) {
|
||||
return js_ast.LiteralNull();
|
||||
}
|
||||
if (isInlineJS(target)) return _emitInlineJSCode(node) as js_ast.Expression;
|
||||
if (target.isFactory) return _emitFactoryInvocation(node);
|
||||
|
||||
@@ -10098,12 +10102,6 @@ class _SwitchLabelState {
|
||||
/// `const pragma('dyn-module:entry-point')`.
|
||||
///
|
||||
/// Used to denote the entrypoint method of a dynamic module.
|
||||
bool _isEntrypointPragma(Expression expression, CoreTypes coreTypes) {
|
||||
if (expression is! ConstantExpression) return false;
|
||||
final value = expression.constant;
|
||||
if (value is! InstanceConstant) return false;
|
||||
if (value.classReference != coreTypes.pragmaClass.reference) return false;
|
||||
final name = value.fieldValues[coreTypes.pragmaName.fieldReference];
|
||||
if (name is! StringConstant) return false;
|
||||
return name.value == 'dyn-module:entry-point';
|
||||
bool _isDynamicModuleEntryPoint(Procedure p, CoreTypes coreTypes) {
|
||||
return hasPragma(p, 'dyn-module:entry-point', coreTypes);
|
||||
}
|
||||
|
||||
@@ -243,6 +243,22 @@ bool isUnsupportedFactoryConstructor(Procedure node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasPragma(Annotatable node, String name, CoreTypes coreTypes) {
|
||||
for (var a in node.annotations) {
|
||||
if (a is! ConstantExpression) continue;
|
||||
final value = a.constant;
|
||||
if (value is! InstanceConstant) continue;
|
||||
if (value.classReference != coreTypes.pragmaClass.reference) {
|
||||
continue;
|
||||
}
|
||||
final nameValue = value.fieldValues[coreTypes.pragmaName.fieldReference];
|
||||
if (nameValue is StringConstant && nameValue.value == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Gets the real supertype of [c] and the list of [mixins] in reverse
|
||||
/// application order (mixins will appear before ones they override).
|
||||
///
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// 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.
|
||||
|
||||
export '../kernel/external_effect.dart' show ExternalEffect;
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
import 'package:_fe_analyzer_shared/src/metadata/expressions.dart' as shared;
|
||||
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' show Token;
|
||||
import 'package:front_end/src/kernel/kernel_constants.dart'
|
||||
show KernelConstantErrorReporter;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/clone.dart';
|
||||
|
||||
import '../api_prototype/experimental_flags.dart';
|
||||
import '../api_prototype/external_effect.dart' show ExternalEffect;
|
||||
import '../base/extension_scope.dart';
|
||||
import '../base/loader.dart';
|
||||
import '../base/scope.dart' show LookupScope;
|
||||
@@ -166,6 +169,25 @@ class MetadataBuilder {
|
||||
for (Annotation annotation in annotations) {
|
||||
annotation.metadataBuilder._expression = annotation.expression;
|
||||
}
|
||||
|
||||
validateAnnotations(annotatable, libraryBuilder);
|
||||
}
|
||||
|
||||
static void validateAnnotations(
|
||||
Annotatable annotatable,
|
||||
SourceLibraryBuilder libraryBuilder,
|
||||
) {
|
||||
if (ExternalEffect.isOutlineAnnotatedWithExternalEffect(
|
||||
annotatable,
|
||||
libraryBuilder.loader.coreTypes,
|
||||
)) {
|
||||
ExternalEffect.validatePragma(
|
||||
annotatable,
|
||||
libraryBuilder.loader.coreTypes,
|
||||
new KernelConstantErrorReporter(libraryBuilder.loader),
|
||||
checkHasFlag: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2046,6 +2046,47 @@ Message _withArgumentsCyclicTypedef({required String name}) {
|
||||
);
|
||||
}
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode dartExternalEffectIncorrectType = const MessageCode(
|
||||
"DartExternalEffectIncorrectType",
|
||||
problemMessage:
|
||||
"""A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'""",
|
||||
correctionMessage: """Try correcting the type of the function.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode dartExternalEffectMalformedPragma = const MessageCode(
|
||||
"DartExternalEffectMalformedPragma",
|
||||
problemMessage:
|
||||
"""The 'external-effect' pragma must be applied as a String literal.""",
|
||||
correctionMessage:
|
||||
"""Try inlining the 'external-string' argument to the pragma.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode dartExternalEffectNotExternal = const MessageCode(
|
||||
"DartExternalEffectNotExternal",
|
||||
problemMessage:
|
||||
"""A function annotated with the 'external-effect' pragma must be external.""",
|
||||
correctionMessage: """Try making the function external.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode dartExternalEffectNotMethod = const MessageCode(
|
||||
"DartExternalEffectNotMethod",
|
||||
problemMessage:
|
||||
"""The 'external-effect' pragma can only be applied to methods.""",
|
||||
correctionMessage: """Try removing the pragma or applying it to a method.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode dartExternalEffectNotStatic = const MessageCode(
|
||||
"DartExternalEffectNotStatic",
|
||||
problemMessage:
|
||||
"""A function annotated with the 'external-effect' pragma must be static.""",
|
||||
correctionMessage: """Try making the function static.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode dartFfiLibraryInDart2Wasm = const MessageCode(
|
||||
"DartFfiLibraryInDart2Wasm",
|
||||
|
||||
@@ -24,6 +24,7 @@ import 'package:_fe_analyzer_shared/src/exhaustiveness/exhaustive.dart';
|
||||
import 'package:_fe_analyzer_shared/src/exhaustiveness/space.dart';
|
||||
import 'package:_fe_analyzer_shared/src/exhaustiveness/static_type.dart';
|
||||
import 'package:front_end/src/codes/diagnostic.dart' as diag;
|
||||
import 'package:front_end/src/kernel/external_effect.dart' show ExternalEffect;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/src/find_type_visitor.dart';
|
||||
@@ -365,6 +366,18 @@ class ConstantsTransformer extends RemovingTransformer {
|
||||
constantEvaluator.errorReporter,
|
||||
);
|
||||
}
|
||||
|
||||
if (ExternalEffect.isAnnotatedWithExternalEffect(
|
||||
parent,
|
||||
typeEnvironment.coreTypes,
|
||||
)) {
|
||||
ExternalEffect.validatePragma(
|
||||
parent,
|
||||
typeEnvironment.coreTypes,
|
||||
constantEvaluator.errorReporter,
|
||||
checkHasFlag: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
RecordUse.validateAnnotations(
|
||||
nodes,
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// 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.
|
||||
|
||||
import 'package:front_end/src/codes/diagnostic.dart' as diag;
|
||||
import 'package:front_end/src/api_prototype/constant_evaluator.dart';
|
||||
import 'package:front_end/src/kernel/utils.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/kernel.dart';
|
||||
|
||||
class ExternalEffect {
|
||||
static const String pragmaName = 'external-effect';
|
||||
|
||||
// Coverage-ignore(suite): Not run.
|
||||
static bool isExternalEffect(StaticInvocation node) {
|
||||
return node.target.hasExternalEffectPragma;
|
||||
}
|
||||
|
||||
static bool isOutlineAnnotatedWithExternalEffect(
|
||||
Annotatable node,
|
||||
CoreTypes coreTypes,
|
||||
) {
|
||||
return isOutlineAnnotatedWithPragma(node, coreTypes, pragmaName);
|
||||
}
|
||||
|
||||
static bool isAnnotatedWithExternalEffect(
|
||||
Annotatable node,
|
||||
CoreTypes coreTypes,
|
||||
) {
|
||||
return isAnnotatedWithPragma(node, coreTypes, pragmaName);
|
||||
}
|
||||
|
||||
static void validatePragma(
|
||||
Annotatable node,
|
||||
CoreTypes coreTypes,
|
||||
ErrorReporter errorReporter, {
|
||||
required bool checkHasFlag,
|
||||
}) {
|
||||
if (node is! Procedure || node.kind != ProcedureKind.Method) {
|
||||
errorReporter.report(
|
||||
diag.dartExternalEffectNotMethod.withLocation(
|
||||
node.location!.file,
|
||||
node.fileOffset,
|
||||
1,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.isInstanceMember) {
|
||||
errorReporter.report(
|
||||
diag.dartExternalEffectNotStatic.withLocation(
|
||||
node.location!.file,
|
||||
node.fileOffset,
|
||||
1,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node.isExternal) {
|
||||
errorReporter.report(
|
||||
diag.dartExternalEffectNotExternal.withLocation(
|
||||
node.location!.file,
|
||||
node.fileOffset,
|
||||
1,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
FunctionNode function = node.function;
|
||||
|
||||
if (function.computeFunctionType(Nullability.nonNullable) !=
|
||||
new FunctionType(
|
||||
[coreTypes.objectNullableRawType],
|
||||
const VoidType(),
|
||||
Nullability.nonNullable,
|
||||
)) {
|
||||
errorReporter.report(
|
||||
diag.dartExternalEffectIncorrectType.withLocation(
|
||||
node.location!.file,
|
||||
node.fileOffset,
|
||||
1,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkHasFlag && !node.hasExternalEffectPragma) {
|
||||
errorReporter.report(
|
||||
diag.dartExternalEffectMalformedPragma.withLocation(
|
||||
node.location!.file,
|
||||
node.fileOffset,
|
||||
1,
|
||||
),
|
||||
);
|
||||
}
|
||||
node.hasExternalEffectPragma = true;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
/// Handling of static weak references.
|
||||
|
||||
import 'package:front_end/src/codes/diagnostic.dart' as diag;
|
||||
import 'package:front_end/src/kernel/utils.dart' show isAnnotatedWithPragma;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/core_types.dart' show CoreTypes;
|
||||
|
||||
@@ -23,27 +24,7 @@ class StaticWeakReferences {
|
||||
static bool isAnnotatedWithWeakReferencePragma(
|
||||
Annotatable node,
|
||||
CoreTypes coreTypes,
|
||||
) {
|
||||
List<Expression> annotations = node.annotations;
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
Expression annotation = annotations[i];
|
||||
if (annotation is ConstantExpression) {
|
||||
Constant constant = annotation.constant;
|
||||
if (constant is InstanceConstant) {
|
||||
if (constant.classNode == coreTypes.pragmaClass) {
|
||||
Constant? name =
|
||||
constant.fieldValues[coreTypes.pragmaName.fieldReference];
|
||||
if (name is StringConstant) {
|
||||
if (name.value == weakTearoffReferencePragma) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
) => isAnnotatedWithPragma(node, coreTypes, weakTearoffReferencePragma);
|
||||
|
||||
static void validateWeakReferenceUse(
|
||||
StaticInvocation node,
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:front_end/src/base/scope.dart';
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/binary/ast_to_binary.dart';
|
||||
import 'package:kernel/clone.dart';
|
||||
import 'package:kernel/core_types.dart' show CoreTypes;
|
||||
import 'package:kernel/text/ast_to_text.dart';
|
||||
import 'package:kernel/src/printer.dart';
|
||||
|
||||
@@ -379,3 +380,51 @@ class _DummyExtensionScope implements ExtensionScope {
|
||||
}
|
||||
|
||||
final Argument dummyArgument = new PositionalArgument(dummyExpression);
|
||||
|
||||
bool isOutlineAnnotatedWithPragma(
|
||||
Annotatable node,
|
||||
CoreTypes coreTypes,
|
||||
String pragmaName,
|
||||
) {
|
||||
List<Expression> annotations = node.annotations;
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
Expression annotation = annotations[i];
|
||||
if (annotation is RedirectingFactoryInvocation &&
|
||||
annotation
|
||||
.redirectingFactoryTarget
|
||||
.function
|
||||
.redirectingFactoryTarget!
|
||||
.target ==
|
||||
coreTypes.pragmaConstructor) {
|
||||
Expression name = annotation.expression.arguments.positional[0];
|
||||
if (name is StringLiteral && name.value == pragmaName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isAnnotatedWithPragma(
|
||||
Annotatable node,
|
||||
CoreTypes coreTypes,
|
||||
String pragmaName,
|
||||
) {
|
||||
List<Expression> annotations = node.annotations;
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
Expression annotation = annotations[i];
|
||||
if (annotation is ConstantExpression) {
|
||||
Constant constant = annotation.constant;
|
||||
if (constant is InstanceConstant) {
|
||||
if (constant.classNode == coreTypes.pragmaClass) {
|
||||
Constant? name =
|
||||
constant.fieldValues[coreTypes.pragmaName.fieldReference];
|
||||
if (name is StringConstant && name.value == pragmaName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7757,3 +7757,46 @@ anonymousMethodWrongParameterTypeCfe:
|
||||
experiments: anonymous-methods
|
||||
statement:
|
||||
- '"".(int i) => 1;'
|
||||
|
||||
dartExternalEffectNotMethod:
|
||||
parameters: none
|
||||
problemMessage: "The 'external-effect' pragma can only be applied to methods."
|
||||
correctionMessage: "Try removing the pragma or applying it to a method."
|
||||
script: |
|
||||
@pragma('external-effect')
|
||||
external int get foo;
|
||||
|
||||
dartExternalEffectIncorrectType:
|
||||
parameters: none
|
||||
problemMessage: "A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'"
|
||||
correctionMessage: "Try correcting the type of the function."
|
||||
script: |
|
||||
@pragma('external-effect')
|
||||
external void foo(int x);
|
||||
|
||||
dartExternalEffectNotStatic:
|
||||
parameters: none
|
||||
problemMessage: "A function annotated with the 'external-effect' pragma must be static."
|
||||
correctionMessage: "Try making the function static."
|
||||
script: |
|
||||
class C {
|
||||
@pragma('external-effect')
|
||||
external void foo(Object? x);
|
||||
}
|
||||
|
||||
dartExternalEffectNotExternal:
|
||||
parameters: none
|
||||
problemMessage: "A function annotated with the 'external-effect' pragma must be external."
|
||||
correctionMessage: "Try making the function external."
|
||||
script: |
|
||||
@pragma('external-effect')
|
||||
void foo(Object? x) {}
|
||||
|
||||
dartExternalEffectMalformedPragma:
|
||||
parameters: none
|
||||
problemMessage: "The 'external-effect' pragma must be applied as a String literal."
|
||||
correctionMessage: "Try inlining the 'external-string' argument to the pragma."
|
||||
script: |
|
||||
const bar = 'external-effect';
|
||||
@pragma(bar)
|
||||
external void foo(Object? x);
|
||||
|
||||
@@ -11,6 +11,7 @@ front_end/lib/src/api_prototype/compiler_options/Exports: Fail
|
||||
front_end/lib/src/api_prototype/const_conditional_simplifier/Exports: Fail
|
||||
front_end/lib/src/api_prototype/constant_evaluator/Exports: Fail
|
||||
front_end/lib/src/api_prototype/dynamic_module_validator/Exports: Fail
|
||||
front_end/lib/src/api_prototype/external_effect/Exports: Fail
|
||||
front_end/lib/src/api_prototype/front_end/Exports: Fail
|
||||
front_end/lib/src/api_prototype/incremental_kernel_generator/Exports: Fail
|
||||
front_end/lib/src/api_prototype/lowering_predicates/Exports: Fail
|
||||
|
||||
@@ -66,6 +66,7 @@ finality
|
||||
float32x
|
||||
float64x
|
||||
flutter_runner
|
||||
function(object
|
||||
function.tojs
|
||||
futureor
|
||||
github.com
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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.
|
||||
|
||||
@pragma('external-effect')
|
||||
external int a;
|
||||
|
||||
@pragma('external-effect')
|
||||
external int get b;
|
||||
|
||||
@pragma('external-effect')
|
||||
external set c(int value);
|
||||
|
||||
@pragma('external-effect')
|
||||
void d(Object? o) {}
|
||||
|
||||
@pragma('external-effect')
|
||||
external void e(Object o);
|
||||
|
||||
const z = 'external-effect';
|
||||
|
||||
@pragma(z)
|
||||
external void f(Object? o);
|
||||
|
||||
class A {
|
||||
@pragma('external-effect')
|
||||
external void a(Object? o);
|
||||
|
||||
@pragma('external-effect')
|
||||
external static int b(Object? o);
|
||||
|
||||
@pragma('external-effect')
|
||||
external static void c(Object? o, Object? x);
|
||||
|
||||
@pragma('external-effect')
|
||||
external static void d(int i);
|
||||
|
||||
@pragma('external-effect')
|
||||
external static void e([Object? o = const Object()]);
|
||||
|
||||
@pragma('external-effect')
|
||||
external static void f({required Object? o});
|
||||
|
||||
@pragma('external-effect')
|
||||
external static void g({Object? o = 3});
|
||||
|
||||
@pragma('external-effect')
|
||||
external static void h<T>(T? t);
|
||||
}
|
||||
|
||||
void main() {}
|
||||
@@ -0,0 +1,128 @@
|
||||
library;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:6:14: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external int a;
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:9:18: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external int get b;
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:12:14: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external set c(int value);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:15:6: Error: A function annotated with the 'external-effect' pragma must be external.
|
||||
// Try making the function external.
|
||||
// void d(Object? o) {}
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:18:15: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external void e(Object o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:27:17: Error: A function annotated with the 'external-effect' pragma must be static.
|
||||
// Try making the function static.
|
||||
// external void a(Object? o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:30:23: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static int b(Object? o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:33:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void c(Object? o, Object? x);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:36:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void d(int i);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:39:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void e([Object? o = const Object()]);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:42:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void f({required Object? o});
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:45:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void g({Object? o = 3});
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:48:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void h<T>(T? t);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:23:15: Error: The 'external-effect' pragma must be applied as a String literal.
|
||||
// Try inlining the 'external-string' argument to the pragma.
|
||||
// external void f(Object? o);
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A extends core::Object {
|
||||
synthetic constructor •() → self::A
|
||||
: super core::Object::•()
|
||||
;
|
||||
@#C3
|
||||
external method a(core::Object? o) → void;
|
||||
@#C3
|
||||
external static method b(core::Object? o) → core::int;
|
||||
@#C3
|
||||
external static method c(core::Object? o, core::Object? x) → void;
|
||||
@#C3
|
||||
external static method d(core::int i) → void;
|
||||
@#C3
|
||||
external static method e([core::Object? o = #C4]) → void;
|
||||
@#C3
|
||||
external static method f({required core::Object? o}) → void;
|
||||
@#C3
|
||||
external static method g({core::Object? o = #C5}) → void;
|
||||
@#C3
|
||||
external static method h<T extends core::Object? = dynamic>(self::A::h::T? t) → void;
|
||||
}
|
||||
static const field core::String z = #C1;
|
||||
@#C3
|
||||
external static get a() → core::int;
|
||||
@#C3
|
||||
external static set a(synthesized core::int #externalFieldValue) → void;
|
||||
@#C3
|
||||
external static get b() → core::int;
|
||||
@#C3
|
||||
external static set c(core::int value) → void;
|
||||
@#C3
|
||||
static method d(core::Object? o) → void {}
|
||||
@#C3
|
||||
external static method e(core::Object o) → void;
|
||||
@#C3
|
||||
external static external-effect method f(core::Object? o) → void;
|
||||
static method main() → void {}
|
||||
|
||||
constants {
|
||||
#C1 = "external-effect"
|
||||
#C2 = null
|
||||
#C3 = core::pragma {name:#C1, options:#C2}
|
||||
#C4 = core::Object {}
|
||||
#C5 = 3
|
||||
}
|
||||
|
||||
|
||||
Constructor coverage from constants:
|
||||
org-dartlang-testcase:///invalid_annotations.dart:
|
||||
- pragma._ (from org-dartlang-sdk:///sdk/lib/core/annotations.dart)
|
||||
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
library;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:6:14: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external int a;
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:9:18: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external int get b;
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:12:14: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external set c(int value);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:15:6: Error: A function annotated with the 'external-effect' pragma must be external.
|
||||
// Try making the function external.
|
||||
// void d(Object? o) {}
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:18:15: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external void e(Object o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:27:17: Error: A function annotated with the 'external-effect' pragma must be static.
|
||||
// Try making the function static.
|
||||
// external void a(Object? o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:30:23: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static int b(Object? o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:33:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void c(Object? o, Object? x);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:36:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void d(int i);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:39:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void e([Object? o = const Object()]);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:42:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void f({required Object? o});
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:45:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void g({Object? o = 3});
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:48:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void h<T>(T? t);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:23:15: Error: The 'external-effect' pragma must be applied as a String literal.
|
||||
// Try inlining the 'external-string' argument to the pragma.
|
||||
// external void f(Object? o);
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A extends core::Object {
|
||||
synthetic constructor •() → self::A
|
||||
: super core::Object::•()
|
||||
;
|
||||
@#C3
|
||||
external method a(core::Object? o) → void;
|
||||
@#C3
|
||||
external static method b(core::Object? o) → core::int;
|
||||
@#C3
|
||||
external static method c(core::Object? o, core::Object? x) → void;
|
||||
@#C3
|
||||
external static method d(core::int i) → void;
|
||||
@#C3
|
||||
external static method e([core::Object? o = #C4]) → void;
|
||||
@#C3
|
||||
external static method f({required core::Object? o}) → void;
|
||||
@#C3
|
||||
external static method g({core::Object? o = #C5}) → void;
|
||||
@#C3
|
||||
external static method h<T extends core::Object? = dynamic>(self::A::h::T? t) → void;
|
||||
}
|
||||
static const field core::String z = #C1;
|
||||
@#C3
|
||||
external static get a() → core::int;
|
||||
@#C3
|
||||
external static set a(synthesized core::int #externalFieldValue) → void;
|
||||
@#C3
|
||||
external static get b() → core::int;
|
||||
@#C3
|
||||
external static set c(core::int value) → void;
|
||||
@#C3
|
||||
static method d(core::Object? o) → void {}
|
||||
@#C3
|
||||
external static method e(core::Object o) → void;
|
||||
@#C3
|
||||
external static external-effect method f(core::Object? o) → void;
|
||||
static method main() → void {}
|
||||
|
||||
constants {
|
||||
#C1 = "external-effect"
|
||||
#C2 = null
|
||||
#C3 = core::pragma {name:#C1, options:#C2}
|
||||
#C4 = core::Object {}
|
||||
#C5 = 3
|
||||
}
|
||||
|
||||
|
||||
Constructor coverage from constants:
|
||||
org-dartlang-testcase:///invalid_annotations.dart:
|
||||
- pragma._ (from org-dartlang-sdk:///sdk/lib/core/annotations.dart)
|
||||
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
library;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:6:14: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external int a;
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:9:18: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external int get b;
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:12:14: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external set c(int value);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:15:6: Error: A function annotated with the 'external-effect' pragma must be external.
|
||||
// Try making the function external.
|
||||
// void d(Object? o) {}
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:18:15: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external void e(Object o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:27:17: Error: A function annotated with the 'external-effect' pragma must be static.
|
||||
// Try making the function static.
|
||||
// external void a(Object? o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:30:23: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static int b(Object? o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:33:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void c(Object? o, Object? x);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:36:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void d(int i);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:39:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void e([Object? o = const Object()]);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:42:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void f({required Object? o});
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:45:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void g({Object? o = 3});
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:48:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void h<T>(T? t);
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A extends core::Object {
|
||||
synthetic constructor •() → self::A
|
||||
;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external method a(core::Object? o) → void;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static method b(core::Object? o) → core::int;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static method c(core::Object? o, core::Object? x) → void;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static method d(core::int i) → void;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static method e([has-declared-initializer core::Object? o]) → void;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static method f({required core::Object? o}) → void;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static method g({has-declared-initializer core::Object? o}) → void;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static method h<T extends core::Object? = dynamic>(self::A::h::T? t) → void;
|
||||
}
|
||||
static const field core::String z = "external-effect";
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static get a() → core::int;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static set a(synthesized core::int #externalFieldValue) → void;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static get b() → core::int;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static set c(core::int value) → void;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
static method d(core::Object? o) → void
|
||||
;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static method e(core::Object o) → void;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_(self::z)
|
||||
external static method f(core::Object? o) → void;
|
||||
static method main() → void
|
||||
;
|
||||
|
||||
|
||||
Extra constant evaluation status:
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:26:4 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:29:4 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:32:4 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:35:4 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:38:4 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:41:4 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:44:4 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:47:4 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:5:2 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:5:2 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:8:2 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:11:2 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:14:2 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:17:2 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///invalid_annotations.dart:22:2 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Extra constant evaluation: evaluated: 15, effectively constant: 15
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
library;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:6:14: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external int a;
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:9:18: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external int get b;
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:12:14: Error: The 'external-effect' pragma can only be applied to methods.
|
||||
// Try removing the pragma or applying it to a method.
|
||||
// external set c(int value);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:15:6: Error: A function annotated with the 'external-effect' pragma must be external.
|
||||
// Try making the function external.
|
||||
// void d(Object? o) {}
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:18:15: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external void e(Object o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:27:17: Error: A function annotated with the 'external-effect' pragma must be static.
|
||||
// Try making the function static.
|
||||
// external void a(Object? o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:30:23: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static int b(Object? o);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:33:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void c(Object? o, Object? x);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:36:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void d(int i);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:39:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void e([Object? o = const Object()]);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:42:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void f({required Object? o});
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:45:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void g({Object? o = 3});
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:48:24: Error: A function annotated with the 'external-effect' pragma must have the type 'void Function(Object?)'
|
||||
// Try correcting the type of the function.
|
||||
// external static void h<T>(T? t);
|
||||
// ^
|
||||
//
|
||||
// pkg/front_end/testcases/external_effect/invalid_annotations.dart:23:15: Error: The 'external-effect' pragma must be applied as a String literal.
|
||||
// Try inlining the 'external-string' argument to the pragma.
|
||||
// external void f(Object? o);
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A extends core::Object {
|
||||
synthetic constructor •() → self::A
|
||||
: super core::Object::•()
|
||||
;
|
||||
@#C3
|
||||
external method a(core::Object? o) → void;
|
||||
@#C3
|
||||
external static method b(core::Object? o) → core::int;
|
||||
@#C3
|
||||
external static method c(core::Object? o, core::Object? x) → void;
|
||||
@#C3
|
||||
external static method d(core::int i) → void;
|
||||
@#C3
|
||||
external static method e([core::Object? o = #C4]) → void;
|
||||
@#C3
|
||||
external static method f({required core::Object? o}) → void;
|
||||
@#C3
|
||||
external static method g({core::Object? o = #C5}) → void;
|
||||
@#C3
|
||||
external static method h<T extends core::Object? = dynamic>(self::A::h::T? t) → void;
|
||||
}
|
||||
static const field core::String z = #C1;
|
||||
@#C3
|
||||
external static get a() → core::int;
|
||||
@#C3
|
||||
external static set a(synthesized core::int #externalFieldValue) → void;
|
||||
@#C3
|
||||
external static get b() → core::int;
|
||||
@#C3
|
||||
external static set c(core::int value) → void;
|
||||
@#C3
|
||||
static method d(core::Object? o) → void {}
|
||||
@#C3
|
||||
external static method e(core::Object o) → void;
|
||||
@#C3
|
||||
external static external-effect method f(core::Object? o) → void;
|
||||
static method main() → void {}
|
||||
|
||||
constants {
|
||||
#C1 = "external-effect"
|
||||
#C2 = null
|
||||
#C3 = core::pragma {name:#C1, options:#C2}
|
||||
#C4 = core::Object {}
|
||||
#C5 = 3
|
||||
}
|
||||
|
||||
|
||||
Constructor coverage from constants:
|
||||
org-dartlang-testcase:///invalid_annotations.dart:
|
||||
- pragma._ (from org-dartlang-sdk:///sdk/lib/core/annotations.dart)
|
||||
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
@pragma('external-effect')
|
||||
external int a;
|
||||
|
||||
@pragma('external-effect')
|
||||
external int get b;
|
||||
|
||||
@pragma('external-effect')
|
||||
external set c(int value);
|
||||
|
||||
@pragma('external-effect')
|
||||
void d(Object? o) {}
|
||||
|
||||
@pragma('external-effect')
|
||||
external void e(Object o);
|
||||
|
||||
const z = 'external-effect';
|
||||
|
||||
@pragma(z)
|
||||
external void f(Object? o);
|
||||
|
||||
class A {
|
||||
@pragma('external-effect')
|
||||
external void a(Object? o);
|
||||
@pragma('external-effect')
|
||||
external static int b(Object? o);
|
||||
@pragma('external-effect')
|
||||
external static void c(Object? o, Object? x);
|
||||
@pragma('external-effect')
|
||||
external static void d(int i);
|
||||
@pragma('external-effect')
|
||||
external static void e([Object? o = const Object()]);
|
||||
@pragma('external-effect')
|
||||
external static void f({required Object? o});
|
||||
@pragma('external-effect')
|
||||
external static void g({Object? o = 3});
|
||||
@pragma('external-effect')
|
||||
external static void h<T>(T? t);
|
||||
}
|
||||
|
||||
void main() {}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
class A {
|
||||
@pragma('external-effect')
|
||||
external static int b(Object? o);
|
||||
@pragma('external-effect')
|
||||
external static void c(Object? o, Object? x);
|
||||
@pragma('external-effect')
|
||||
external static void d(int i);
|
||||
@pragma('external-effect')
|
||||
external static void e([Object? o = const Object()]);
|
||||
@pragma('external-effect')
|
||||
external static void f({required Object? o});
|
||||
@pragma('external-effect')
|
||||
external static void g({Object? o = 3});
|
||||
@pragma('external-effect')
|
||||
external static void h<T>(T? t);
|
||||
@pragma('external-effect')
|
||||
external void a(Object? o);
|
||||
}
|
||||
|
||||
const z = 'external-effect';
|
||||
|
||||
@pragma('external-effect')
|
||||
external int a;
|
||||
|
||||
@pragma('external-effect')
|
||||
external int get b;
|
||||
|
||||
@pragma('external-effect')
|
||||
external set c(int value);
|
||||
|
||||
@pragma('external-effect')
|
||||
external void e(Object o);
|
||||
|
||||
@pragma(z)
|
||||
external void f(Object? o);
|
||||
|
||||
@pragma('external-effect')
|
||||
void d(Object? o) {}
|
||||
|
||||
void main() {}
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
|
||||
@pragma('external-effect')
|
||||
external void foo(Object? o);
|
||||
|
||||
class A {
|
||||
@pragma('external-effect')
|
||||
external static void foo(Object? o);
|
||||
}
|
||||
|
||||
void main() {}
|
||||
@@ -0,0 +1,26 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A extends core::Object {
|
||||
synthetic constructor •() → self::A
|
||||
: super core::Object::•()
|
||||
;
|
||||
@#C3
|
||||
external static external-effect method foo(core::Object? o) → void;
|
||||
}
|
||||
@#C3
|
||||
external static external-effect method foo(core::Object? o) → void;
|
||||
static method main() → void {}
|
||||
|
||||
constants {
|
||||
#C1 = "external-effect"
|
||||
#C2 = null
|
||||
#C3 = core::pragma {name:#C1, options:#C2}
|
||||
}
|
||||
|
||||
|
||||
Constructor coverage from constants:
|
||||
org-dartlang-testcase:///valid_annotations.dart:
|
||||
- pragma._ (from org-dartlang-sdk:///sdk/lib/core/annotations.dart)
|
||||
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
|
||||
@@ -0,0 +1,26 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A extends core::Object {
|
||||
synthetic constructor •() → self::A
|
||||
: super core::Object::•()
|
||||
;
|
||||
@#C3
|
||||
external static external-effect method foo(core::Object? o) → void;
|
||||
}
|
||||
@#C3
|
||||
external static external-effect method foo(core::Object? o) → void;
|
||||
static method main() → void {}
|
||||
|
||||
constants {
|
||||
#C1 = "external-effect"
|
||||
#C2 = null
|
||||
#C3 = core::pragma {name:#C1, options:#C2}
|
||||
}
|
||||
|
||||
|
||||
Constructor coverage from constants:
|
||||
org-dartlang-testcase:///valid_annotations.dart:
|
||||
- pragma._ (from org-dartlang-sdk:///sdk/lib/core/annotations.dart)
|
||||
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
|
||||
@@ -0,0 +1,20 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A extends core::Object {
|
||||
synthetic constructor •() → self::A
|
||||
;
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static external-effect method foo(core::Object? o) → void;
|
||||
}
|
||||
@/*original=core::pragma::•*/ const core::pragma::_("external-effect")
|
||||
external static external-effect method foo(core::Object? o) → void;
|
||||
static method main() → void
|
||||
;
|
||||
|
||||
|
||||
Extra constant evaluation status:
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///valid_annotations.dart:9:4 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Evaluated: RedirectingFactoryInvocation @ org-dartlang-testcase:///valid_annotations.dart:5:2 -> InstanceConstant(const pragma{pragma.name: "external-effect", pragma.options: null})
|
||||
Extra constant evaluation: evaluated: 2, effectively constant: 2
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class A extends core::Object {
|
||||
synthetic constructor •() → self::A
|
||||
: super core::Object::•()
|
||||
;
|
||||
@#C3
|
||||
external static external-effect method foo(core::Object? o) → void;
|
||||
}
|
||||
@#C3
|
||||
external static external-effect method foo(core::Object? o) → void;
|
||||
static method main() → void {}
|
||||
|
||||
constants {
|
||||
#C1 = "external-effect"
|
||||
#C2 = null
|
||||
#C3 = core::pragma {name:#C1, options:#C2}
|
||||
}
|
||||
|
||||
|
||||
Constructor coverage from constants:
|
||||
org-dartlang-testcase:///valid_annotations.dart:
|
||||
- pragma._ (from org-dartlang-sdk:///sdk/lib/core/annotations.dart)
|
||||
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
|
||||
@@ -0,0 +1,9 @@
|
||||
@pragma('external-effect')
|
||||
external void foo(Object? o);
|
||||
|
||||
class A {
|
||||
@pragma('external-effect')
|
||||
external static void foo(Object? o);
|
||||
}
|
||||
|
||||
void main() {}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
class A {
|
||||
@pragma('external-effect')
|
||||
external static void foo(Object? o);
|
||||
}
|
||||
|
||||
@pragma('external-effect')
|
||||
external void foo(Object? o);
|
||||
|
||||
void main() {}
|
||||
@@ -457,7 +457,7 @@ type Procedure extends Member {
|
||||
UInt flags (isStatic, isAbstract, isExternal, isConst,
|
||||
isExtensionMember, isSynthetic, isInternalImplementation,
|
||||
isExtensionTypeMember, hasWeakTearoffReferencePragma, IsLoweredLateField,
|
||||
isErroneous);
|
||||
isErroneous, isExternalEffect);
|
||||
Name name;
|
||||
List<Expression> annotations;
|
||||
MemberReference stubTarget; // May be NullReference.
|
||||
|
||||
@@ -1092,6 +1092,7 @@ class Procedure extends Member implements GenericFunction {
|
||||
static const int FlagExtensionTypeMember = 1 << 7;
|
||||
static const int FlagHasWeakTearoffReferencePragma = 1 << 8;
|
||||
static const int FlagErroneous = 1 << 9;
|
||||
static const int FlagHasExternalEffectPragma = 1 << 10;
|
||||
|
||||
bool get isStatic => flags & FlagStatic != 0;
|
||||
|
||||
@@ -1243,6 +1244,14 @@ class Procedure extends Member implements GenericFunction {
|
||||
: (flags & ~FlagHasWeakTearoffReferencePragma);
|
||||
}
|
||||
|
||||
bool get hasExternalEffectPragma => flags & FlagHasExternalEffectPragma != 0;
|
||||
|
||||
void set hasExternalEffectPragma(bool value) {
|
||||
flags = value
|
||||
? (flags | FlagHasExternalEffectPragma)
|
||||
: (flags & ~FlagHasExternalEffectPragma);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isErroneous => flags & FlagErroneous != 0;
|
||||
|
||||
|
||||
@@ -1380,6 +1380,7 @@ class Printer extends VisitorDefault<void> with VisitorVoidMixin {
|
||||
writeModifier(node.isSynthetic, 'synthetic');
|
||||
writeModifier(node.isConst, 'const');
|
||||
writeModifier(node.isErroneous, 'erroneous');
|
||||
writeModifier(node.hasExternalEffectPragma, 'external-effect');
|
||||
switch (node.stubKind) {
|
||||
case ProcedureStubKind.Regular:
|
||||
case ProcedureStubKind.AbstractForwardingStub:
|
||||
|
||||
@@ -148,3 +148,12 @@ corresponding to the category the recognized method belongs to, as defined in
|
||||
The pragmas must match exactly the set of recognized methods. This enables
|
||||
kernel-level analyses and optimizations to query whether a method is recognized
|
||||
by the VM. The correspondence is checked when running in debug mode.
|
||||
|
||||
### Declaring an external effect method
|
||||
|
||||
```dart
|
||||
@pragma('external-effect')
|
||||
external void effect(Object? o);
|
||||
```
|
||||
|
||||
Declares a special static external method `effect` which the compiler will treat as live code when performing any analysis of the program. For example, a type referenced from this call that would otherwise be tree-shaken will no longer be tree-shaken. To reduce code size the call itself (and its arguments) are dropped in the compiled output though.
|
||||
|
||||
@@ -22,6 +22,7 @@ These pragmas are part of the VM's API and are safe for use in external code.
|
||||
| `vm:deeply-immutable` | [Specifying a class and all its subtypes are deeply immutable](deeply_immutable.md) |
|
||||
| `vm:align-loops` | Tells compiler to align all loop headers inside the function to an architecture specific boundary: currently 32 bytes on X64 and ARM64 (except Apple Silicon, which explicitly discourages aligning branch targets) |
|
||||
| `vm:no-sanitize-thread` | Disable ThreadSanitizer instrumentation |
|
||||
| `external-effect` | Declares a static method which will be treated as live code when performing any analysis of the program. The call itself (and its arguments) are then dropped from the running program.
|
||||
|
||||
## Unsafe pragmas for general use
|
||||
|
||||
|
||||
@@ -305,10 +305,6 @@ DEFINE_NATIVE_ENTRY(Internal_unsafeCast, 0, 1) {
|
||||
return arguments->NativeArgAt(0);
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Internal_nativeEffect, 0, 1) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Internal_collectAllGarbage, 0, 0) {
|
||||
auto isolate_group = thread->isolate_group();
|
||||
isolate_group->heap()->CollectAllGarbage(GCReason::kDebugging,
|
||||
|
||||
@@ -265,7 +265,6 @@ namespace dart {
|
||||
V(GrowableList_setLength, 2) \
|
||||
V(GrowableList_setData, 2) \
|
||||
V(Internal_unsafeCast, 1) \
|
||||
V(Internal_nativeEffect, 1) \
|
||||
V(Internal_collectAllGarbage, 0) \
|
||||
V(Internal_makeListFixedLength, 1) \
|
||||
V(Internal_makeFixedListUnmodifiable, 1) \
|
||||
|
||||
@@ -3426,10 +3426,13 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(TokenPosition* p) {
|
||||
return BuildCachableIdempotentCall(position, target);
|
||||
}
|
||||
|
||||
if (target.IsExternalEffect()) {
|
||||
// AOT kernels will already have external effect calls removed by TFA.
|
||||
return BuildExternalEffect();
|
||||
}
|
||||
|
||||
const auto recognized_kind = target.recognized_kind();
|
||||
switch (recognized_kind) {
|
||||
case MethodRecognizer::kNativeEffect:
|
||||
return BuildNativeEffect();
|
||||
case MethodRecognizer::kReachabilityFence:
|
||||
return BuildReachabilityFence();
|
||||
case MethodRecognizer::kFfiCall:
|
||||
@@ -6003,7 +6006,7 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
|
||||
return instructions;
|
||||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::BuildNativeEffect() {
|
||||
Fragment StreamingFlowGraphBuilder::BuildExternalEffect() {
|
||||
const intptr_t argc = ReadUInt(); // Read argument count.
|
||||
ASSERT(argc == 1); // Native side effect to ignore.
|
||||
const intptr_t list_length = ReadListLength(); // Read types list length.
|
||||
|
||||
@@ -375,8 +375,8 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
Fragment BuildFunctionNode(intptr_t local_function_id,
|
||||
intptr_t func_decl_offset);
|
||||
|
||||
// Build flow graph for '_nativeEffect'.
|
||||
Fragment BuildNativeEffect();
|
||||
// Build flow graph for 'external-effect' methods.
|
||||
Fragment BuildExternalEffect();
|
||||
|
||||
// Build the call-site manually, to avoid doing initialization checks
|
||||
// for late fields.
|
||||
|
||||
@@ -191,7 +191,6 @@ namespace dart {
|
||||
V(FfiLibrary, ::, _checkNotDeeplyImmutable, CheckNotDeeplyImmutable, \
|
||||
0x34e4da90) \
|
||||
V(InternalLibrary, ClassID, getID, ClassIDgetID, 0xdc6e70ca) \
|
||||
V(InternalLibrary, ::, _nativeEffect, NativeEffect, 0x61c2f399) \
|
||||
V(InternalLibrary, ::, reachabilityFence, ReachabilityFence, 0x72f213bf) \
|
||||
V(InternalLibrary, ::, get:has63BitSmis, Has63BitSmis, 0xf5fe3f31) \
|
||||
V(InternalLibrary, ::, copyRangeFromUint8ListToOneByteString, \
|
||||
|
||||
@@ -9601,6 +9601,13 @@ bool Function::IsCachableIdempotent() const {
|
||||
return InVmTests(*this);
|
||||
}
|
||||
|
||||
bool Function::IsExternalEffect() const {
|
||||
if (!has_pragma()) return false;
|
||||
|
||||
return Library::FindPragma(dart::Thread::Current(), /*only_core=*/false,
|
||||
*this, Symbols::external_effect());
|
||||
}
|
||||
|
||||
bool Function::IsFfiCallClosure() const {
|
||||
if (!IsNonImplicitClosureFunction()) return false;
|
||||
if (!has_pragma()) return false;
|
||||
|
||||
@@ -3699,6 +3699,10 @@ class Function : public Object {
|
||||
|
||||
bool IsCachableIdempotent() const;
|
||||
|
||||
// Whether this function represents an external effect and should be dropped
|
||||
// from codegen.
|
||||
bool IsExternalEffect() const;
|
||||
|
||||
// Whether this function's |recognized_kind| requires optimization.
|
||||
bool RecognizedKindForceOptimize() const;
|
||||
|
||||
|
||||
@@ -525,6 +525,7 @@ namespace dart {
|
||||
V(dyn_module_implicitly_callable, "dyn-module:implicitly-callable") \
|
||||
V(dyn_module_can_be_used_as_type, "dyn-module:can-be-used-as-type") \
|
||||
V(executable, "executable") \
|
||||
V(external_effect, "external-effect") \
|
||||
V(get, "get") \
|
||||
V(isLeaf, "isLeaf") \
|
||||
V(isPaused, "isPaused") \
|
||||
|
||||
@@ -171,9 +171,8 @@ external void reachabilityFence(Object? object);
|
||||
// This function can be used to encode native side effects.
|
||||
//
|
||||
// The function call and it's argument are removed in flow graph construction.
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:external-name", "Internal_nativeEffect")
|
||||
external void _nativeEffect(Object object);
|
||||
@pragma("external-effect")
|
||||
external void _nativeEffect(Object? object);
|
||||
|
||||
// Collection of functions which should only be used for testing purposes.
|
||||
abstract class VMInternalsForTesting {
|
||||
|
||||
@@ -66,8 +66,8 @@ void reachabilityFence(Object? object) {}
|
||||
external void exportWasmFunction(Function object);
|
||||
|
||||
// This function can be used to encode native side effects.
|
||||
@pragma("wasm:intrinsic")
|
||||
external void _nativeEffect(Object object);
|
||||
@pragma("external-effect")
|
||||
external void _nativeEffect(Object? object);
|
||||
|
||||
// Thomas Wang 64-bit mix.
|
||||
// https://gist.github.com/badboy/6267743
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
@pragma('external-effect')
|
||||
external void externalEffect(Object? o);
|
||||
|
||||
void noExternalEffect(Object? o) {}
|
||||
|
||||
List<Object> used = [];
|
||||
|
||||
Null use(int o) {
|
||||
used.add(o);
|
||||
return null;
|
||||
}
|
||||
|
||||
const Object constObj = Object();
|
||||
|
||||
Null useConstObject() {
|
||||
used.add(constObj);
|
||||
return null;
|
||||
}
|
||||
|
||||
void main() {
|
||||
externalEffect(use(3));
|
||||
externalEffect(useConstObject());
|
||||
Expect.isTrue(used.isEmpty);
|
||||
noExternalEffect(use(4));
|
||||
noExternalEffect(useConstObject());
|
||||
Expect.equals(used.length, 2);
|
||||
Expect.equals(used[0], 4);
|
||||
Expect.equals(used[1], constObj);
|
||||
}
|
||||
Reference in New Issue
Block a user