[kernel] Merge front_end and kernel verifiers

This merges the front_end and kernel verifiers into one Target based
kernel verifier. The RedirectingFactoryBody work-around is moved to
package:kernel to support its verification.

TEST=existing

Change-Id: I0adf4d2c22c4009cf439b3b23fa14192253a2846
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/280161
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
Reviewed-by: Joshua Litt <joshualitt@google.com>
This commit is contained in:
Johnni Winther 2023-04-26 14:14:36 +00:00 committed by Commit Queue
parent 498dcc02ee
commit 009cbfbeac
35 changed files with 954 additions and 680 deletions

View file

@ -103,8 +103,8 @@ Future<CompilerOutput?> compileToModule(compiler.CompilerOptions options,
useRapidTypeAnalysis: false);
assert(() {
verifyComponent(component,
afterConst: true, constantsAreAlwaysInlined: true);
verifyComponent(
target, VerificationStage.afterGlobalTransformations, component);
return true;
}());

View file

@ -15,6 +15,7 @@ import 'package:kernel/reference_from_index.dart';
import 'package:kernel/target/changed_structure_notifier.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/type_environment.dart';
import 'package:kernel/verifier.dart';
import 'package:vm/transformations/mixin_full_resolution.dart'
as transformMixins show transformLibraries;
import 'package:vm/transformations/ffi/common.dart' as ffiHelper
@ -52,6 +53,9 @@ class WasmTarget extends Target {
@override
ConstantsBackend get constantsBackend => const ConstantsBackend();
@override
Verification get verification => const WasmVerification();
@override
String get name => 'wasm';
@ -378,3 +382,24 @@ class WasmTarget extends Target {
int numPositionalFields, List<String> namedFields) =>
recordClasses[RecordShape(numPositionalFields, namedFields)]!;
}
class WasmVerification extends Verification {
const WasmVerification();
@override
bool allowNoFileOffset(VerificationStage stage, TreeNode node) {
if (super.allowNoFileOffset(stage, node)) {
return true;
}
if (stage >= VerificationStage.afterModularTransformations) {
// Allow synthesized classes, procedures, fields and casts.
// TODO(askesc): Improve the precision of these exceptions.
return node is Class ||
node is Constructor ||
node is Procedure ||
node is Field ||
node is AsExpression;
}
return false;
}
}

View file

@ -88,6 +88,8 @@ export 'package:_fe_analyzer_shared/src/util/link_implementation.dart'
export 'package:_fe_analyzer_shared/src/util/relativize.dart'
show relativizeUri;
export 'package:kernel/src/redirecting_factory_body.dart'
show isRedirectingFactoryField;
export '../api_prototype/compiler_options.dart'
show
@ -121,9 +123,6 @@ export '../base/nnbd_mode.dart' show NnbdMode;
export '../compute_platform_binaries_location.dart'
show computePlatformBinariesLocation;
export '../fasta/kernel/redirecting_factory_body.dart'
show isRedirectingFactoryField;
export '../fasta/operator.dart' show operatorFromString;
export 'compiler_state.dart' show InitializedCompilerState;

View file

@ -38,6 +38,12 @@ export 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
show DiagnosticMessage;
export 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
export 'package:kernel/src/redirecting_factory_body.dart'
show
getRedirectingFactories,
RedirectingFactoryBody,
isRedirectingFactoryField,
redirectingName;
export '../api_prototype/compiler_options.dart'
show CompilerOptions, parseExperimentalFlags, parseExperimentalArguments;
@ -68,13 +74,6 @@ export '../fasta/incremental_compiler.dart' show IncrementalCompiler;
export '../fasta/kernel/constructor_tearoff_lowering.dart'
show isTearOffLowering;
export '../fasta/kernel/redirecting_factory_body.dart'
show
getRedirectingFactories,
RedirectingFactoryBody,
isRedirectingFactoryField,
redirectingName;
export '../fasta/type_inference/type_schema_environment.dart'
show TypeSchemaEnvironment;

View file

@ -6,6 +6,11 @@ export 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
show DiagnosticMessage, DiagnosticMessageHandler, getMessageUri;
export 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
export 'package:kernel/src/redirecting_factory_body.dart'
show
expressionValueWrappedFinalizableName,
getRedirectingFactoryBody,
isRedirectingFactoryField;
export '../api_prototype/compiler_options.dart'
show
@ -101,12 +106,6 @@ export '../fasta/fasta_codes.dart'
export '../fasta/hybrid_file_system.dart' show HybridFileSystem;
export '../fasta/kernel/redirecting_factory_body.dart'
show
expressionValueWrappedFinalizableName,
getRedirectingFactoryBody,
isRedirectingFactoryField;
export '../fasta/kernel/utils.dart'
show
createExpressionEvaluationComponent,

View file

