diff --git a/pkg/dart2wasm/lib/compile.dart b/pkg/dart2wasm/lib/compile.dart index d68417bc263..127ce150eb5 100644 --- a/pkg/dart2wasm/lib/compile.dart +++ b/pkg/dart2wasm/lib/compile.dart @@ -103,8 +103,8 @@ Future compileToModule(compiler.CompilerOptions options, useRapidTypeAnalysis: false); assert(() { - verifyComponent(component, - afterConst: true, constantsAreAlwaysInlined: true); + verifyComponent( + target, VerificationStage.afterGlobalTransformations, component); return true; }()); diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart index 54194cfdbe0..a8ea4efe334 100644 --- a/pkg/dart2wasm/lib/target.dart +++ b/pkg/dart2wasm/lib/target.dart @@ -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 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; + } +} diff --git a/pkg/front_end/lib/src/api_unstable/dart2js.dart b/pkg/front_end/lib/src/api_unstable/dart2js.dart index 84eb3cb11ea..03e9928161c 100644 --- a/pkg/front_end/lib/src/api_unstable/dart2js.dart +++ b/pkg/front_end/lib/src/api_unstable/dart2js.dart @@ -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; diff --git a/pkg/front_end/lib/src/api_unstable/ddc.dart b/pkg/front_end/lib/src/api_unstable/ddc.dart index f65247e9c21..7bf57f12882 100644 --- a/pkg/front_end/lib/src/api_unstable/ddc.dart +++ b/pkg/front_end/lib/src/api_unstable/ddc.dart @@ -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; diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart index bafc97a2d09..5c23131c5de 100644 --- a/pkg/front_end/lib/src/api_unstable/vm.dart +++ b/pkg/front_end/lib/src/api_unstable/vm.dart @@ -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, diff --git a/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart b/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart index bbda5b65e18..cd047c2c7ad 100644 --- a/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart +++ b/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart @@ -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 { diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart index 04dcd0c6453..323e0502aa9 100644 --- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart @@ -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; diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart index f8696a8a939..4475119fb01 100644 --- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart +++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart @@ -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!), diff --git a/pkg/front_end/lib/src/fasta/kernel/verifier.dart b/pkg/front_end/lib/src/fasta/kernel/verifier.dart index 68923de2f80..1bb15444370 100644 --- a/pkg/front_end/lib/src/fasta/kernel/verifier.dart +++ b/pkg/front_end/lib/src/fasta/kernel/verifier.dart @@ -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 verifyComponent(Component component, Target target, - {bool? isOutline, bool? afterConst, bool skipPlatform = false}) { - FastaVerifyingVisitor verifier = new FastaVerifyingVisitor(target, - isOutline: isOutline, afterConst: afterConst, skipPlatform: skipPlatform); +List 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 errors = []; - - Uri? fileUri; - final List treeNodeStack = []; - 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 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? 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, diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart index ee91ec3eaf5..9e5dc7e1852 100644 --- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart +++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart @@ -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; diff --git a/pkg/front_end/lib/src/fasta/source/source_factory_builder.dart b/pkg/front_end/lib/src/fasta/source/source_factory_builder.dart index c64a9e5fd68..d76f5c99bcb 100644 --- a/pkg/front_end/lib/src/fasta/source/source_factory_builder.dart +++ b/pkg/front_end/lib/src/fasta/source/source_factory_builder.dart @@ -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, diff --git a/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart b/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart index 53821fe77fa..33b2f4b4d7a 100644 --- a/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart +++ b/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart @@ -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); diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart index 4e2d2fc003e..1cab3a796ce 100644 --- a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart +++ b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart @@ -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'; diff --git a/pkg/front_end/lib/src/kernel_generator_impl.dart b/pkg/front_end/lib/src/kernel_generator_impl.dart index b56d5177e35..5a647886ea5 100644 --- a/pkg/front_end/lib/src/kernel_generator_impl.dart +++ b/pkg/front_end/lib/src/kernel_generator_impl.dart @@ -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 _buildInternal( List? 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); } } diff --git a/pkg/front_end/lib/src/testing/analysis_helper.dart b/pkg/front_end/lib/src/testing/analysis_helper.dart index 77a3b77dcc4..c325fab2abe 100644 --- a/pkg/front_end/lib/src/testing/analysis_helper.dart +++ b/pkg/front_end/lib/src/testing/analysis_helper.dart @@ -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( diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart index cce99db70fc..c226d969ffc 100644 --- a/pkg/front_end/test/fasta/testing/suite.dart +++ b/pkg/front_end/test/fasta/testing/suite.dart @@ -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 = [ 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 { } class Verify extends Step { - 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 { result.options, (compilerContext) async { compilerContext.uriToSource.addAll(component.uriToSource); List 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); diff --git a/pkg/front_end/test/issue_34856_test.dart b/pkg/front_end/test/issue_34856_test.dart index a0b5fc11982..68c21c26f62 100644 --- a/pkg/front_end/test/issue_34856_test.dart +++ b/pkg/front_end/test/issue_34856_test.dart @@ -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 files = const { "repro.dart": """ @@ -96,8 +97,9 @@ Future test() async { List errors = await CompilerContext.runWithOptions( new ProcessedOptions(options: options, inputs: inputs), - (_) => new Future>.value( - verifyComponent(component, options.target!, skipPlatform: true))); + (_) => new Future>.value(verifyComponent(options.target!, + VerificationStage.afterModularTransformations, component, + skipPlatform: true))); serializeComponent(component); diff --git a/pkg/front_end/tool/_fasta/entry_points.dart b/pkg/front_end/tool/_fasta/entry_points.dart index 62b89ae0c3a..b83919c5226 100644 --- a/pkg/front_end/tool/_fasta/entry_points.dart +++ b/pkg/front_end/tool/_fasta/entry_points.dart @@ -538,6 +538,9 @@ Future 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()}"); diff --git a/pkg/frontend_server/test/frontend_server_flutter.dart b/pkg/frontend_server/test/frontend_server_flutter.dart index 43aa95f7e61..d4045371ff5 100644 --- a/pkg/frontend_server/test/frontend_server_flutter.dart +++ b/pkg/frontend_server/test/frontend_server_flutter.dart @@ -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 args) async { String? flutterDir; @@ -244,11 +246,13 @@ Future> attemptStuff( List platformData = File.fromUri(flutterPlatformDirectory.uri.resolve("platform_strong.dill")) .readAsBytesSync(); + final String targetName = 'flutter'; + final Target target = createFrontEndTarget(targetName)!; final List args = [ '--sdk-root', flutterPlatformDirectory.path, '--incremental', - '--target=flutter', + '--target=$targetName', '--packages', packageConfig.path, '--output-dill=${dillFile.path}', @@ -303,7 +307,8 @@ Future> attemptStuff( List 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."); } diff --git a/pkg/frontend_server/test/frontend_server_test.dart b/pkg/frontend_server/test/frontend_server_test.dart index 43d3d725c7d..31a398285ed 100644 --- a/pkg/frontend_server/test/frontend_server_test.dart +++ b/pkg/frontend_server/test/frontend_server_test.dart @@ -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 { expect(dillFile.existsSync(), equals(false)); // First compile app entry point A. + final String targetName = 'vm'; + final Target target = createFrontEndTarget(targetName)!; final List args = [ '--sdk-root=${sdkRoot.toFilePath()}', '--incremental', @@ -1212,7 +1216,8 @@ class BarState extends State { // 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 { expect(dillFile.existsSync(), equals(false)); // First compile app entry point A. + final String targetName = 'vm'; + final Target target = createFrontEndTarget(targetName)!; final List args = [ '--sdk-root=${sdkRoot.toFilePath()}', '--incremental', @@ -1332,7 +1339,8 @@ class BarState extends State { // 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 args = [ '--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; diff --git a/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart b/pkg/kernel/lib/src/redirecting_factory_body.dart similarity index 98% rename from pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart rename to pkg/kernel/lib/src/redirecting_factory_body.dart index fb19edd454d..009f18323cd 100644 --- a/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart +++ b/pkg/kernel/lib/src/redirecting_factory_body.dart @@ -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. diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart index 2f5e9259a5d..5ae04f0f121 100644 --- a/pkg/kernel/lib/target/targets.dart +++ b/pkg/kernel/lib/target/targets.dart @@ -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 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 diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart index 792d93aa0d5..fb480d9636a 100644 --- a/pkg/kernel/lib/verifier.dart +++ b/pkg/kernel/lib/verifier.dart @@ -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 { + final Target target; + + Uri? fileUri; + + final VerificationErrorListener listener; + + final List treeNodeStack = []; + final bool skipPlatform; + final Set classes = new Set(); final Set typedefs = new Set(); Set typeParametersInScope = new Set(); @@ -63,17 +137,8 @@ class VerifyingVisitor extends RecursiveResultVisitor { final Set seenConstants = {}; 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 { 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 { 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 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 { @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 { 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 { exitParent(oldParent); undeclareTypeParameters(node.typeParameters); currentInlineClass = null; + exitTreeNode(node); } void checkTypedef(Typedef node) { @@ -295,6 +401,7 @@ class VerifyingVisitor extends RecursiveResultVisitor { problem(node, "The typedef '$node' refers to itself", context: node); } assert(state == null); + enterTreeNode(node); typedefState[node] = TypedefState.BeingChecked; Set savedTypeParameters = typeParametersInScope; typeParametersInScope = node.typeParameters.toSet(); @@ -306,17 +413,23 @@ class VerifyingVisitor extends RecursiveResultVisitor { 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 { 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 { } }*/ 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 { }*/ 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 { 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 { 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 { @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 { node.value.accept(this); exitParent(oldParent); exitLocalScope(stackHeight); + exitTreeNode(node); } @override @@ -613,6 +764,7 @@ class VerifyingVisitor extends RecursiveResultVisitor { @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 { } } } + 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 { 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 { 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 { 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 { @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 { 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 { @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 { } 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 { @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 { @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 { @override void visitTypedefTearOffConstant(TypedefTearOffConstant node) { + _checkTypedefTearOff(node); declareTypeParameters(node.parameters); super.visitTypedefTearOffConstant(node); undeclareTypeParameters(node.parameters); @@ -1038,6 +1223,349 @@ class VerifyingVisitor extends RecursiveResultVisitor { } 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 { + 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; +} diff --git a/pkg/kernel/pubspec.yaml b/pkg/kernel/pubspec.yaml index eb65a93fda9..de38fccb4b0 100644 --- a/pkg/kernel/pubspec.yaml +++ b/pkg/kernel/pubspec.yaml @@ -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: diff --git a/pkg/kernel/test/verify_bench.dart b/pkg/kernel/test/verify_bench.dart index d8e5a61ffab..a34691808d0 100644 --- a/pkg/kernel/test/verify_bench.dart +++ b/pkg/kernel/test/verify_bench.dart @@ -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 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'); diff --git a/pkg/kernel/test/verify_test.dart b/pkg/kernel/test/verify_test.dart index aab3d68a0b4..4610b057c94 100644 --- a/pkg/kernel/test/verify_test.dart +++ b/pkg/kernel/test/verify_test.dart @@ -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 = 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', (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', (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 = 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 = 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 []); 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 [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 [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); }, ); } diff --git a/pkg/vm/lib/transformations/ffi/native.dart b/pkg/vm/lib/transformations/ffi/native.dart index 5d055508289..8a4c949665b 100644 --- a/pkg/vm/lib/transformations/ffi/native.dart +++ b/pkg/vm/lib/transformations/ffi/native.dart @@ -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. diff --git a/pkg/vm/lib/transformations/to_string_transformer.dart b/pkg/vm/lib/transformations/to_string_transformer.dart index bf16900d687..08ad159875a 100644 --- a/pkg/vm/lib/transformations/to_string_transformer.dart +++ b/pkg/vm/lib/transformations/to_string_transformer.dart @@ -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 packageUris) { @@ -84,6 +85,7 @@ class ToStringVisitor extends RecursiveVisitor { return findSuperMethod(cls.superclass!); } + node.transformerFlags |= TransformerFlag.superCalls; node.function.body!.replaceWith( ReturnStatement( SuperMethodInvocation( diff --git a/pkg/vm/test/common_test_utils.dart b/pkg/vm/test/common_test_utils.dart index 50d83c354a0..b8c59599892 100644 --- a/pkg/vm/test/common_test_utils.dart +++ b/pkg/vm/test/common_test_utils.dart @@ -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 compileTestCaseToKernelProgram(Uri sourceUri, - {Target? target, + {required Target target, List? experimentalFlags, Map? environmentDefines, Uri? packagesFileUri, List? linkedDependencies}) async { Directory? tempDirectory; try { - target ??= new VmTarget(new TargetFlags()); final platformFileName = (target is WasmTarget) ? 'dart2wasm_platform.dill' : 'vm_platform_strong.dill'; diff --git a/pkg/vm/test/modular_kernel_plus_aot_test.dart b/pkg/vm/test/modular_kernel_plus_aot_test.dart index 0b08a37dfc6..16893336d6e 100644 --- a/pkg/vm/test/modular_kernel_plus_aot_test.dart +++ b/pkg/vm/test/modular_kernel_plus_aot_test.dart @@ -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. diff --git a/pkg/vm/test/transformations/ffi_test.dart b/pkg/vm/test/transformations/ffi_test.dart index 9d5028b8a04..d6b0c3e1951 100644 --- a/pkg/vm/test/transformations/ffi_test.dart +++ b/pkg/vm/test/transformations/ffi_test.dart @@ -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); diff --git a/pkg/vm/test/transformations/to_string_transformer_test.dart b/pkg/vm/test/transformations/to_string_transformer_test.dart index 34f775f6ed3..2c56cc917fc 100644 --- a/pkg/vm/test/transformations/to_string_transformer_test.dart +++ b/pkg/vm/test/transformations/to_string_transformer_test.dart @@ -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 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); diff --git a/pkg/vm/test/transformations/type_flow/summary_collector_test.dart b/pkg/vm/test/transformations/type_flow/summary_collector_test.dart index 4817b1d2db1..e62e3b45a36 100644 --- a/pkg/vm/test/transformations/type_flow/summary_collector_test.dart +++ b/pkg/vm/test/transformations/type_flow/summary_collector_test.dart @@ -116,7 +116,7 @@ class TestOptions { runTestCase(Uri source, List? 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); diff --git a/pkg/vm/test/transformations/unreachable_code_elimination_test.dart b/pkg/vm/test/transformations/unreachable_code_elimination_test.dart index 11f0ff3beac..e6a3becd3ee 100644 --- a/pkg/vm/test/transformations/unreachable_code_elimination_test.dart +++ b/pkg/vm/test/transformations/unreachable_code_elimination_test.dart @@ -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); diff --git a/pkg/vm/test/transformations/vm_constant_evaluator_test.dart b/pkg/vm/test/transformations/vm_constant_evaluator_test.dart index 1ae70031c34..251fa1e01d9 100644 --- a/pkg/vm/test/transformations/vm_constant_evaluator_test.dart +++ b/pkg/vm/test/transformations/vm_constant_evaluator_test.dart @@ -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}';