@ -7,41 +7,32 @@ library fasta.dill_library_builder;
import 'dart:convert' show jsonDecode;
import 'package:kernel/ast.dart';
import '../builder/builder.dart';
import '../builder/class_builder.dart';
import '../builder/dynamic_type_declaration_builder.dart';
import '../builder/extension_builder.dart';
import '../builder/modifier_builder.dart';
import '../builder/never_type_declaration_builder.dart';
import '../builder/invalid_type_declaration_builder.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
import '../builder/type_alias_builder.dart';
import '../fasta_codes.dart'
show Message, noLength, templateDuplicatedDeclaration, templateUnspecified;
import '../kernel/constructor_tearoff_lowering.dart';
import '../kernel/redirecting_factory_body.dart'
import 'package:kernel/src/redirecting_factory_body.dart'
show
RedirectingFactoryBody,
getRedirectingFactories,
isRedirectingFactoryField;
import '../builder/builder.dart';
import '../builder/class_builder.dart';
import '../builder/dynamic_type_declaration_builder.dart';
import '../builder/extension_builder.dart';
import '../builder/invalid_type_declaration_builder.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
import '../builder/modifier_builder.dart';
import '../builder/never_type_declaration_builder.dart';
import '../builder/type_alias_builder.dart';
import '../fasta_codes.dart'
show Message, noLength, templateDuplicatedDeclaration, templateUnspecified;
import '../kernel/constructor_tearoff_lowering.dart';
import '../kernel/utils.dart';
import '../problems.dart' show internalProblem, unhandled, unimplemented;
import '../scope.dart';
import 'dill_class_builder.dart' show DillClassBuilder;
import 'dill_extension_builder.dart';
import 'dill_member_builder.dart';
import 'dill_loader.dart' show DillLoader;
import 'dill_member_builder.dart';
import 'dill_type_alias_builder.dart' show DillTypeAliasBuilder;
class LazyLibraryScope extends LazyScope {

View file

@ -117,8 +117,9 @@ import 'implicit_type_argument.dart' show ImplicitTypeArgument;
import 'internal_ast.dart';
import 'kernel_variable_builder.dart';
import 'load_library_builder.dart';
import 'redirecting_factory_body.dart'
import 'package:kernel/src/redirecting_factory_body.dart'
show
EnsureLoaded,
RedirectingFactoryBody,
RedirectionTarget,
getRedirectingFactoryBody,
@ -9666,12 +9667,6 @@ class BodyBuilder extends StackListenerImpl
}
}
abstract class EnsureLoaded {
void ensureLoaded(Member? member);
bool isLoaded(Member? member);
}
class Operator {
final Token token;

View file

@ -15,6 +15,7 @@ import 'package:kernel/target/targets.dart' show DiagnosticReporter, Target;
import 'package:kernel/transformations/value_class.dart' as valueClass;
import 'package:kernel/type_algebra.dart' show Substitution;
import 'package:kernel/type_environment.dart' show TypeEnvironment;
import 'package:kernel/verifier.dart' show VerificationStage;
import 'package:package_config/package_config.dart' hide LanguageVersion;
import '../../api_prototype/experimental_flags.dart'
@ -1057,9 +1058,9 @@ class KernelTarget extends TargetImplementation {
isConst: isConst,
reference: constructorReference,
fileUri: cls.fileUri)
// TODO(johnniwinther): Should we add file offsets to synthesized
..fileOffset = cls.fileOffset
// TODO(johnniwinther): Should we add file end offset to synthesized
// constructors?
//..fileOffset = cls.fileOffset
//..fileEndOffset = cls.fileOffset
..isNonNullableByDefault = cls.enclosingLibrary.isNonNullableByDefault;
DelayedDefaultValueCloner delayedDefaultValueCloner =
@ -1622,7 +1623,8 @@ class KernelTarget extends TargetImplementation {
void verify() {
// TODO(ahe): How to handle errors.
verifyComponent(component!, context.options.target,
verifyComponent(context.options.target,
VerificationStage.afterModularTransformations, component!,
skipPlatform: context.options.skipPlatformVerification);
ClassHierarchy hierarchy =
new ClassHierarchy(component!, new CoreTypes(component!),

View file

@ -11,8 +11,7 @@ import 'package:kernel/target/targets.dart';
import 'package:kernel/type_environment.dart' show TypeEnvironment;
import 'package:kernel/verifier.dart'
show VerifyGetStaticType, VerifyingVisitor;
import 'package:kernel/verifier.dart';
import '../compiler_context.dart' show CompilerContext;
@ -24,176 +23,32 @@ import '../fasta_codes.dart'
noLength,
templateInternalProblemVerificationError;
import '../type_inference/type_schema.dart' show UnknownType;
import 'redirecting_factory_body.dart'
show
RedirectingFactoryBody,
isRedirectingFactory,
isRedirectingFactoryField;
List<LocatedMessage> verifyComponent(Component component, Target target,
{bool? isOutline, bool? afterConst, bool skipPlatform = false}) {
FastaVerifyingVisitor verifier = new FastaVerifyingVisitor(target,
isOutline: isOutline, afterConst: afterConst, skipPlatform: skipPlatform);
List<LocatedMessage> verifyComponent(
Target target, VerificationStage stage, Component component,
{bool skipPlatform = false}) {
FastaVerificationErrorListener listener =
new FastaVerificationErrorListener();
VerifyingVisitor verifier = new VerifyingVisitor(target, stage,
skipPlatform: skipPlatform, listener: listener);
component.accept(verifier);
return verifier.errors;
return listener.errors;
}
class FastaVerifyingVisitor extends VerifyingVisitor {
final Target target;
final List<LocatedMessage> errors = <LocatedMessage>[];
Uri? fileUri;
final List<TreeNode> treeNodeStack = <TreeNode>[];
final bool skipPlatform;
FastaVerifyingVisitor(this.target,
{bool? isOutline, bool? afterConst, required this.skipPlatform})
: super(
isOutline: isOutline,
afterConst: afterConst,
constantsAreAlwaysInlined:
target.constantsBackend.alwaysInlineConstants);
/// Invoked by all visit methods if the visited node is a [TreeNode].
void enterTreeNode(TreeNode node) {
treeNodeStack.add(node);
}
/// Invoked by all visit methods if the visited node is a [TreeNode].
void exitTreeNode(TreeNode node) {
if (treeNodeStack.isEmpty) {
throw new StateError("Attempting to exit tree node '${node}' "
"when the tree node stack is empty.");
}
if (!identical(treeNodeStack.last, node)) {
throw new StateError("Attempting to exit tree node '${node}' "
"when another node '${treeNodeStack.last}' is active.");
}
treeNodeStack.removeLast();
}
TreeNode? getLastSeenTreeNode({bool withLocation = false}) {
assert(treeNodeStack.isNotEmpty);
for (int i = treeNodeStack.length - 1; i >= 0; --i) {
TreeNode node = treeNodeStack[i];
if (withLocation && !_hasLocation(node)) continue;
return node;
}
return null;
}
TreeNode? getSameLibraryLastSeenTreeNode({bool withLocation = false}) {
if (treeNodeStack.isEmpty) return null;
// ignore: unnecessary_null_comparison
if (currentLibrary == null || currentLibrary!.fileUri == null) return null;
for (int i = treeNodeStack.length - 1; i >= 0; --i) {
TreeNode node = treeNodeStack[i];
if (withLocation && !_hasLocation(node)) continue;
if (node.location?.file != null &&
node.location!.file == currentLibrary!.fileUri) {
return node;
}
}
return null;
}
static bool _hasLocation(TreeNode node) {
return node.location != null &&
// ignore: unnecessary_null_comparison
node.location!.file != null &&
// ignore: unnecessary_null_comparison
node.fileOffset != null &&
node.fileOffset != -1;
}
static bool _isInSameLibrary(Library? library, TreeNode node) {
if (library == null) return false;
// ignore: unnecessary_null_comparison
if (library.fileUri == null) return false;
if (node.location == null) return false;
// ignore: unnecessary_null_comparison
if (node.location!.file == null) return false;
return library.fileUri == node.location!.file;
}
TreeNode? get localContext {
TreeNode? result = getSameLibraryLastSeenTreeNode(withLocation: true);
if (result == null &&
currentClassOrExtensionOrMember != null &&
_isInSameLibrary(currentLibrary, currentClassOrExtensionOrMember!)) {
result = currentClassOrExtensionOrMember;
}
return result;
}
TreeNode? get remoteContext {
TreeNode? result = getLastSeenTreeNode(withLocation: true);
if (result != null && _isInSameLibrary(currentLibrary, result)) {
result = null;
}
return result;
}
Uri checkLocation(TreeNode node, String? name, Uri fileUri) {
if (name == null || name.contains("#")) {
// TODO(ahe): Investigate if these checks can be enabled:
// if (node.fileUri != null && node is! Library) {
// problem(node, "A synthetic node shouldn't have a fileUri",
// context: node);
// }
// if (node.fileOffset != -1) {
// problem(node, "A synthetic node shouldn't have a fileOffset",
// context: node);
// }
return fileUri;
} else {
// ignore: unnecessary_null_comparison
if (fileUri == null) {
problem(node, "'$name' has no fileUri", context: node);
return fileUri;
}
if (node.fileOffset == -1 && node is! Library) {
problem(node, "'$name' has no fileOffset", context: node);
}
return fileUri;
}
}
void checkSuperInvocation(TreeNode node) {
Member? containingMember = getContainingMember(node);
if (containingMember == null) {
problem(node, 'Super call outside of any member');
} else {
if (!containingMember.containsSuperCalls) {
problem(
node, 'Super call in a member lacking TransformerFlag.superCalls');
}
}
}
Member? getContainingMember(TreeNode? node) {
while (node != null) {
if (node is Member) return node;
node = node.parent;
}
return null;
}
class FastaVerificationErrorListener implements VerificationErrorListener {
List<LocatedMessage> errors = [];
@override
void problem(TreeNode? node, String details,
{TreeNode? context, TreeNode? origin}) {
node ??= (context ?? currentClassOrExtensionOrMember);
int offset = node?.fileOffset ?? -1;
Uri? file = node?.location?.file ?? fileUri;
Uri? uri = file == null ? null : file;
void reportError(String details,
{required TreeNode? node,
required Uri? problemUri,
required int? problemOffset,
required TreeNode? context,
required TreeNode? origin}) {
Message message =
templateInternalProblemVerificationError.withArguments(details);
LocatedMessage locatedMessage = uri != null
? message.withLocation(uri, offset, noLength)
LocatedMessage locatedMessage = problemUri != null
? message.withLocation(
problemUri, problemOffset ?? TreeNode.noOffset, noLength)
: message.withoutLocation();
List<LocatedMessage>? contextMessages;
if (origin != null) {
@ -206,323 +61,6 @@ class FastaVerifyingVisitor extends VerifyingVisitor {
.report(locatedMessage, Severity.error, context: contextMessages);
errors.add(locatedMessage);
}
@override
void visitAsExpression(AsExpression node) {
enterTreeNode(node);
super.visitAsExpression(node);
if (node.fileOffset == -1) {
TreeNode? parent = node.parent;
while (parent != null) {
if (parent.fileOffset != -1) break;
parent = parent.parent;
}
problem(parent, "No offset for $node", context: node);
}
exitTreeNode(node);
}
@override
void visitExpressionStatement(ExpressionStatement node) {
// Bypass verification of the [StaticGet] in [RedirectingFactoryBody] as
// this is a static get without a getter.
if (node is! RedirectingFactoryBody) {
enterTreeNode(node);
super.visitExpressionStatement(node);
exitTreeNode(node);
}
}
@override
void visitLibrary(Library node) {
// Issue(http://dartbug.com/32530)
// 'dart:test' is used in the unit tests and isn't an actual part of the
// platform.
if (skipPlatform &&
node.importUri.isScheme('dart') &&
node.importUri.path != 'test') {
return;
}
enterTreeNode(node);
fileUri = checkLocation(node, node.name, node.fileUri);
currentLibrary = node;
super.visitLibrary(node);
currentLibrary = null;
exitTreeNode(node);
}
@override
void visitClass(Class node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name, node.fileUri);
super.visitClass(node);
exitTreeNode(node);
}
@override
void visitExtension(Extension node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name, node.fileUri);
super.visitExtension(node);
exitTreeNode(node);
}
@override
void visitField(Field node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name.text, node.fileUri);
super.visitField(node);
exitTreeNode(node);
}
@override
void visitProcedure(Procedure node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name.text, node.fileUri);
// TODO(cstefantsova): Investigate why some redirecting factory bodies
// retain the shape, but aren't of the RedirectingFactoryBody type.
bool hasBody = isRedirectingFactory(node) ||
RedirectingFactoryBody.hasRedirectingFactoryBodyShape(node);
bool hasFlag = node.isRedirectingFactory;
if (hasFlag && !hasBody) {
problem(
node,
"Procedure '${node.name}' doesn't have a body "
"of a redirecting factory, but has the "
"'isRedirectingFactory' bit set.");
}
super.visitProcedure(node);
exitTreeNode(node);
}
bool isNullType(DartType node) => node is NullType;
bool isObjectClass(Class c) {
return c.name == "Object" &&
c.enclosingLibrary.importUri.isScheme("dart") &&
c.enclosingLibrary.importUri.path == "core";
}
bool isTopType(DartType node) {
return node is DynamicType ||
node is VoidType ||
node is InterfaceType &&
isObjectClass(node.classNode) &&
(node.nullability == Nullability.nullable ||
node.nullability == Nullability.legacy) ||
node is FutureOrType && isTopType(node.typeArgument);
}
bool isFutureOrNull(DartType node) {
return isNullType(node) ||
node is FutureOrType && isFutureOrNull(node.typeArgument);
}
@override
void defaultDartType(DartType node) {
final TreeNode? localContext = this.localContext;
final TreeNode? remoteContext = this.remoteContext;
if (node is UnknownType) {
problem(localContext, "Unexpected appearance of the unknown type.",
origin: remoteContext);
}
// TODO(johnniwinther): This check wasn't called from InterfaceType and
// is currently very broken. Disabling for now.
/*bool isTypeCast = localContext != null &&
localContext is AsExpression &&
localContext.isTypeError;
// Don't check cases like foo(x as{TypeError} T). In cases where foo comes
// from a library with a different opt-in status than the current library,
// the check may not be necessary. For now, just skip all type-error casts.
// TODO(cstefantsova): Implement a more precise analysis.
bool isFromAnotherLibrary = remoteContext != null || isTypeCast;
// Checking for non-legacy types in opt-out libraries.
{
bool neverLegacy = isNullType(node) ||
isFutureOrNull(node) ||
isTopType(node) ||
node is InvalidType ||
node is NeverType ||
node is BottomType;
// TODO(cstefantsova): Consider checking types coming from other
// libraries.
bool expectedLegacy = !isFromAnotherLibrary &&
!currentLibrary.isNonNullableByDefault &&
!neverLegacy;
if (expectedLegacy && node.nullability != Nullability.legacy) {
problem(localContext,
"Found a non-legacy type '${node}' in an opted-out library.",
origin: remoteContext);
}
}
// Checking for legacy types in opt-in libraries.
{
Nullability nodeNullability =
node is InvalidType ? Nullability.undetermined : node.nullability;
// TODO(cstefantsova): Consider checking types coming from other
// libraries.
if (!isFromAnotherLibrary &&
currentLibrary.isNonNullableByDefault &&
nodeNullability == Nullability.legacy) {
problem(localContext,
"Found a legacy type '${node}' in an opted-in library.",
origin: remoteContext);
}
}*/
super.defaultDartType(node);
}
@override
void visitFunctionType(FunctionType node) {
if (node.typeParameters.isNotEmpty) {
for (TypeParameter typeParameter in node.typeParameters) {
if (typeParameter.parent != null) {
problem(
localContext,
"Type parameters of function types shouldn't have parents: "
"$node.");
}
}
}
super.visitFunctionType(node);
}
@override
void visitInterfaceType(InterfaceType node) {
if (isNullType(node) && node.nullability != Nullability.nullable) {
problem(localContext, "Found a not nullable Null type: ${node}");
}
super.visitInterfaceType(node);
}
@override
void visitSuperMethodInvocation(SuperMethodInvocation node) {
enterTreeNode(node);
checkSuperInvocation(node);
super.visitSuperMethodInvocation(node);
exitTreeNode(node);
}
@override
void visitSuperPropertyGet(SuperPropertyGet node) {
enterTreeNode(node);
checkSuperInvocation(node);
super.visitSuperPropertyGet(node);
exitTreeNode(node);
}
@override
void visitSuperPropertySet(SuperPropertySet node) {
enterTreeNode(node);
checkSuperInvocation(node);
super.visitSuperPropertySet(node);
exitTreeNode(node);
}
@override
void visitStaticInvocation(StaticInvocation node) {
enterTreeNode(node);
super.visitStaticInvocation(node);
exitTreeNode(node);
}
void _checkConstructorTearOff(Node node, Member tearOffTarget) {
if (tearOffTarget.enclosingLibrary.importUri.isScheme('dart')) {
// Platform libraries are not compilation with test flags and might
// contain tear-offs not expected when testing lowerings.
return;
}
if (currentMember != null && isRedirectingFactoryField(currentMember!)) {
// The encoding of the redirecting factory field uses
// [ConstructorTearOffConstant] nodes also when lowerings are enabled.
return;
}
if (tearOffTarget is Constructor &&
target.isConstructorTearOffLoweringEnabled) {
problem(
node is TreeNode ? node : getLastSeenTreeNode(),
'${node.runtimeType} nodes for generative constructors should be '
'lowered for target "${target.name}".');
}
if (tearOffTarget is Procedure &&
tearOffTarget.isFactory &&
target.isFactoryTearOffLoweringEnabled) {
problem(
node is TreeNode ? node : getLastSeenTreeNode(),
'${node.runtimeType} nodes for factory constructors should be '
'lowered for target "${target.name}".');
}
}
@override
void visitConstructorTearOff(ConstructorTearOff node) {
_checkConstructorTearOff(node, node.target);
super.visitConstructorTearOff(node);
}
@override
void visitConstructorTearOffConstant(ConstructorTearOffConstant node) {
_checkConstructorTearOff(node, node.target);
super.visitConstructorTearOffConstant(node);
}
void _checkTypedefTearOff(Node node) {
if (target.isTypedefTearOffLoweringEnabled) {
problem(
node is TreeNode ? node : getLastSeenTreeNode(),
'${node.runtimeType} nodes for typedefs should be '
'lowered for target "${target.name}".');
}
}
@override
void visitTypedefTearOff(TypedefTearOff node) {
_checkTypedefTearOff(node);
super.visitTypedefTearOff(node);
}
@override
void visitTypedefTearOffConstant(TypedefTearOffConstant node) {
_checkTypedefTearOff(node);
super.visitTypedefTearOffConstant(node);
}
void _checkRedirectingFactoryTearOff(Node node) {
if (target.isRedirectingFactoryTearOffLoweringEnabled) {
problem(
node is TreeNode ? node : getLastSeenTreeNode(),
'ConstructorTearOff nodes for redirecting factories should be '
'lowered for target "${target.name}".');
}
}
@override
void visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) {
_checkRedirectingFactoryTearOff(node);
super.visitRedirectingFactoryTearOff(node);
}
@override
void visitRedirectingFactoryTearOffConstant(
RedirectingFactoryTearOffConstant node) {
_checkRedirectingFactoryTearOff(node);
super.visitRedirectingFactoryTearOffConstant(node);
}
@override
void defaultTreeNode(TreeNode node) {
enterTreeNode(node);
super.defaultTreeNode(node);
exitTreeNode(node);
}
}
void verifyGetStaticType(TypeEnvironment env, Component component,

View file

@ -44,7 +44,7 @@ import '../identifiers.dart';
import '../kernel/hierarchy/hierarchy_builder.dart';
import '../kernel/hierarchy/hierarchy_node.dart';
import '../kernel/kernel_helper.dart';
import '../kernel/redirecting_factory_body.dart'
import 'package:kernel/src/redirecting_factory_body.dart'
show RedirectingFactoryBody, redirectingName;
import '../kernel/type_algorithms.dart' show computeTypeVariableBuilderVariance;
import '../kernel/utils.dart' show compareProcedures;

View file

@ -4,6 +4,8 @@
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/src/redirecting_factory_body.dart'
show getRedirectingFactoryBody, RedirectingFactoryBody;
import 'package:kernel/type_algebra.dart';
import 'package:kernel/type_environment.dart';
@ -18,8 +20,6 @@ import '../dill/dill_member_builder.dart';
import '../kernel/constructor_tearoff_lowering.dart';
import '../kernel/hierarchy/class_member.dart';
import '../kernel/kernel_helper.dart';
import '../kernel/redirecting_factory_body.dart'
show getRedirectingFactoryBody, RedirectingFactoryBody;
import '../messages.dart'
show
messageConstFactoryRedirectionToNonConst,

View file

@ -56,7 +56,7 @@ class SourceTypeAliasBuilder extends TypeAliasBuilderImpl {
(new Typedef(name, null,
typeParameters: TypeVariableBuilder.typeParametersFromBuilders(
_typeVariables),
fileUri: parent.library.fileUri,
fileUri: parent.fileUri,
reference: referenceFrom?.reference)
..fileOffset = charOffset),
super(metadata, name, parent, charOffset);

View file

@ -10,6 +10,7 @@ import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, ClassHierarchyBase;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/src/norm.dart';
import 'package:kernel/src/redirecting_factory_body.dart';
import 'package:kernel/type_environment.dart';
import '../../base/instrumentation.dart' show Instrumentation;
@ -19,7 +20,6 @@ import '../kernel/hierarchy/members_builder.dart' show ClassMembersBuilder;
import '../kernel/implicit_field_type.dart';
import '../kernel/internal_ast.dart';
import '../kernel/kernel_helper.dart';
import '../kernel/redirecting_factory_body.dart';
import '../source/source_constructor_builder.dart';
import '../source/source_library_builder.dart' show SourceLibraryBuilder;
import 'factor_type.dart';

View file

@ -14,6 +14,7 @@ import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/verifier.dart' show VerificationStage;
import 'api_prototype/file_system.dart' show FileSystem;
import 'api_prototype/front_end.dart' show CompilerOptions, CompilerResult;
@ -145,8 +146,8 @@ Future<CompilerResult> _buildInternal(
List<int>? summary = null;
if (buildSummary) {
if (options.verify) {
for (LocatedMessage error
in verifyComponent(summaryComponent, options.target)) {
for (LocatedMessage error in verifyComponent(
options.target, VerificationStage.outline, summaryComponent)) {
options.report(error, Severity.error);
}
}

View file

@ -9,11 +9,11 @@ import 'package:front_end/src/api_prototype/terminal_color_support.dart';
import 'package:front_end/src/compute_platform_binaries_location.dart';
import 'package:front_end/src/fasta/command_line_reporting.dart';
import 'package:front_end/src/fasta/fasta_codes.dart';
import 'package:front_end/src/fasta/kernel/redirecting_factory_body.dart';
import 'package:front_end/src/kernel_generator_impl.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/src/redirecting_factory_body.dart';
import 'package:kernel/type_environment.dart';
typedef PerformAnalysisFunction = void Function(

View file

@ -119,6 +119,7 @@ import 'package:kernel/target/targets.dart'
import 'package:kernel/type_environment.dart'
show StaticTypeContext, TypeEnvironment;
import 'package:kernel/verifier.dart' show VerificationStage;
import 'package:testing/testing.dart'
show
Chain,
@ -367,7 +368,9 @@ class FastaContext extends ChainContext with MatchContext {
: steps = <Step>[
new Outline(compileMode, updateComments: updateComments),
const Print(),
new Verify(compileMode)
new Verify(compileMode == CompileMode.full
? VerificationStage.afterConstantEvaluation
: VerificationStage.outline)
] {
String prefix;
String infix;
@ -407,7 +410,7 @@ class FastaContext extends ChainContext with MatchContext {
switch (compileMode) {
case CompileMode.full:
steps.add(const Transform());
steps.add(const Verify(CompileMode.full));
steps.add(const Verify(VerificationStage.afterModularTransformations));
steps.add(const StressConstantEvaluatorStep());
if (!ignoreExpectations) {
steps.add(new MatchExpectation("$prefix$infix.transformed.expect",
@ -2486,9 +2489,9 @@ class Transform extends Step<ComponentResult, ComponentResult, FastaContext> {
}
class Verify extends Step<ComponentResult, ComponentResult, FastaContext> {
final CompileMode compileMode;
final VerificationStage stage;
const Verify(this.compileMode);
const Verify(this.stage);
@override
String get name => "verify";
@ -2518,8 +2521,8 @@ class Verify extends Step<ComponentResult, ComponentResult, FastaContext> {
result.options, (compilerContext) async {
compilerContext.uriToSource.addAll(component.uriToSource);
List<LocatedMessage> verificationErrors = verifyComponent(
component, result.options.target,
isOutline: compileMode == CompileMode.outline, skipPlatform: true);
result.options.target, stage, component,
skipPlatform: true);
assert(verificationErrors.isEmpty || messages.isNotEmpty);
if (messages.isEmpty) {
return pass(result);

View file

@ -29,6 +29,7 @@ import 'package:front_end/src/fasta/kernel/verifier.dart' show verifyComponent;
import 'package:kernel/ast.dart' show Component;
import 'package:kernel/target/targets.dart';
import 'package:kernel/verifier.dart' show VerificationStage;
const Map<String, String> files = const <String, String>{
"repro.dart": """
@ -96,8 +97,9 @@ Future<void> test() async {
List<Object> errors = await CompilerContext.runWithOptions(
new ProcessedOptions(options: options, inputs: inputs),
(_) => new Future<List<Object>>.value(
verifyComponent(component, options.target!, skipPlatform: true)));
(_) => new Future<List<Object>>.value(verifyComponent(options.target!,
VerificationStage.afterModularTransformations, component,
skipPlatform: true)));
serializeComponent(component);

View file

@ -538,6 +538,9 @@ Future<void> compilePlatformInternal(CompilerContext c, Uri fullOutput,
new File.fromUri(outlineOutput).writeAsBytesSync(result.summary!);
c.options.ticker.logMs("Wrote outline to ${outlineOutput.toFilePath()}");
// TODO(johnniwinther): Enable verification.
//verifyComponent(c.options.target,
// VerificationStage.afterModularTransformations, result.component!);
await writeComponentToFile(result.component!, fullOutput);
c.options.ticker.logMs("Wrote component to ${fullOutput.toFilePath()}");

View file

@ -13,7 +13,9 @@ import 'package:front_end/src/api_unstable/vm.dart'
import 'package:frontend_server/starter.dart';
import 'package:kernel/ast.dart' show Component;
import 'package:kernel/kernel.dart' show loadComponentFromBytes;
import 'package:kernel/verifier.dart' show verifyComponent;
import 'package:kernel/target/targets.dart';
import 'package:kernel/verifier.dart' show VerificationStage, verifyComponent;
import 'package:vm/kernel_front_end.dart';
main(List<String> args) async {
String? flutterDir;
@ -244,11 +246,13 @@ Future<List<String>> attemptStuff(
List<int> platformData =
File.fromUri(flutterPlatformDirectory.uri.resolve("platform_strong.dill"))
.readAsBytesSync();
final String targetName = 'flutter';
final Target target = createFrontEndTarget(targetName)!;
final List<String> args = <String>[
'--sdk-root',
flutterPlatformDirectory.path,
'--incremental',
'--target=flutter',
'--target=$targetName',
'--packages',
packageConfig.path,
'--output-dill=${dillFile.path}',
@ -303,7 +307,8 @@ Future<List<String>> attemptStuff(
List<int> resultBytes = dillFile.readAsBytesSync();
Component component = loadComponentFromBytes(platformData);
component = loadComponentFromBytes(resultBytes, component);
verifyComponent(component);
verifyComponent(
target, VerificationStage.afterModularTransformations, component);
logger
.log(" => verified in ${stopwatch2.elapsedMilliseconds} ms.");
}

View file

@ -18,10 +18,12 @@ import 'package:frontend_server/starter.dart';
import 'package:kernel/ast.dart' show Component;
import 'package:kernel/binary/ast_to_binary.dart';
import 'package:kernel/kernel.dart' show loadComponentFromBinary;
import 'package:kernel/verifier.dart' show verifyComponent;
import 'package:kernel/target/targets.dart';
import 'package:kernel/verifier.dart' show VerificationStage, verifyComponent;
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:vm/incremental_compiler.dart';
import 'package:vm/kernel_front_end.dart';
class _MockedBinaryPrinter implements BinaryPrinter {
@override
@ -1164,6 +1166,8 @@ class BarState extends State<FizzWidget> {
expect(dillFile.existsSync(), equals(false));
// First compile app entry point A.
final String targetName = 'vm';
final Target target = createFrontEndTarget(targetName)!;
final List<String> args = <String>[
'--sdk-root=${sdkRoot.toFilePath()}',
'--incremental',
@ -1212,7 +1216,8 @@ class BarState extends State<FizzWidget> {
// Verifiable (together with the platform file).
component =
loadComponentFromBinary(platformKernel.toFilePath(), component);
verifyComponent(component);
verifyComponent(target,
VerificationStage.afterModularTransformations, component);
}
});
expect(await result, 0);
@ -1285,6 +1290,8 @@ class BarState extends State<FizzWidget> {
expect(dillFile.existsSync(), equals(false));
// First compile app entry point A.
final String targetName = 'vm';
final Target target = createFrontEndTarget(targetName)!;
final List<String> args = <String>[
'--sdk-root=${sdkRoot.toFilePath()}',
'--incremental',
@ -1332,7 +1339,8 @@ class BarState extends State<FizzWidget> {
// Verifiable (together with the platform file).
component =
loadComponentFromBinary(platformKernel.toFilePath(), component);
verifyComponent(component);
verifyComponent(target,
VerificationStage.afterModularTransformations, component);
}
});
expect(await result, 0);
@ -2850,6 +2858,8 @@ e() {
var dillFile = File('${tempDir.path}/full.dill');
var incrementalDillFile = File('${tempDir.path}/incremental.dill');
expect(dillFile.existsSync(), equals(false));
final String targetName = 'vm';
final Target target = createFrontEndTarget(targetName)!;
final List<String> args = <String>[
'--sdk-root=${sdkRoot.toFilePath()}',
'--incremental',
@ -2915,7 +2925,7 @@ e() {
component =
loadComponentFromBinary(platformKernel.toFilePath(), component);
expect(component.mainMethod, isNotNull);
verifyComponent(component);
verifyComponent(target, VerificationStage.afterModularTransformations, component);
count += 1;
@ -2940,7 +2950,7 @@ e() {
component =
loadComponentFromBinary(platformKernel.toFilePath(), component);
expect(component.mainMethod, isNotNull);
verifyComponent(component);
verifyComponent(target, VerificationStage.afterModularTransformations, component);
count += 1;

View file

@ -1,4 +1,4 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// Copyright (c) 2023, 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.
@ -8,7 +8,11 @@ import 'package:kernel/ast.dart';
import 'package:kernel/type_algebra.dart' show Substitution;
import 'body_builder.dart' show EnsureLoaded;
abstract class EnsureLoaded {
void ensureLoaded(Member? member);
bool isLoaded(Member? member);
}
/// Name of variable used to wrap expression by the VMs FFI Finalizable
/// transformation.

View file

@ -8,6 +8,7 @@ import '../ast.dart';
import '../class_hierarchy.dart';
import '../core_types.dart';
import '../reference_from_index.dart';
import '../verifier.dart';
import 'changed_structure_notifier.dart';
final List<String> targetNames = targets.keys.toList();
@ -539,6 +540,9 @@ abstract class Target {
ConstantsBackend get constantsBackend;
/// Object that defines how AST nodes are verified for this [Target].
Verification get verification => const Verification();
/// Returns an [DartLibrarySupport] the defines which, if any, of the
/// `dart:` libraries supported in the platform, that should not be
/// considered supported when queried in conditional imports and

View file

@ -5,17 +5,82 @@
library kernel.checks;
import 'ast.dart';
import 'src/redirecting_factory_body.dart';
import 'target/targets.dart';
import 'transformations/flags.dart';
import 'type_environment.dart' show StatefulStaticTypeContext, TypeEnvironment;
void verifyComponent(Component component,
{bool? isOutline,
bool? afterConst,
bool constantsAreAlwaysInlined = true}) {
VerifyingVisitor.check(component,
isOutline: isOutline,
afterConst: afterConst,
constantsAreAlwaysInlined: constantsAreAlwaysInlined);
/// Stages at which verification can occur.
///
/// These can be used to enforce different invariants during different stages
/// of the compilation.
enum VerificationStage {
/// Verification after the outline compilation.
outline,
/// Verification after the body, aka full, compilation, but before pre-
/// constant evaluation transformations have been performed.
beforePreConstantEvaluationTransformations,
/// Verification after pre- constant evaluation transformations have been
/// performed but before constant evaluation.
beforeConstantEvaluation,
/// Verification after constant evaluation but before modular transformations
/// have been performed.
afterConstantEvaluation,
/// Verification after modular transformations have been performed.
///
/// This is final stage of a normal compilation.
afterModularTransformations,
/// Verification after global transformations have been performed.
///
/// The global transformation is an additional step performed by some
/// backends which is not triggered by the front end compilation itself.
afterGlobalTransformations,
;
bool operator <(VerificationStage other) => index < other.index;
bool operator <=(VerificationStage other) => index <= other.index;
bool operator >(VerificationStage other) => index > other.index;
bool operator >=(VerificationStage other) => index >= other.index;
}
/// Interface that defines how the AST is verified.
class Verification {
const Verification();
/// Returns `true` if [node] is allowed to have no file offset.
bool allowNoFileOffset(VerificationStage stage, TreeNode node) {
return node is Library;
}
/// Returns `true` if [node] is allowed to have location with a file offset
/// that is not in the range of the enclosing file.
bool allowInvalidLocation(VerificationStage stage, TreeNode node) {
return false;
}
}
void verifyComponent(
Target target, VerificationStage stage, Component component,
{bool skipPlatform = false}) {
VerifyingVisitor.check(target, stage, component, skipPlatform: skipPlatform);
}
class VerificationErrorListener {
const VerificationErrorListener();
void reportError(String details,
{required TreeNode? node,
required Uri? problemUri,
required int? problemOffset,
required TreeNode? context,
required TreeNode? origin}) {
throw new VerificationError(context, node, details);
}
}
class VerificationError {
@ -53,6 +118,15 @@ enum TypedefState { Done, BeingChecked }
///
/// This does not include any kind of type checking.
class VerifyingVisitor extends RecursiveResultVisitor<void> {
final Target target;
Uri? fileUri;
final VerificationErrorListener listener;
final List<TreeNode> treeNodeStack = <TreeNode>[];
final bool skipPlatform;
final Set<Class> classes = new Set<Class>();
final Set<Typedef> typedefs = new Set<Typedef>();
Set<TypeParameter> typeParametersInScope = new Set<TypeParameter>();
@ -63,17 +137,8 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
final Set<Constant> seenConstants = <Constant>{};
bool classTypeParametersAreInScope = false;
/// If true, relax certain checks for *outline* mode. For example, don't
/// attempt to validate constructor initializers.
final bool isOutline;
/// If true, assume that constant evaluation has been performed (with a
/// target that did not opt out of any of the constant inlining) and report
/// a verification error for anything that should have been removed by it.
final bool afterConst;
/// If true, constant fields and local variables are expected to be inlined.
final bool constantsAreAlwaysInlined;
/// The compilation stage at which this verification is performed.
final VerificationStage stage;
AsyncMarker currentAsyncMarker = AsyncMarker.Sync;
@ -98,26 +163,35 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
TreeNode? get currentClassOrExtensionOrMember =>
currentMember ?? currentClass ?? currentExtension ?? currentInlineClass;
static void check(Component component,
{bool? isOutline,
bool? afterConst,
required bool constantsAreAlwaysInlined}) {
component.accept(new VerifyingVisitor(
isOutline: isOutline,
afterConst: afterConst,
constantsAreAlwaysInlined: constantsAreAlwaysInlined));
static void check(Target target, VerificationStage stage, Component component,
{required bool skipPlatform}) {
component.accept(
new VerifyingVisitor(target, stage, skipPlatform: skipPlatform));
}
VerifyingVisitor(
{bool? isOutline,
bool? afterConst,
required this.constantsAreAlwaysInlined})
: isOutline = isOutline ?? false,
afterConst = afterConst ?? !(isOutline ?? false);
VerifyingVisitor(this.target, this.stage,
{required this.skipPlatform,
VerificationErrorListener this.listener =
const VerificationErrorListener()});
/// If true, relax certain checks for *outline* mode. For example, don't
/// attempt to validate constructor initializers.
bool get isOutline => stage == VerificationStage.outline;
/// If true, assume that constant evaluation has been performed (with a
/// target that did not opt out of any of the constant inlining) and report
/// a verification error for anything that should have been removed by it.
bool get afterConst => stage >= VerificationStage.afterConstantEvaluation;
/// If true, constant fields and local variables are expected to be inlined.
bool get constantsAreAlwaysInlined =>
target.constantsBackend.alwaysInlineConstants;
@override
void defaultTreeNode(TreeNode node) {
enterTreeNode(node);
visitChildren(node);
exitTreeNode(node);
}
@override
@ -132,9 +206,21 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
constant.visitChildren(this);
}
void problem(TreeNode? node, String details, {TreeNode? context}) {
context ??= currentClassOrExtensionOrMember;
throw new VerificationError(context, node, details);
void problem(TreeNode? node, String details,
{TreeNode? context, TreeNode? origin}) {
TreeNode? problemNode = node ?? context ?? currentClassOrExtensionOrMember;
int offset = problemNode?.fileOffset ?? -1;
Location? location = problemNode != null
? _getLocation(problemNode, allowInvalidLocation: true)
: null;
Uri? file = location?.file ?? fileUri;
Uri? uri = file == null ? null : file;
listener.reportError(details,
problemUri: uri,
problemOffset: offset,
node: node,
context: context ?? currentClassOrExtensionOrMember,
origin: origin);
}
TreeNode? enterParent(TreeNode node) {
@ -171,9 +257,11 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
}
void visitWithLocalScope(TreeNode node) {
enterTreeNode(node);
int stackHeight = enterLocalScope();
visitChildren(node);
exitLocalScope(stackHeight);
exitTreeNode(node);
}
void declareMember(Member member) {
@ -261,13 +349,27 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
@override
void visitLibrary(Library node) {
// Issue(http://dartbug.com/32530)
// 'dart:test' is used in the unit tests and isn't an actual part of the
// platform.
if (skipPlatform &&
node.importUri.isScheme('dart') &&
node.importUri.path != 'test') {
return;
}
enterTreeNode(node);
fileUri = checkLocation(node, node.name, node.fileUri);
currentLibrary = node;
super.visitLibrary(node);
currentLibrary = null;
exitTreeNode(node);
}
@override
void visitExtension(Extension node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name, node.fileUri);
currentExtension = node;
declareTypeParameters(node.typeParameters);
final TreeNode? oldParent = enterParent(node);
@ -275,10 +377,13 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
exitParent(oldParent);
undeclareTypeParameters(node.typeParameters);
currentExtension = null;
exitTreeNode(node);
}
@override
void visitInlineClass(InlineClass node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name, node.fileUri);
currentInlineClass = node;
declareTypeParameters(node.typeParameters);
final TreeNode? oldParent = enterParent(node);
@ -286,6 +391,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
exitParent(oldParent);
undeclareTypeParameters(node.typeParameters);
currentInlineClass = null;
exitTreeNode(node);
}
void checkTypedef(Typedef node) {
@ -295,6 +401,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
problem(node, "The typedef '$node' refers to itself", context: node);
}
assert(state == null);
enterTreeNode(node);
typedefState[node] = TypedefState.BeingChecked;
Set<TypeParameter> savedTypeParameters = typeParametersInScope;
typeParametersInScope = node.typeParameters.toSet();
@ -306,17 +413,23 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
currentParent = savedParent;
typeParametersInScope = savedTypeParameters;
typedefState[node] = TypedefState.Done;
exitTreeNode(node);
}
@override
void visitTypedef(Typedef node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name, node.fileUri);
checkTypedef(node);
// Enter and exit the node to check the parent pointer on the typedef node.
exitParent(enterParent(node));
exitTreeNode(node);
}
@override
void visitField(Field node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name.text, node.fileUri);
currentMember = node;
TreeNode? oldParent = enterParent(node);
bool isTopLevel = node.parent == currentLibrary;
@ -356,10 +469,27 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
visitList(node.annotations, this);
exitParent(oldParent);
currentMember = null;
exitTreeNode(node);
}
@override
void visitProcedure(Procedure node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name.text, node.fileUri);
// TODO(cstefantsova): Investigate why some redirecting factory bodies
// retain the shape, but aren't of the RedirectingFactoryBody type.
bool hasBody = isRedirectingFactory(node) ||
RedirectingFactoryBody.hasRedirectingFactoryBodyShape(node);
bool hasFlag = node.isRedirectingFactory;
if (hasFlag && !hasBody) {
problem(
node,
"Procedure '${node.name}' doesn't have a body "
"of a redirecting factory, but has the "
"'isRedirectingFactory' bit set.");
}
currentMember = node;
TreeNode? oldParent = enterParent(node);
classTypeParametersAreInScope = !node.isStatic;
@ -423,10 +553,13 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
}
}*/
currentMember = null;
exitTreeNode(node);
}
@override
void visitConstructor(Constructor node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name.text, node.fileUri);
currentMember = node;
classTypeParametersAreInScope = true;
// The constructor member needs special treatment due to parameters being
@ -456,10 +589,13 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
}*/
classTypeParametersAreInScope = false;
currentMember = null;
exitTreeNode(node);
}
@override
void visitClass(Class node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name, node.fileUri);
currentClass = node;
declareTypeParameters(node.typeParameters);
TreeNode? oldParent = enterParent(node);
@ -473,10 +609,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
exitParent(oldParent);
undeclareTypeParameters(node.typeParameters);
currentClass = null;
exitTreeNode(node);
}
@override
void visitFunctionNode(FunctionNode node) {
enterTreeNode(node);
declareTypeParameters(node.typeParameters);
bool savedInCatchBlock = inCatchBlock;
AsyncMarker savedAsyncMarker = currentAsyncMarker;
@ -492,10 +630,21 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
inCatchBlock = savedInCatchBlock;
currentAsyncMarker = savedAsyncMarker;
undeclareTypeParameters(node.typeParameters);
exitTreeNode(node);
}
@override
void visitFunctionType(FunctionType node) {
if (node.typeParameters.isNotEmpty) {
for (TypeParameter typeParameter in node.typeParameters) {
if (typeParameter.parent != null) {
problem(
localContext,
"Type parameters of function types shouldn't have parents: "
"$node.");
}
}
}
for (int i = 1; i < node.namedParameters.length; ++i) {
if (node.namedParameters[i - 1].compareTo(node.namedParameters[i]) >= 0) {
problem(currentParent,
@ -544,6 +693,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
@override
void visitBlockExpression(BlockExpression node) {
enterTreeNode(node);
int stackHeight = enterLocalScope();
// Do not visit the block directly because the value expression needs to
// be in its scope.
@ -556,6 +706,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
node.value.accept(this);
exitParent(oldParent);
exitLocalScope(stackHeight);
exitTreeNode(node);
}
@override
@ -613,6 +764,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
@override
void visitVariableDeclaration(VariableDeclaration node) {
enterTreeNode(node);
TreeNode? parent = node.parent;
if (parent is! Block &&
!(parent is Catch && parent.body != node) &&
@ -640,25 +792,31 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
}
}
}
exitTreeNode(node);
}
@override
void visitVariableGet(VariableGet node) {
enterTreeNode(node);
checkVariableInScope(node.variable, node);
visitChildren(node);
if (constantsAreAlwaysInlined && afterConst && node.variable.isConst) {
problem(node, "VariableGet of const variable '${node.variable}'.");
}
exitTreeNode(node);
}
@override
void visitVariableSet(VariableSet node) {
enterTreeNode(node);
checkVariableInScope(node.variable, node);
visitChildren(node);
exitTreeNode(node);
}
@override
void visitStaticGet(StaticGet node) {
enterTreeNode(node);
visitChildren(node);
// ignore: unnecessary_null_comparison
if (node.target == null) {
@ -684,10 +842,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
node.target.isConst) {
problem(node, "StaticGet of const field '${node.target}'.");
}
exitTreeNode(node);
}
@override
void visitStaticSet(StaticSet node) {
enterTreeNode(node);
visitChildren(node);
// ignore: unnecessary_null_comparison
if (node.target == null) {
@ -699,10 +859,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
if (node.target.isInstanceMember) {
problem(node, "StaticSet to '${node.target}' that's an instance member.");
}
exitTreeNode(node);
}
@override
void visitStaticInvocation(StaticInvocation node) {
enterTreeNode(node);
checkTargetedInvocation(node.target, node);
if (node.target.isInstanceMember) {
problem(node,
@ -720,10 +882,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
if (afterConst && node.isConst && !inUnevaluatedConstant) {
problem(node, "Constant StaticInvocation.");
}
exitTreeNode(node);
}
@override
void visitTypedefTearOff(TypedefTearOff node) {
_checkTypedefTearOff(node);
declareTypeParameters(node.typeParameters);
super.visitTypedefTearOff(node);
undeclareTypeParameters(node.typeParameters);
@ -755,6 +919,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
@override
void visitConstructorInvocation(ConstructorInvocation node) {
enterTreeNode(node);
checkTargetedInvocation(node.target, node);
if (node.target.enclosingClass.isAbstract) {
problem(node, "ConstructorInvocation of abstract class.");
@ -768,6 +933,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
if (afterConst && node.isConst) {
problem(node, "Invocation of const constructor '${node.target}'.");
}
exitTreeNode(node);
}
bool areArgumentsCompatible(Arguments arguments, FunctionNode function) {
@ -791,37 +957,46 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
@override
void visitListLiteral(ListLiteral node) {
enterTreeNode(node);
visitChildren(node);
if (afterConst && node.isConst) {
problem(node, "Constant list literal.");
}
exitTreeNode(node);
}
@override
void visitSetLiteral(SetLiteral node) {
enterTreeNode(node);
visitChildren(node);
if (afterConst && node.isConst) {
problem(node, "Constant set literal.");
}
exitTreeNode(node);
}
@override
void visitMapLiteral(MapLiteral node) {
enterTreeNode(node);
visitChildren(node);
if (afterConst && node.isConst) {
problem(node, "Constant map literal.");
}
exitTreeNode(node);
}
@override
void visitSymbolLiteral(SymbolLiteral node) {
enterTreeNode(node);
if (afterConst) {
problem(node, "Symbol literal.");
}
exitTreeNode(node);
}
@override
void visitContinueSwitchStatement(ContinueSwitchStatement node) {
enterTreeNode(node);
// ignore: unnecessary_null_comparison
if (node.target == null) {
problem(node, "No target.");
@ -830,10 +1005,14 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
} else {
SwitchStatement statement = node.target.parent as SwitchStatement;
for (SwitchCase switchCase in statement.cases) {
if (switchCase == node.target) return;
if (switchCase == node.target) {
exitTreeNode(node);
return;
}
}
problem(node, "Switch case isn't child of parent.");
}
exitTreeNode(node);
}
@override
@ -930,6 +1109,9 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
@override
void visitInterfaceType(InterfaceType node) {
if (isNullType(node) && node.nullability != Nullability.nullable) {
problem(localContext, "Found a not nullable Null type: ${node}");
}
node.visitChildren(this);
if (node.typeArguments.length != node.classNode.typeParameters.length) {
problem(
@ -972,10 +1154,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
@override
void visitConstantExpression(ConstantExpression node) {
enterTreeNode(node);
bool oldInConstant = inConstant;
inConstant = true;
visitChildren(node);
inConstant = oldInConstant;
exitTreeNode(node);
}
@override
@ -990,6 +1174,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
@override
void visitTypedefTearOffConstant(TypedefTearOffConstant node) {
_checkTypedefTearOff(node);
declareTypeParameters(node.parameters);
super.visitTypedefTearOffConstant(node);
undeclareTypeParameters(node.parameters);
@ -1038,6 +1223,349 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
}
super.visitInstanceSet(node);
}
/// Invoked by all visit methods if the visited node is a [TreeNode].
// TODO(johnniwinther): Merge this with enter/exitParent.
void enterTreeNode(TreeNode node) {
treeNodeStack.add(node);
}
/// Invoked by all visit methods if the visited node is a [TreeNode].
void exitTreeNode(TreeNode node) {
if (treeNodeStack.isEmpty) {
throw new StateError("Attempting to exit tree node '${node}' "
"when the tree node stack is empty.");
}
if (!identical(treeNodeStack.last, node)) {
throw new StateError("Attempting to exit tree node '${node}' "
"when another node '${treeNodeStack.last}' is active.");
}
treeNodeStack.removeLast();
}
TreeNode? getLastSeenTreeNode({bool withLocation = false}) {
assert(treeNodeStack.isNotEmpty);
for (int i = treeNodeStack.length - 1; i >= 0; --i) {
TreeNode node = treeNodeStack[i];
if (withLocation && !_hasLocation(node)) continue;
return node;
}
return null;
}
TreeNode? getSameLibraryLastSeenTreeNode({bool withLocation = false}) {
if (treeNodeStack.isEmpty) return null;
// ignore: unnecessary_null_comparison
if (currentLibrary == null || currentLibrary!.fileUri == null) return null;
for (int i = treeNodeStack.length - 1; i >= 0; --i) {
TreeNode node = treeNodeStack[i];
if (withLocation && !_hasLocation(node)) continue;
Location? location = _getLocation(node);
if (location != null && location.file == currentLibrary!.fileUri) {
return node;
}
}
return null;
}
/// Returns the `TreeNode.location` while handling [RangeError]s caused by
/// file offsets not within the range of the enclosing file.
Location? _getLocation(TreeNode node, {bool allowInvalidLocation = false}) {
try {
return node.location;
} on RangeError catch (e) {
if (allowInvalidLocation ||
target.verification.allowInvalidLocation(stage, node)) {
return null;
}
problem(
node,
"Invalid location with target '${target.name}' on "
"${node} (${node.runtimeType}): $e");
}
return null;
}
bool _hasLocation(TreeNode node) {
Location? location = _getLocation(node);
return location != null && node.fileOffset != TreeNode.noOffset;
}
bool _isInSameLibrary(Library? library, TreeNode node) {
if (library == null) return false;
Location? location = _getLocation(node);
if (location == null) return false;
return library.fileUri == location.file;
}
TreeNode? get localContext {
TreeNode? result = getSameLibraryLastSeenTreeNode(withLocation: true);
if (result == null &&
currentClassOrExtensionOrMember != null &&
_isInSameLibrary(currentLibrary, currentClassOrExtensionOrMember!)) {
result = currentClassOrExtensionOrMember;
}
return result;
}
TreeNode? get remoteContext {
TreeNode? result = getLastSeenTreeNode(withLocation: true);
if (result != null && _isInSameLibrary(currentLibrary, result)) {
result = null;
}
return result;
}
Uri checkLocation(TreeNode node, String? name, Uri fileUri) {
if (name == null || name.contains("#")) {
// TODO(ahe): Investigate if these checks can be enabled:
// if (node.fileUri != null && node is! Library) {
// problem(node, "A synthetic node shouldn't have a fileUri",
// context: node);
// }
// if (node.fileOffset != -1) {
// problem(node, "A synthetic node shouldn't have a fileOffset",
// context: node);
// }
return fileUri;
} else {
// ignore: unnecessary_null_comparison
if (fileUri == null) {
problem(node, "'$name' has no fileUri", context: node);
return fileUri;
}
if (node.fileOffset == TreeNode.noOffset &&
!target.verification.allowNoFileOffset(stage, node)) {
problem(node, "'$name' has no fileOffset", context: node);
}
return fileUri;
}
}
void checkSuperInvocation(TreeNode node) {
Member? containingMember = getContainingMember(node);
if (containingMember == null) {
problem(node, 'Super call outside of any member');
} else {
if (!containingMember.containsSuperCalls) {
problem(
node, 'Super call in a member lacking TransformerFlag.superCalls');
}
}
}
Member? getContainingMember(TreeNode? node) {
while (node != null) {
if (node is Member) return node;
node = node.parent;
}
return null;
}
@override
void visitAsExpression(AsExpression node) {
enterTreeNode(node);
super.visitAsExpression(node);
if (node.fileOffset == TreeNode.noOffset &&
!target.verification.allowNoFileOffset(stage, node)) {
TreeNode? parent = node.parent;
while (parent != null) {
if (parent.fileOffset != TreeNode.noOffset) break;
parent = parent.parent;
}
problem(parent, "No offset for $node", context: node);
}
exitTreeNode(node);
}
@override
void visitExpressionStatement(ExpressionStatement node) {
// Bypass verification of the [StaticGet] in [RedirectingFactoryBody] as
// this is a static get without a getter.
if (node is! RedirectingFactoryBody) {
enterTreeNode(node);
super.visitExpressionStatement(node);
exitTreeNode(node);
}
}
bool isNullType(DartType node) => node is NullType;
bool isObjectClass(Class c) {
return c.name == "Object" &&
c.enclosingLibrary.importUri.isScheme("dart") &&
c.enclosingLibrary.importUri.path == "core";
}
bool isTopType(DartType node) {
return node is DynamicType ||
node is VoidType ||
node is InterfaceType &&
isObjectClass(node.classNode) &&
(node.nullability == Nullability.nullable ||
node.nullability == Nullability.legacy) ||
node is FutureOrType && isTopType(node.typeArgument);
}
bool isFutureOrNull(DartType node) {
return isNullType(node) ||
node is FutureOrType && isFutureOrNull(node.typeArgument);
}
@override
void defaultDartType(DartType node) {
final TreeNode? localContext = this.localContext;
final TreeNode? remoteContext = this.remoteContext;
if (!KnownTypes.isKnown(node)) {
problem(localContext, "Unexpected appearance of the unknown type.",
origin: remoteContext);
}
// TODO(johnniwinther): This check wasn't called from InterfaceType and
// is currently very broken. Disabling for now.
/*bool isTypeCast = localContext != null &&
localContext is AsExpression &&
localContext.isTypeError;
// Don't check cases like foo(x as{TypeError} T). In cases where foo comes
// from a library with a different opt-in status than the current library,
// the check may not be necessary. For now, just skip all type-error casts.
// TODO(cstefantsova): Implement a more precise analysis.
bool isFromAnotherLibrary = remoteContext != null || isTypeCast;
// Checking for non-legacy types in opt-out libraries.
{
bool neverLegacy = isNullType(node) ||
isFutureOrNull(node) ||
isTopType(node) ||
node is InvalidType ||
node is NeverType ||
node is BottomType;
// TODO(cstefantsova): Consider checking types coming from other
// libraries.
bool expectedLegacy = !isFromAnotherLibrary &&
!currentLibrary.isNonNullableByDefault &&
!neverLegacy;
if (expectedLegacy && node.nullability != Nullability.legacy) {
problem(localContext,
"Found a non-legacy type '${node}' in an opted-out library.",
origin: remoteContext);
}
}
// Checking for legacy types in opt-in libraries.
{
Nullability nodeNullability =
node is InvalidType ? Nullability.undetermined : node.nullability;
// TODO(cstefantsova): Consider checking types coming from other
// libraries.
if (!isFromAnotherLibrary &&
currentLibrary.isNonNullableByDefault &&
nodeNullability == Nullability.legacy) {
problem(localContext,
"Found a legacy type '${node}' in an opted-in library.",
origin: remoteContext);
}
}*/
super.defaultDartType(node);
}
@override
void visitSuperMethodInvocation(SuperMethodInvocation node) {
enterTreeNode(node);
checkSuperInvocation(node);
super.visitSuperMethodInvocation(node);
exitTreeNode(node);
}
@override
void visitSuperPropertyGet(SuperPropertyGet node) {
enterTreeNode(node);
checkSuperInvocation(node);
super.visitSuperPropertyGet(node);
exitTreeNode(node);
}
@override
void visitSuperPropertySet(SuperPropertySet node) {
enterTreeNode(node);
checkSuperInvocation(node);
super.visitSuperPropertySet(node);
exitTreeNode(node);
}
void _checkConstructorTearOff(Node node, Member tearOffTarget) {
if (tearOffTarget.enclosingLibrary.importUri.isScheme('dart')) {
// Platform libraries are not compilation with test flags and might
// contain tear-offs not expected when testing lowerings.
return;
}
if (currentMember != null && isRedirectingFactoryField(currentMember!)) {
// The encoding of the redirecting factory field uses
// [ConstructorTearOffConstant] nodes also when lowerings are enabled.
return;
}
if (tearOffTarget is Constructor &&
target.isConstructorTearOffLoweringEnabled) {
problem(
node is TreeNode ? node : getLastSeenTreeNode(),
'${node.runtimeType} nodes for generative constructors should be '
'lowered for target "${target.name}".');
}
if (tearOffTarget is Procedure &&
tearOffTarget.isFactory &&
target.isFactoryTearOffLoweringEnabled) {
problem(
node is TreeNode ? node : getLastSeenTreeNode(),
'${node.runtimeType} nodes for factory constructors should be '
'lowered for target "${target.name}".');
}
}
@override
void visitConstructorTearOff(ConstructorTearOff node) {
_checkConstructorTearOff(node, node.target);
super.visitConstructorTearOff(node);
}
@override
void visitConstructorTearOffConstant(ConstructorTearOffConstant node) {
_checkConstructorTearOff(node, node.target);
super.visitConstructorTearOffConstant(node);
}
void _checkTypedefTearOff(Node node) {
if (target.isTypedefTearOffLoweringEnabled) {
problem(
node is TreeNode ? node : getLastSeenTreeNode(),
'${node.runtimeType} nodes for typedefs should be '
'lowered for target "${target.name}".');
}
}
void _checkRedirectingFactoryTearOff(Node node) {
if (target.isRedirectingFactoryTearOffLoweringEnabled) {
problem(
node is TreeNode ? node : getLastSeenTreeNode(),
'ConstructorTearOff nodes for redirecting factories should be '
'lowered for target "${target.name}".');
}
}
@override
void visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) {
_checkRedirectingFactoryTearOff(node);
super.visitRedirectingFactoryTearOff(node);
}
@override
void visitRedirectingFactoryTearOffConstant(
RedirectingFactoryTearOffConstant node) {
_checkRedirectingFactoryTearOff(node);
super.visitRedirectingFactoryTearOffConstant(node);
}
}
void verifyGetStaticType(TypeEnvironment env, Component component) {
@ -1143,3 +1671,56 @@ void checkInitializers(Constructor constructor) {
bool _isCompileTimeErrorEncoding(TreeNode? node) {
return node is Let && node.variable.initializer is InvalidExpression;
}
class KnownTypes implements DartTypeVisitor<bool> {
static bool isKnown(DartType type) {
return type.accept(const KnownTypes());
}
const KnownTypes();
@override
bool defaultDartType(DartType node) => false;
@override
bool visitDynamicType(DynamicType node) => true;
@override
bool visitExtensionType(ExtensionType node) => true;
@override
bool visitFunctionType(FunctionType node) => true;
@override
bool visitFutureOrType(FutureOrType node) => true;
@override
bool visitInlineType(InlineType node) => true;
@override
bool visitInterfaceType(InterfaceType node) => true;
@override
bool visitIntersectionType(IntersectionType node) => true;
@override
bool visitInvalidType(InvalidType node) => true;
@override
bool visitNeverType(NeverType node) => true;
@override
bool visitNullType(NullType node) => true;
@override
bool visitRecordType(RecordType node) => true;
@override
bool visitTypeParameterType(TypeParameterType node) => true;
@override
bool visitTypedefType(TypedefType node) => true;
@override
bool visitVoidType(VoidType node) => true;
}

View file

@ -6,7 +6,7 @@ name: kernel
publish_to: none
environment:
sdk: '>=2.14.0 <3.0.0'
sdk: '>=2.19.0 <3.0.0'
# Use 'any' constraints here; we get our versions from the DEPS file.
dev_dependencies:

View file

@ -5,6 +5,7 @@
import 'dart:io';
import 'package:kernel/kernel.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/verifier.dart';
final String usage = '''
@ -18,18 +19,22 @@ void main(List<String> args) {
print(usage);
exit(1);
}
var target = new NoneTarget(new TargetFlags());
var component = loadComponentFromBinary(args[0]);
var watch = new Stopwatch()..start();
verifyComponent(component);
verifyComponent(
target, VerificationStage.afterModularTransformations, component);
print('Cold: ${watch.elapsedMilliseconds} ms');
const int warmUpTrials = 20;
for (int i = 0; i < warmUpTrials; ++i) {
verifyComponent(component);
verifyComponent(
target, VerificationStage.afterModularTransformations, component);
}
watch.reset();
const int numberOfTrials = 100;
for (int i = 0; i < numberOfTrials; ++i) {
verifyComponent(component);
verifyComponent(
target, VerificationStage.afterModularTransformations, component);
}
double millisecondsPerRun = watch.elapsedMilliseconds / numberOfTrials;
print('Hot: $millisecondsPerRun ms');

View file

@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:kernel/ast.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/text/ast_to_text.dart';
import 'package:kernel/verifier.dart';
import 'package:test/test.dart';
const int dummyFileOffset = 4312;
/// Checks that the verifier correctly find errors in invalid components.
///
/// The frontend should never generate invalid components, so we have to test
@ -81,7 +84,8 @@ void main() {
name: 'Test',
supertype: test.objectClass.asRawSupertype,
fields: [field, field],
fileUri: dummyUri));
fileUri: dummyUri)
..fileOffset = dummyFileOffset);
return field;
},
(Node? node) => "Member '$node' has been declared more than once.",
@ -104,7 +108,8 @@ void main() {
name: 'Test',
supertype: test.objectClass.asRawSupertype,
typeParameters: [parameter, parameter],
fileUri: dummyUri));
fileUri: dummyUri)
..fileOffset = dummyFileOffset);
return parameter;
},
(Node? node) => "Type parameter '$node' redeclared.",
@ -118,7 +123,8 @@ void main() {
ProcedureKind.Method,
new FunctionNode(new ReturnStatement(new NullLiteral()),
typeParameters: [parameter, parameter]),
fileUri: dummyUri));
fileUri: dummyUri)
..fileOffset = dummyFileOffset);
return parameter;
},
@ -158,7 +164,8 @@ void main() {
new FunctionNode(new ReturnStatement(new TypeLiteral(
new TypeParameterType(node, Nullability.legacy)))),
isStatic: true,
fileUri: dummyUri));
fileUri: dummyUri)
..fileOffset = dummyFileOffset);
return [node, test.enclosingClass];
},
@ -174,7 +181,8 @@ void main() {
initializer:
new TypeLiteral(new TypeParameterType(node, Nullability.legacy)),
isStatic: true,
fileUri: dummyUri));
fileUri: dummyUri)
..fileOffset = dummyFileOffset);
return [node, test.enclosingClass];
},
(Node? node, Node? parent) =>
@ -192,15 +200,18 @@ void main() {
supertype: test.objectClass.asRawSupertype,
procedures: [
new Procedure(new Name('generic'), ProcedureKind.Method, parent,
fileUri: dummyUri),
fileUri: dummyUri)
..fileOffset = dummyFileOffset,
new Procedure(
new Name('use'),
ProcedureKind.Method,
new FunctionNode(new ReturnStatement(new TypeLiteral(
new TypeParameterType(parameter, Nullability.legacy)))),
fileUri: dummyUri)
..fileOffset = dummyFileOffset
],
fileUri: dummyUri));
fileUri: dummyUri)
..fileOffset = dummyFileOffset);
return [parameter, parent];
},
@ -233,7 +244,8 @@ void main() {
negative1Test(
'Dangling interface type',
(TestHarness test) {
Class orphan = new Class(name: 'Class', fileUri: dummyUri);
Class orphan = new Class(name: 'Class', fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.addNode(
new TypeLiteral(new InterfaceType(orphan, Nullability.legacy)));
return orphan;
@ -243,7 +255,8 @@ void main() {
negative1Test(
'Dangling field get',
(TestHarness test) {
Field orphan = new Field.mutable(new Name('foo'), fileUri: dummyUri);
Field orphan = new Field.mutable(new Name('foo'), fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.addNode(new InstanceGet(
InstanceAccessKind.Instance, new NullLiteral(), orphan.name,
interfaceTarget: orphan, resultType: orphan.getterType));
@ -268,7 +281,8 @@ void main() {
(TestHarness test) {
var procedure = new Procedure(
new Name('bar'), ProcedureKind.Method, dummyFunctionNode,
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
procedure.function = new FunctionNode(new EmptyStatement());
test.addNode(procedure);
},
@ -282,7 +296,8 @@ void main() {
new FunctionNode(new EmptyStatement(),
positionalParameters: [new VariableDeclaration('p')]),
isStatic: true,
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingClass.addProcedure(method);
test.addNode(
StaticInvocation(method, new Arguments([new NullLiteral()])));
@ -293,7 +308,8 @@ void main() {
(TestHarness test) {
var method = new Procedure(new Name('bar'), ProcedureKind.Method,
new FunctionNode(new EmptyStatement()),
isStatic: true, fileUri: dummyUri);
isStatic: true, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingClass.addProcedure(method);
test.addNode(
StaticInvocation(method, new Arguments([new NullLiteral()])));
@ -311,7 +327,8 @@ void main() {
new FunctionNode(new EmptyStatement(),
positionalParameters: [new VariableDeclaration('p')]),
isStatic: true,
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingClass.addProcedure(method);
test.addNode(StaticInvocation(method, new Arguments.empty()));
return method;
@ -323,7 +340,8 @@ void main() {
(TestHarness test) {
var method = new Procedure(new Name('bar'), ProcedureKind.Method,
new FunctionNode(new EmptyStatement()),
isStatic: true, fileUri: dummyUri);
isStatic: true, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingClass.addProcedure(method);
test.addNode(StaticInvocation(
method,
@ -343,7 +361,8 @@ void main() {
new FunctionNode(new EmptyStatement(),
typeParameters: [test.makeTypeParameter()]),
isStatic: true,
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingClass.addProcedure(method);
test.addNode(StaticInvocation(method, new Arguments.empty()));
return method;
@ -355,7 +374,8 @@ void main() {
'ConstructorInvocation with missing type argument',
(TestHarness test) {
var constructor = new Constructor(new FunctionNode(new EmptyStatement()),
name: new Name('foo'), fileUri: dummyUri);
name: new Name('foo'), fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingClass.addConstructor(constructor);
test.addNode(ConstructorInvocation(constructor, new Arguments.empty()));
return constructor;
@ -371,7 +391,8 @@ void main() {
'Foo',
new FunctionType(
[test.otherLegacyRawType], const VoidType(), Nullability.legacy),
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.addNode(typedef_);
},
);
@ -382,15 +403,18 @@ void main() {
'Foo',
new InterfaceType(
test.otherClass, Nullability.legacy, [const DynamicType()]),
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.addNode(typedef_);
},
);
positiveTest(
'Valid typedefs Foo = Bar, Bar = C',
(TestHarness test) {
var foo = new Typedef('Foo', null, fileUri: dummyUri);
var bar = new Typedef('Bar', null, fileUri: dummyUri);
var foo = new Typedef('Foo', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
var bar = new Typedef('Bar', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
foo.type = new TypedefType(bar, Nullability.legacy);
bar.type = test.otherLegacyRawType;
test.enclosingLibrary.addTypedef(foo);
@ -400,8 +424,10 @@ void main() {
positiveTest(
'Valid typedefs Foo = C<Bar>, Bar = C',
(TestHarness test) {
var foo = new Typedef('Foo', null, fileUri: dummyUri);
var bar = new Typedef('Bar', null, fileUri: dummyUri);
var foo = new Typedef('Foo', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
var bar = new Typedef('Bar', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
foo.type = new InterfaceType(test.otherClass, Nullability.legacy,
[new TypedefType(bar, Nullability.legacy)]);
bar.type = test.otherLegacyRawType;
@ -416,11 +442,13 @@ void main() {
'Foo',
new FunctionType(
[test.otherLegacyRawType], const VoidType(), Nullability.legacy),
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
var field = new Field.mutable(new Name('field'),
type: new TypedefType(typedef_, Nullability.legacy),
isStatic: true,
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingLibrary.addTypedef(typedef_);
test.enclosingLibrary.addField(field);
},
@ -428,7 +456,8 @@ void main() {
negative1Test(
'Invalid typedef Foo = Foo',
(TestHarness test) {
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri);
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
typedef_.type = new TypedefType(typedef_, Nullability.legacy);
test.addNode(typedef_);
return typedef_;
@ -438,7 +467,8 @@ void main() {
negative1Test(
'Invalid typedef Foo = `(Foo) => void`',
(TestHarness test) {
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri);
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
typedef_.type = new FunctionType(
[new TypedefType(typedef_, Nullability.legacy)],
const VoidType(),
@ -451,7 +481,8 @@ void main() {
negative1Test(
'Invalid typedef Foo = `() => Foo`',
(TestHarness test) {
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri);
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
typedef_.type = new FunctionType([],
new TypedefType(typedef_, Nullability.legacy), Nullability.legacy);
test.addNode(typedef_);
@ -462,7 +493,8 @@ void main() {
negative1Test(
'Invalid typedef Foo = C<Foo>',
(TestHarness test) {
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri);
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
typedef_.type = new InterfaceType(test.otherClass, Nullability.legacy,
[new TypedefType(typedef_, Nullability.legacy)]);
test.addNode(typedef_);
@ -473,8 +505,10 @@ void main() {
negative1Test(
'Invalid typedefs Foo = Bar, Bar = Foo',
(TestHarness test) {
var foo = new Typedef('Foo', null, fileUri: dummyUri);
var bar = new Typedef('Bar', null, fileUri: dummyUri);
var foo = new Typedef('Foo', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
var bar = new Typedef('Bar', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
foo.type = new TypedefType(bar, Nullability.legacy);
bar.type = new TypedefType(foo, Nullability.legacy);
test.enclosingLibrary.addTypedef(foo);
@ -486,8 +520,10 @@ void main() {
negative1Test(
'Invalid typedefs Foo = Bar, Bar = C<Foo>',
(TestHarness test) {
var foo = new Typedef('Foo', null, fileUri: dummyUri);
var bar = new Typedef('Bar', null, fileUri: dummyUri);
var foo = new Typedef('Foo', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
var bar = new Typedef('Bar', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
foo.type = new TypedefType(bar, Nullability.legacy);
bar.type = new InterfaceType(test.otherClass, Nullability.legacy,
[new TypedefType(foo, Nullability.legacy)]);
@ -500,8 +536,10 @@ void main() {
negative1Test(
'Invalid typedefs Foo = C<Bar>, Bar = C<Foo>',
(TestHarness test) {
var foo = new Typedef('Foo', null, fileUri: dummyUri);
var bar = new Typedef('Bar', null, fileUri: dummyUri);
var foo = new Typedef('Foo', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
var bar = new Typedef('Bar', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
foo.type = new InterfaceType(test.otherClass, Nullability.legacy,
[new TypedefType(bar, Nullability.legacy)]);
bar.type = new InterfaceType(test.otherClass, Nullability.legacy,
@ -515,12 +553,14 @@ void main() {
positiveTest(
'Valid long typedefs C20 = C19 = ... = C1 = C0 = dynamic',
(TestHarness test) {
var typedef_ = new Typedef('C0', const DynamicType(), fileUri: dummyUri);
var typedef_ = new Typedef('C0', const DynamicType(), fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingLibrary.addTypedef(typedef_);
for (int i = 1; i < 20; ++i) {
typedef_ = new Typedef(
'C$i', new TypedefType(typedef_, Nullability.legacy),
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingLibrary.addTypedef(typedef_);
}
},
@ -528,14 +568,16 @@ void main() {
negative1Test(
'Invalid long typedefs C20 = C19 = ... = C1 = C0 = C20',
(TestHarness test) {
Typedef firstTypedef = new Typedef('C0', null, fileUri: dummyUri);
Typedef firstTypedef = new Typedef('C0', null, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
Typedef typedef_ = firstTypedef;
test.enclosingLibrary.addTypedef(typedef_);
var first = typedef_;
for (int i = 1; i < 20; ++i) {
typedef_ = new Typedef(
'C$i', new TypedefType(typedef_, Nullability.legacy),
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingLibrary.addTypedef(typedef_);
}
first.type = new TypedefType(typedef_, Nullability.legacy);
@ -552,7 +594,8 @@ void main() {
new InterfaceType(test.otherClass, Nullability.legacy,
[new TypeParameterType(param, Nullability.legacy)]),
typeParameters: [param],
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.addNode(foo);
},
);
@ -567,7 +610,8 @@ void main() {
new InterfaceType(test.otherClass, Nullability.legacy,
[new TypeParameterType(param, Nullability.legacy)]),
typeParameters: [param],
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.addNode(foo);
},
);
@ -576,7 +620,8 @@ void main() {
(TestHarness test) {
var fooParam = test.makeTypeParameter('T');
var foo = new Typedef('Foo', const DynamicType(),
typeParameters: [fooParam], fileUri: dummyUri);
typeParameters: [fooParam], fileUri: dummyUri)
..fileOffset = dummyFileOffset;
var barParam = new TypeParameter('T', null);
barParam.bound = new TypedefType(foo, Nullability.legacy,
[new TypeParameterType(barParam, Nullability.legacy)]);
@ -585,7 +630,8 @@ void main() {
new InterfaceType(test.otherClass, Nullability.legacy,
[new TypeParameterType(barParam, Nullability.legacy)]),
typeParameters: [barParam],
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addTypedef(bar);
},
@ -595,7 +641,8 @@ void main() {
(TestHarness test) {
var fooParam = test.makeTypeParameter('T');
var foo = new Typedef('Foo', const DynamicType(),
typeParameters: [fooParam], fileUri: dummyUri);
typeParameters: [fooParam], fileUri: dummyUri)
..fileOffset = dummyFileOffset;
var barParam = new TypeParameter('T', null);
barParam.bound = new TypedefType(foo, Nullability.legacy,
[new TypeParameterType(barParam, Nullability.legacy)]);
@ -622,7 +669,8 @@ void main() {
new InterfaceType(test.otherClass, Nullability.legacy,
[new TypeParameterType(param, Nullability.legacy)]),
typeParameters: [param],
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
param.bound =
new TypedefType(foo, Nullability.legacy, [const DynamicType()]);
test.addNode(foo);
@ -635,10 +683,12 @@ void main() {
(TestHarness test) {
var param = test.makeTypeParameter('T');
var foo = new Typedef('Foo', test.otherLegacyRawType,
typeParameters: [param], fileUri: dummyUri);
typeParameters: [param], fileUri: dummyUri)
..fileOffset = dummyFileOffset;
var typedefType = new TypedefType(foo, Nullability.legacy, []);
var field = new Field.mutable(new Name('field'),
type: typedefType, isStatic: true, fileUri: dummyUri);
type: typedefType, isStatic: true, fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addField(field);
return typedefType;
@ -651,11 +701,13 @@ void main() {
'Dangling typedef reference',
(TestHarness test) {
var foo = new Typedef('Foo', test.otherLegacyRawType,
typeParameters: [], fileUri: dummyUri);
typeParameters: [], fileUri: dummyUri)
..fileOffset = dummyFileOffset;
var field = new Field.mutable(new Name('field'),
type: new TypedefType(foo, Nullability.legacy, []),
isStatic: true,
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingLibrary.addField(field);
return foo;
},
@ -664,17 +716,63 @@ void main() {
negative1Test(
'Non-static top-level field',
(TestHarness test) {
var field = new Field.mutable(new Name('field'), fileUri: dummyUri);
var field = new Field.mutable(new Name('field'), fileUri: dummyUri)
..fileOffset = dummyFileOffset;
test.enclosingLibrary.addField(field);
return null;
},
(Node? node) => "The top-level field 'field' should be static",
);
positiveTest('No library file offset', (TestHarness test) {
var library = new Library(dummyUri, fileUri: dummyUri)
..parent = test.component;
test.component.libraries.add(library);
return null;
});
negative1Test(
'Class file offset',
(TestHarness test) {
var cls = new Class(name: 'Class', fileUri: dummyUri);
test.enclosingLibrary.addClass(cls);
return null;
},
(Node? node) => "'Class' has no fileOffset",
);
negative1Test(
'Extension file offset',
(TestHarness test) {
var extension = new Extension(name: 'Extension', fileUri: dummyUri);
test.enclosingLibrary.addExtension(extension);
return null;
},
(Node? node) => "'Extension' has no fileOffset",
);
negative1Test(
'Procedure file offset',
(TestHarness test) {
var method = new Procedure(
new Name('method'), ProcedureKind.Method, new FunctionNode(null),
fileUri: dummyUri);
test.enclosingClass.addProcedure(method);
return null;
},
(Node? node) => "'method' has no fileOffset",
);
negative1Test(
'Field file offset',
(TestHarness test) {
var field = new Field.mutable(new Name('field'), fileUri: dummyUri);
test.enclosingClass.addField(field);
return null;
},
(Node? node) => "'field' has no fileOffset",
);
}
void checkHasError(Component component, Matcher matcher) {
void checkHasError(Target target, Component component, Matcher matcher) {
try {
verifyComponent(component);
verifyComponent(
target, VerificationStage.afterModularTransformations, component);
} on VerificationError catch (e) {
expect(e.details, matcher);
return;
@ -683,6 +781,7 @@ void checkHasError(Component component, Matcher matcher) {
}
class TestHarness {
late Target target;
late Component component;
late Class objectClass;
late Library stubLibrary;
@ -766,12 +865,14 @@ class TestHarness {
}
void setupComponent() {
target = new NoneTarget(new TargetFlags());
component = new Component();
Uri dartCoreUri = Uri.parse('dart:core');
stubLibrary = new Library(dartCoreUri, fileUri: dartCoreUri);
component.libraries.add(stubLibrary..parent = component);
stubLibrary.name = 'dart.core';
objectClass = new Class(name: 'Object', fileUri: dartCoreUri);
objectClass = new Class(name: 'Object', fileUri: dartCoreUri)
..fileOffset = dummyFileOffset;
objectLegacyRawType =
new InterfaceType(objectClass, Nullability.legacy, const <DartType>[]);
stubLibrary.addClass(objectClass);
@ -784,19 +885,22 @@ class TestHarness {
name: 'TestClass',
typeParameters: [classTypeParameter],
supertype: objectClass.asRawSupertype,
fileUri: testUri);
fileUri: testUri)
..fileOffset = dummyFileOffset;
enclosingLegacyRawType = new InterfaceType(enclosingClass,
Nullability.legacy, const <DartType>[const DynamicType()]);
enclosingLibrary.addClass(enclosingClass);
enclosingMember = new Procedure(new Name('test'), ProcedureKind.Method,
new FunctionNode(new EmptyStatement()),
fileUri: dummyUri);
fileUri: dummyUri)
..fileOffset = dummyFileOffset;
enclosingClass.addProcedure(enclosingMember);
otherClass = new Class(
name: 'OtherClass',
typeParameters: [makeTypeParameter('OtherT')],
supertype: objectClass.asRawSupertype,
fileUri: testUri);
fileUri: testUri)
..fileOffset = dummyFileOffset;
otherLegacyRawType = new InterfaceType(
otherClass, Nullability.legacy, const <DartType>[const DynamicType()]);
enclosingLibrary.addClass(otherClass);
@ -814,7 +918,7 @@ void negative1Test(String name, Node? Function(TestHarness test) nodeProvider,
if (matcherResult is String) {
matcherResult = equals(matcherResult);
}
checkHasError(testHarness.component, matcherResult);
checkHasError(testHarness.target, testHarness.component, matcherResult);
},
);
}
@ -833,7 +937,7 @@ void negative2Test(
if (matcherResult is String) {
matcherResult = equals(matcherResult);
}
checkHasError(testHarness.component, matcherResult);
checkHasError(testHarness.target, testHarness.component, matcherResult);
},
);
}
@ -848,7 +952,7 @@ void simpleNegativeTest(String name, dynamic matcher,
if (matcher is String) {
matcher = equals(matcher);
}
checkHasError(testHarness.component, matcher);
checkHasError(testHarness.target, testHarness.component, matcher);
},
);
}
@ -859,7 +963,8 @@ void positiveTest(String name, void makeTestCase(TestHarness test)) {
() {
var test = new TestHarness();
makeTestCase(test);
verifyComponent(test.component);
verifyComponent(test.target,
VerificationStage.afterModularTransformations, test.component);
},
);
}

View file

@ -505,13 +505,6 @@ class FfiNativeTransformer extends FfiTransformer {
final parent = node.parent;
var fileUri = currentLibrary.fileUri;
if (parent is Class) {
fileUri = parent.fileUri;
} else if (parent is Library) {
fileUri = parent.fileUri;
}
// static final _myMethod$FfiNative$Ptr = ..
final resolvedField = _createResolvedFfiNativeField(
'${node.name.text}\$${node.kind.name}',
@ -521,7 +514,7 @@ class FfiNativeTransformer extends FfiTransformer {
wrappedDartFunctionType,
ffiFunctionType,
node.fileOffset,
fileUri,
node.fileUri,
);
// Add field to the parent the FfiNative function belongs to.

View file

@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:kernel/ast.dart';
import 'package:kernel/transformations/flags.dart';
/// Transformer/visitor for toString
transformComponent(Component component, List<String> packageUris) {
@ -84,6 +85,7 @@ class ToStringVisitor extends RecursiveVisitor {
return findSuperMethod(cls.superclass!);
}
node.transformerFlags |= TransformerFlag.superCalls;
node.function.body!.replaceWith(
ReturnStatement(
SuperMethodInvocation(

View file

@ -22,8 +22,6 @@ import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
import 'package:kernel/target/targets.dart';
import 'package:test/test.dart';
import 'package:vm/target/vm.dart' show VmTarget;
/// Environment define to update expectation files on failures.
const kUpdateExpectations = 'updateExpectations';
@ -31,14 +29,13 @@ const kUpdateExpectations = 'updateExpectations';
const kDumpActualResult = 'dump.actual.result';
Future<Component> compileTestCaseToKernelProgram(Uri sourceUri,
{Target? target,
{required Target target,
List<String>? experimentalFlags,
Map<String, String>? environmentDefines,
Uri? packagesFileUri,
List<Uri>? linkedDependencies}) async {
Directory? tempDirectory;
try {
target ??= new VmTarget(new TargetFlags());
final platformFileName = (target is WasmTarget)
? 'dart2wasm_platform.dill'
: 'vm_platform_strong.dill';

View file

@ -52,7 +52,8 @@ main() async {
final component = loadComponentFromBytes(bytes);
// Verify before running global transformations.
verifyComponent(component, isOutline: false, afterConst: true);
verifyComponent(
vmTarget, VerificationStage.afterModularTransformations, component);
const useGlobalTypeFlowAnalysis = true;
const enableAsserts = false;
@ -66,7 +67,8 @@ main() async {
ErrorDetector());
// Verify after running global transformations.
verifyComponent(component, isOutline: false, afterConst: true);
verifyComponent(
vmTarget, VerificationStage.afterGlobalTransformations, component);
// Verify that we can reserialize the component to ensure that all
// references are contained within the component.

View file

@ -42,7 +42,8 @@ runTestCaseJit(Uri source) async {
TestDiagnosticReporter(),
/*referenceFromIndex=*/ null);
verifyComponent(component);
verifyComponent(
target, VerificationStage.afterModularTransformations, component);
final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);
@ -69,7 +70,8 @@ runTestCaseAot(Uri source) async {
treeShakeWriteOnlyFields: true,
);
verifyComponent(component);
verifyComponent(
target, VerificationStage.afterGlobalTransformations, component);
final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);

View file

@ -6,8 +6,10 @@ import 'dart:io';
import 'package:kernel/ast.dart';
import 'package:kernel/kernel.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/verifier.dart';
import 'package:test/test.dart';
import 'package:vm/target/vm.dart';
import 'package:vm/transformations/to_string_transformer.dart'
show transformComponent;
@ -16,16 +18,19 @@ import '../common_test_utils.dart';
final Uri pkgVmUri = Platform.script.resolve('../..');
runTestCase(List<String> packageUris, String expectationName) async {
final target = new VmTarget(new TargetFlags());
final testCasesUri =
pkgVmUri.resolve('testcases/transformations/to_string_transformer/');
final packagesFileUri =
testCasesUri.resolve('.dart_tool/package_config.json');
Component component = await compileTestCaseToKernelProgram(
Uri.parse('package:to_string_transformer_test/main.dart'),
target: target,
packagesFileUri: packagesFileUri);
transformComponent(component, packageUris);
verifyComponent(component);
verifyComponent(
target, VerificationStage.afterModularTransformations, component);
final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);

View file

@ -116,7 +116,7 @@ class TestOptions {
runTestCase(Uri source, List<String>? experimentalFlags) async {
final Target target = new VmTarget(new TargetFlags());
final Component component = await compileTestCaseToKernelProgram(source,
experimentalFlags: experimentalFlags);
target: target, experimentalFlags: experimentalFlags);
final Library library = component.mainMethod!.enclosingLibrary;
final CoreTypes coreTypes = new CoreTypes(component);

View file

@ -33,7 +33,8 @@ runTestCase(Uri source) async {
target, component, /* targetOS = */ null, NnbdMode.Strong);
component =
transformComponent(component, /* enableAsserts = */ false, evaluator);
verifyComponent(component);
verifyComponent(
target, VerificationStage.afterGlobalTransformations, component);
final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);

View file

@ -34,7 +34,8 @@ runTestCase(Uri source, TargetOS os) async {
VMConstantEvaluator.create(target, component, os, NnbdMode.Strong);
final enableAsserts = false;
component = transformComponent(component, enableAsserts, evaluator);
verifyComponent(component);
verifyComponent(
target, VerificationStage.afterGlobalTransformations, component);
final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);
final postfix = '.${os.name}';