mirror of
https://github.com/dart-lang/sdk
synced 2024-07-20 18:05:01 +00:00
[kernel] Merge front_end and kernel verifiers
This merges the front_end and kernel verifiers into one Target based kernel verifier. The RedirectingFactoryBody work-around is moved to package:kernel to support its verification. TEST=existing Change-Id: I0adf4d2c22c4009cf439b3b23fa14192253a2846 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/280161 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Jens Johansen <jensj@google.com> Reviewed-by: Joshua Litt <joshualitt@google.com>
This commit is contained in:
parent
498dcc02ee
commit
009cbfbeac
|
@ -103,8 +103,8 @@ Future<CompilerOutput?> compileToModule(compiler.CompilerOptions options,
|
|||
useRapidTypeAnalysis: false);
|
||||
|
||||
assert(() {
|
||||
verifyComponent(component,
|
||||
afterConst: true, constantsAreAlwaysInlined: true);
|
||||
verifyComponent(
|
||||
target, VerificationStage.afterGlobalTransformations, component);
|
||||
return true;
|
||||
}());
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'package:kernel/reference_from_index.dart';
|
|||
import 'package:kernel/target/changed_structure_notifier.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/type_environment.dart';
|
||||
import 'package:kernel/verifier.dart';
|
||||
import 'package:vm/transformations/mixin_full_resolution.dart'
|
||||
as transformMixins show transformLibraries;
|
||||
import 'package:vm/transformations/ffi/common.dart' as ffiHelper
|
||||
|
@ -52,6 +53,9 @@ class WasmTarget extends Target {
|
|||
@override
|
||||
ConstantsBackend get constantsBackend => const ConstantsBackend();
|
||||
|
||||
@override
|
||||
Verification get verification => const WasmVerification();
|
||||
|
||||
@override
|
||||
String get name => 'wasm';
|
||||
|
||||
|
@ -378,3 +382,24 @@ class WasmTarget extends Target {
|
|||
int numPositionalFields, List<String> namedFields) =>
|
||||
recordClasses[RecordShape(numPositionalFields, namedFields)]!;
|
||||
}
|
||||
|
||||
class WasmVerification extends Verification {
|
||||
const WasmVerification();
|
||||
|
||||
@override
|
||||
bool allowNoFileOffset(VerificationStage stage, TreeNode node) {
|
||||
if (super.allowNoFileOffset(stage, node)) {
|
||||
return true;
|
||||
}
|
||||
if (stage >= VerificationStage.afterModularTransformations) {
|
||||
// Allow synthesized classes, procedures, fields and casts.
|
||||
// TODO(askesc): Improve the precision of these exceptions.
|
||||
return node is Class ||
|
||||
node is Constructor ||
|
||||
node is Procedure ||
|
||||
node is Field ||
|
||||
node is AsExpression;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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!),
|
||||
|
|
|
@ -11,8 +11,7 @@ import 'package:kernel/target/targets.dart';
|
|||
|
||||
import 'package:kernel/type_environment.dart' show TypeEnvironment;
|
||||
|
||||
import 'package:kernel/verifier.dart'
|
||||
show VerifyGetStaticType, VerifyingVisitor;
|
||||
import 'package:kernel/verifier.dart';
|
||||
|
||||
import '../compiler_context.dart' show CompilerContext;
|
||||
|
||||
|
@ -24,176 +23,32 @@ import '../fasta_codes.dart'
|
|||
noLength,
|
||||
templateInternalProblemVerificationError;
|
||||
|
||||
import '../type_inference/type_schema.dart' show UnknownType;
|
||||
|
||||
import 'redirecting_factory_body.dart'
|
||||
show
|
||||
RedirectingFactoryBody,
|
||||
isRedirectingFactory,
|
||||
isRedirectingFactoryField;
|
||||
|
||||
List<LocatedMessage> verifyComponent(Component component, Target target,
|
||||
{bool? isOutline, bool? afterConst, bool skipPlatform = false}) {
|
||||
FastaVerifyingVisitor verifier = new FastaVerifyingVisitor(target,
|
||||
isOutline: isOutline, afterConst: afterConst, skipPlatform: skipPlatform);
|
||||
List<LocatedMessage> verifyComponent(
|
||||
Target target, VerificationStage stage, Component component,
|
||||
{bool skipPlatform = false}) {
|
||||
FastaVerificationErrorListener listener =
|
||||
new FastaVerificationErrorListener();
|
||||
VerifyingVisitor verifier = new VerifyingVisitor(target, stage,
|
||||
skipPlatform: skipPlatform, listener: listener);
|
||||
component.accept(verifier);
|
||||
return verifier.errors;
|
||||
return listener.errors;
|
||||
}
|
||||
|
||||
class FastaVerifyingVisitor extends VerifyingVisitor {
|
||||
final Target target;
|
||||
final List<LocatedMessage> errors = <LocatedMessage>[];
|
||||
|
||||
Uri? fileUri;
|
||||
final List<TreeNode> treeNodeStack = <TreeNode>[];
|
||||
final bool skipPlatform;
|
||||
|
||||
FastaVerifyingVisitor(this.target,
|
||||
{bool? isOutline, bool? afterConst, required this.skipPlatform})
|
||||
: super(
|
||||
isOutline: isOutline,
|
||||
afterConst: afterConst,
|
||||
constantsAreAlwaysInlined:
|
||||
target.constantsBackend.alwaysInlineConstants);
|
||||
|
||||
/// Invoked by all visit methods if the visited node is a [TreeNode].
|
||||
void enterTreeNode(TreeNode node) {
|
||||
treeNodeStack.add(node);
|
||||
}
|
||||
|
||||
/// Invoked by all visit methods if the visited node is a [TreeNode].
|
||||
void exitTreeNode(TreeNode node) {
|
||||
if (treeNodeStack.isEmpty) {
|
||||
throw new StateError("Attempting to exit tree node '${node}' "
|
||||
"when the tree node stack is empty.");
|
||||
}
|
||||
if (!identical(treeNodeStack.last, node)) {
|
||||
throw new StateError("Attempting to exit tree node '${node}' "
|
||||
"when another node '${treeNodeStack.last}' is active.");
|
||||
}
|
||||
treeNodeStack.removeLast();
|
||||
}
|
||||
|
||||
TreeNode? getLastSeenTreeNode({bool withLocation = false}) {
|
||||
assert(treeNodeStack.isNotEmpty);
|
||||
for (int i = treeNodeStack.length - 1; i >= 0; --i) {
|
||||
TreeNode node = treeNodeStack[i];
|
||||
if (withLocation && !_hasLocation(node)) continue;
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
TreeNode? getSameLibraryLastSeenTreeNode({bool withLocation = false}) {
|
||||
if (treeNodeStack.isEmpty) return null;
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (currentLibrary == null || currentLibrary!.fileUri == null) return null;
|
||||
|
||||
for (int i = treeNodeStack.length - 1; i >= 0; --i) {
|
||||
TreeNode node = treeNodeStack[i];
|
||||
if (withLocation && !_hasLocation(node)) continue;
|
||||
if (node.location?.file != null &&
|
||||
node.location!.file == currentLibrary!.fileUri) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool _hasLocation(TreeNode node) {
|
||||
return node.location != null &&
|
||||
// ignore: unnecessary_null_comparison
|
||||
node.location!.file != null &&
|
||||
// ignore: unnecessary_null_comparison
|
||||
node.fileOffset != null &&
|
||||
node.fileOffset != -1;
|
||||
}
|
||||
|
||||
static bool _isInSameLibrary(Library? library, TreeNode node) {
|
||||
if (library == null) return false;
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (library.fileUri == null) return false;
|
||||
if (node.location == null) return false;
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (node.location!.file == null) return false;
|
||||
|
||||
return library.fileUri == node.location!.file;
|
||||
}
|
||||
|
||||
TreeNode? get localContext {
|
||||
TreeNode? result = getSameLibraryLastSeenTreeNode(withLocation: true);
|
||||
if (result == null &&
|
||||
currentClassOrExtensionOrMember != null &&
|
||||
_isInSameLibrary(currentLibrary, currentClassOrExtensionOrMember!)) {
|
||||
result = currentClassOrExtensionOrMember;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TreeNode? get remoteContext {
|
||||
TreeNode? result = getLastSeenTreeNode(withLocation: true);
|
||||
if (result != null && _isInSameLibrary(currentLibrary, result)) {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Uri checkLocation(TreeNode node, String? name, Uri fileUri) {
|
||||
if (name == null || name.contains("#")) {
|
||||
// TODO(ahe): Investigate if these checks can be enabled:
|
||||
// if (node.fileUri != null && node is! Library) {
|
||||
// problem(node, "A synthetic node shouldn't have a fileUri",
|
||||
// context: node);
|
||||
// }
|
||||
// if (node.fileOffset != -1) {
|
||||
// problem(node, "A synthetic node shouldn't have a fileOffset",
|
||||
// context: node);
|
||||
// }
|
||||
return fileUri;
|
||||
} else {
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (fileUri == null) {
|
||||
problem(node, "'$name' has no fileUri", context: node);
|
||||
return fileUri;
|
||||
}
|
||||
if (node.fileOffset == -1 && node is! Library) {
|
||||
problem(node, "'$name' has no fileOffset", context: node);
|
||||
}
|
||||
return fileUri;
|
||||
}
|
||||
}
|
||||
|
||||
void checkSuperInvocation(TreeNode node) {
|
||||
Member? containingMember = getContainingMember(node);
|
||||
if (containingMember == null) {
|
||||
problem(node, 'Super call outside of any member');
|
||||
} else {
|
||||
if (!containingMember.containsSuperCalls) {
|
||||
problem(
|
||||
node, 'Super call in a member lacking TransformerFlag.superCalls');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Member? getContainingMember(TreeNode? node) {
|
||||
while (node != null) {
|
||||
if (node is Member) return node;
|
||||
node = node.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
class FastaVerificationErrorListener implements VerificationErrorListener {
|
||||
List<LocatedMessage> errors = [];
|
||||
|
||||
@override
|
||||
void problem(TreeNode? node, String details,
|
||||
{TreeNode? context, TreeNode? origin}) {
|
||||
node ??= (context ?? currentClassOrExtensionOrMember);
|
||||
int offset = node?.fileOffset ?? -1;
|
||||
Uri? file = node?.location?.file ?? fileUri;
|
||||
Uri? uri = file == null ? null : file;
|
||||
void reportError(String details,
|
||||
{required TreeNode? node,
|
||||
required Uri? problemUri,
|
||||
required int? problemOffset,
|
||||
required TreeNode? context,
|
||||
required TreeNode? origin}) {
|
||||
Message message =
|
||||
templateInternalProblemVerificationError.withArguments(details);
|
||||
LocatedMessage locatedMessage = uri != null
|
||||
? message.withLocation(uri, offset, noLength)
|
||||
LocatedMessage locatedMessage = problemUri != null
|
||||
? message.withLocation(
|
||||
problemUri, problemOffset ?? TreeNode.noOffset, noLength)
|
||||
: message.withoutLocation();
|
||||
List<LocatedMessage>? contextMessages;
|
||||
if (origin != null) {
|
||||
|
@ -206,323 +61,6 @@ class FastaVerifyingVisitor extends VerifyingVisitor {
|
|||
.report(locatedMessage, Severity.error, context: contextMessages);
|
||||
errors.add(locatedMessage);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitAsExpression(AsExpression node) {
|
||||
enterTreeNode(node);
|
||||
super.visitAsExpression(node);
|
||||
if (node.fileOffset == -1) {
|
||||
TreeNode? parent = node.parent;
|
||||
while (parent != null) {
|
||||
if (parent.fileOffset != -1) break;
|
||||
parent = parent.parent;
|
||||
}
|
||||
problem(parent, "No offset for $node", context: node);
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitExpressionStatement(ExpressionStatement node) {
|
||||
// Bypass verification of the [StaticGet] in [RedirectingFactoryBody] as
|
||||
// this is a static get without a getter.
|
||||
if (node is! RedirectingFactoryBody) {
|
||||
enterTreeNode(node);
|
||||
super.visitExpressionStatement(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitLibrary(Library node) {
|
||||
// Issue(http://dartbug.com/32530)
|
||||
// 'dart:test' is used in the unit tests and isn't an actual part of the
|
||||
// platform.
|
||||
if (skipPlatform &&
|
||||
node.importUri.isScheme('dart') &&
|
||||
node.importUri.path != 'test') {
|
||||
return;
|
||||
}
|
||||
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name, node.fileUri);
|
||||
currentLibrary = node;
|
||||
super.visitLibrary(node);
|
||||
currentLibrary = null;
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitClass(Class node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name, node.fileUri);
|
||||
super.visitClass(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitExtension(Extension node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name, node.fileUri);
|
||||
super.visitExtension(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitField(Field node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name.text, node.fileUri);
|
||||
super.visitField(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitProcedure(Procedure node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name.text, node.fileUri);
|
||||
|
||||
// TODO(cstefantsova): Investigate why some redirecting factory bodies
|
||||
// retain the shape, but aren't of the RedirectingFactoryBody type.
|
||||
bool hasBody = isRedirectingFactory(node) ||
|
||||
RedirectingFactoryBody.hasRedirectingFactoryBodyShape(node);
|
||||
bool hasFlag = node.isRedirectingFactory;
|
||||
if (hasFlag && !hasBody) {
|
||||
problem(
|
||||
node,
|
||||
"Procedure '${node.name}' doesn't have a body "
|
||||
"of a redirecting factory, but has the "
|
||||
"'isRedirectingFactory' bit set.");
|
||||
}
|
||||
|
||||
super.visitProcedure(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
bool isNullType(DartType node) => node is NullType;
|
||||
|
||||
bool isObjectClass(Class c) {
|
||||
return c.name == "Object" &&
|
||||
c.enclosingLibrary.importUri.isScheme("dart") &&
|
||||
c.enclosingLibrary.importUri.path == "core";
|
||||
}
|
||||
|
||||
bool isTopType(DartType node) {
|
||||
return node is DynamicType ||
|
||||
node is VoidType ||
|
||||
node is InterfaceType &&
|
||||
isObjectClass(node.classNode) &&
|
||||
(node.nullability == Nullability.nullable ||
|
||||
node.nullability == Nullability.legacy) ||
|
||||
node is FutureOrType && isTopType(node.typeArgument);
|
||||
}
|
||||
|
||||
bool isFutureOrNull(DartType node) {
|
||||
return isNullType(node) ||
|
||||
node is FutureOrType && isFutureOrNull(node.typeArgument);
|
||||
}
|
||||
|
||||
@override
|
||||
void defaultDartType(DartType node) {
|
||||
final TreeNode? localContext = this.localContext;
|
||||
final TreeNode? remoteContext = this.remoteContext;
|
||||
|
||||
if (node is UnknownType) {
|
||||
problem(localContext, "Unexpected appearance of the unknown type.",
|
||||
origin: remoteContext);
|
||||
}
|
||||
|
||||
// TODO(johnniwinther): This check wasn't called from InterfaceType and
|
||||
// is currently very broken. Disabling for now.
|
||||
/*bool isTypeCast = localContext != null &&
|
||||
localContext is AsExpression &&
|
||||
localContext.isTypeError;
|
||||
// Don't check cases like foo(x as{TypeError} T). In cases where foo comes
|
||||
// from a library with a different opt-in status than the current library,
|
||||
// the check may not be necessary. For now, just skip all type-error casts.
|
||||
// TODO(cstefantsova): Implement a more precise analysis.
|
||||
bool isFromAnotherLibrary = remoteContext != null || isTypeCast;
|
||||
|
||||
// Checking for non-legacy types in opt-out libraries.
|
||||
{
|
||||
bool neverLegacy = isNullType(node) ||
|
||||
isFutureOrNull(node) ||
|
||||
isTopType(node) ||
|
||||
node is InvalidType ||
|
||||
node is NeverType ||
|
||||
node is BottomType;
|
||||
// TODO(cstefantsova): Consider checking types coming from other
|
||||
// libraries.
|
||||
bool expectedLegacy = !isFromAnotherLibrary &&
|
||||
!currentLibrary.isNonNullableByDefault &&
|
||||
!neverLegacy;
|
||||
if (expectedLegacy && node.nullability != Nullability.legacy) {
|
||||
problem(localContext,
|
||||
"Found a non-legacy type '${node}' in an opted-out library.",
|
||||
origin: remoteContext);
|
||||
}
|
||||
}
|
||||
|
||||
// Checking for legacy types in opt-in libraries.
|
||||
{
|
||||
Nullability nodeNullability =
|
||||
node is InvalidType ? Nullability.undetermined : node.nullability;
|
||||
// TODO(cstefantsova): Consider checking types coming from other
|
||||
// libraries.
|
||||
if (!isFromAnotherLibrary &&
|
||||
currentLibrary.isNonNullableByDefault &&
|
||||
nodeNullability == Nullability.legacy) {
|
||||
problem(localContext,
|
||||
"Found a legacy type '${node}' in an opted-in library.",
|
||||
origin: remoteContext);
|
||||
}
|
||||
}*/
|
||||
|
||||
super.defaultDartType(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitFunctionType(FunctionType node) {
|
||||
if (node.typeParameters.isNotEmpty) {
|
||||
for (TypeParameter typeParameter in node.typeParameters) {
|
||||
if (typeParameter.parent != null) {
|
||||
problem(
|
||||
localContext,
|
||||
"Type parameters of function types shouldn't have parents: "
|
||||
"$node.");
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitFunctionType(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitInterfaceType(InterfaceType node) {
|
||||
if (isNullType(node) && node.nullability != Nullability.nullable) {
|
||||
problem(localContext, "Found a not nullable Null type: ${node}");
|
||||
}
|
||||
super.visitInterfaceType(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSuperMethodInvocation(SuperMethodInvocation node) {
|
||||
enterTreeNode(node);
|
||||
checkSuperInvocation(node);
|
||||
super.visitSuperMethodInvocation(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSuperPropertyGet(SuperPropertyGet node) {
|
||||
enterTreeNode(node);
|
||||
checkSuperInvocation(node);
|
||||
super.visitSuperPropertyGet(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSuperPropertySet(SuperPropertySet node) {
|
||||
enterTreeNode(node);
|
||||
checkSuperInvocation(node);
|
||||
super.visitSuperPropertySet(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitStaticInvocation(StaticInvocation node) {
|
||||
enterTreeNode(node);
|
||||
super.visitStaticInvocation(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
void _checkConstructorTearOff(Node node, Member tearOffTarget) {
|
||||
if (tearOffTarget.enclosingLibrary.importUri.isScheme('dart')) {
|
||||
// Platform libraries are not compilation with test flags and might
|
||||
// contain tear-offs not expected when testing lowerings.
|
||||
return;
|
||||
}
|
||||
if (currentMember != null && isRedirectingFactoryField(currentMember!)) {
|
||||
// The encoding of the redirecting factory field uses
|
||||
// [ConstructorTearOffConstant] nodes also when lowerings are enabled.
|
||||
return;
|
||||
}
|
||||
if (tearOffTarget is Constructor &&
|
||||
target.isConstructorTearOffLoweringEnabled) {
|
||||
problem(
|
||||
node is TreeNode ? node : getLastSeenTreeNode(),
|
||||
'${node.runtimeType} nodes for generative constructors should be '
|
||||
'lowered for target "${target.name}".');
|
||||
}
|
||||
if (tearOffTarget is Procedure &&
|
||||
tearOffTarget.isFactory &&
|
||||
target.isFactoryTearOffLoweringEnabled) {
|
||||
problem(
|
||||
node is TreeNode ? node : getLastSeenTreeNode(),
|
||||
'${node.runtimeType} nodes for factory constructors should be '
|
||||
'lowered for target "${target.name}".');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitConstructorTearOff(ConstructorTearOff node) {
|
||||
_checkConstructorTearOff(node, node.target);
|
||||
super.visitConstructorTearOff(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitConstructorTearOffConstant(ConstructorTearOffConstant node) {
|
||||
_checkConstructorTearOff(node, node.target);
|
||||
super.visitConstructorTearOffConstant(node);
|
||||
}
|
||||
|
||||
void _checkTypedefTearOff(Node node) {
|
||||
if (target.isTypedefTearOffLoweringEnabled) {
|
||||
problem(
|
||||
node is TreeNode ? node : getLastSeenTreeNode(),
|
||||
'${node.runtimeType} nodes for typedefs should be '
|
||||
'lowered for target "${target.name}".');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitTypedefTearOff(TypedefTearOff node) {
|
||||
_checkTypedefTearOff(node);
|
||||
super.visitTypedefTearOff(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitTypedefTearOffConstant(TypedefTearOffConstant node) {
|
||||
_checkTypedefTearOff(node);
|
||||
super.visitTypedefTearOffConstant(node);
|
||||
}
|
||||
|
||||
void _checkRedirectingFactoryTearOff(Node node) {
|
||||
if (target.isRedirectingFactoryTearOffLoweringEnabled) {
|
||||
problem(
|
||||
node is TreeNode ? node : getLastSeenTreeNode(),
|
||||
'ConstructorTearOff nodes for redirecting factories should be '
|
||||
'lowered for target "${target.name}".');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) {
|
||||
_checkRedirectingFactoryTearOff(node);
|
||||
super.visitRedirectingFactoryTearOff(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRedirectingFactoryTearOffConstant(
|
||||
RedirectingFactoryTearOffConstant node) {
|
||||
_checkRedirectingFactoryTearOff(node);
|
||||
super.visitRedirectingFactoryTearOffConstant(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void defaultTreeNode(TreeNode node) {
|
||||
enterTreeNode(node);
|
||||
super.defaultTreeNode(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
void verifyGetStaticType(TypeEnvironment env, Component component,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
|
|||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/class_hierarchy.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/verifier.dart' show VerificationStage;
|
||||
|
||||
import 'api_prototype/file_system.dart' show FileSystem;
|
||||
import 'api_prototype/front_end.dart' show CompilerOptions, CompilerResult;
|
||||
|
@ -145,8 +146,8 @@ Future<CompilerResult> _buildInternal(
|
|||
List<int>? summary = null;
|
||||
if (buildSummary) {
|
||||
if (options.verify) {
|
||||
for (LocatedMessage error
|
||||
in verifyComponent(summaryComponent, options.target)) {
|
||||
for (LocatedMessage error in verifyComponent(
|
||||
options.target, VerificationStage.outline, summaryComponent)) {
|
||||
options.report(error, Severity.error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -119,6 +119,7 @@ import 'package:kernel/target/targets.dart'
|
|||
|
||||
import 'package:kernel/type_environment.dart'
|
||||
show StaticTypeContext, TypeEnvironment;
|
||||
import 'package:kernel/verifier.dart' show VerificationStage;
|
||||
import 'package:testing/testing.dart'
|
||||
show
|
||||
Chain,
|
||||
|
@ -367,7 +368,9 @@ class FastaContext extends ChainContext with MatchContext {
|
|||
: steps = <Step>[
|
||||
new Outline(compileMode, updateComments: updateComments),
|
||||
const Print(),
|
||||
new Verify(compileMode)
|
||||
new Verify(compileMode == CompileMode.full
|
||||
? VerificationStage.afterConstantEvaluation
|
||||
: VerificationStage.outline)
|
||||
] {
|
||||
String prefix;
|
||||
String infix;
|
||||
|
@ -407,7 +410,7 @@ class FastaContext extends ChainContext with MatchContext {
|
|||
switch (compileMode) {
|
||||
case CompileMode.full:
|
||||
steps.add(const Transform());
|
||||
steps.add(const Verify(CompileMode.full));
|
||||
steps.add(const Verify(VerificationStage.afterModularTransformations));
|
||||
steps.add(const StressConstantEvaluatorStep());
|
||||
if (!ignoreExpectations) {
|
||||
steps.add(new MatchExpectation("$prefix$infix.transformed.expect",
|
||||
|
@ -2486,9 +2489,9 @@ class Transform extends Step<ComponentResult, ComponentResult, FastaContext> {
|
|||
}
|
||||
|
||||
class Verify extends Step<ComponentResult, ComponentResult, FastaContext> {
|
||||
final CompileMode compileMode;
|
||||
final VerificationStage stage;
|
||||
|
||||
const Verify(this.compileMode);
|
||||
const Verify(this.stage);
|
||||
|
||||
@override
|
||||
String get name => "verify";
|
||||
|
@ -2518,8 +2521,8 @@ class Verify extends Step<ComponentResult, ComponentResult, FastaContext> {
|
|||
result.options, (compilerContext) async {
|
||||
compilerContext.uriToSource.addAll(component.uriToSource);
|
||||
List<LocatedMessage> verificationErrors = verifyComponent(
|
||||
component, result.options.target,
|
||||
isOutline: compileMode == CompileMode.outline, skipPlatform: true);
|
||||
result.options.target, stage, component,
|
||||
skipPlatform: true);
|
||||
assert(verificationErrors.isEmpty || messages.isNotEmpty);
|
||||
if (messages.isEmpty) {
|
||||
return pass(result);
|
||||
|
|
|
@ -29,6 +29,7 @@ import 'package:front_end/src/fasta/kernel/verifier.dart' show verifyComponent;
|
|||
|
||||
import 'package:kernel/ast.dart' show Component;
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/verifier.dart' show VerificationStage;
|
||||
|
||||
const Map<String, String> files = const <String, String>{
|
||||
"repro.dart": """
|
||||
|
@ -96,8 +97,9 @@ Future<void> test() async {
|
|||
|
||||
List<Object> errors = await CompilerContext.runWithOptions(
|
||||
new ProcessedOptions(options: options, inputs: inputs),
|
||||
(_) => new Future<List<Object>>.value(
|
||||
verifyComponent(component, options.target!, skipPlatform: true)));
|
||||
(_) => new Future<List<Object>>.value(verifyComponent(options.target!,
|
||||
VerificationStage.afterModularTransformations, component,
|
||||
skipPlatform: true)));
|
||||
|
||||
serializeComponent(component);
|
||||
|
||||
|
|
|
@ -538,6 +538,9 @@ Future<void> compilePlatformInternal(CompilerContext c, Uri fullOutput,
|
|||
new File.fromUri(outlineOutput).writeAsBytesSync(result.summary!);
|
||||
c.options.ticker.logMs("Wrote outline to ${outlineOutput.toFilePath()}");
|
||||
|
||||
// TODO(johnniwinther): Enable verification.
|
||||
//verifyComponent(c.options.target,
|
||||
// VerificationStage.afterModularTransformations, result.component!);
|
||||
await writeComponentToFile(result.component!, fullOutput);
|
||||
|
||||
c.options.ticker.logMs("Wrote component to ${fullOutput.toFilePath()}");
|
||||
|
|
|
@ -13,7 +13,9 @@ import 'package:front_end/src/api_unstable/vm.dart'
|
|||
import 'package:frontend_server/starter.dart';
|
||||
import 'package:kernel/ast.dart' show Component;
|
||||
import 'package:kernel/kernel.dart' show loadComponentFromBytes;
|
||||
import 'package:kernel/verifier.dart' show verifyComponent;
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/verifier.dart' show VerificationStage, verifyComponent;
|
||||
import 'package:vm/kernel_front_end.dart';
|
||||
|
||||
main(List<String> args) async {
|
||||
String? flutterDir;
|
||||
|
@ -244,11 +246,13 @@ Future<List<String>> attemptStuff(
|
|||
List<int> platformData =
|
||||
File.fromUri(flutterPlatformDirectory.uri.resolve("platform_strong.dill"))
|
||||
.readAsBytesSync();
|
||||
final String targetName = 'flutter';
|
||||
final Target target = createFrontEndTarget(targetName)!;
|
||||
final List<String> args = <String>[
|
||||
'--sdk-root',
|
||||
flutterPlatformDirectory.path,
|
||||
'--incremental',
|
||||
'--target=flutter',
|
||||
'--target=$targetName',
|
||||
'--packages',
|
||||
packageConfig.path,
|
||||
'--output-dill=${dillFile.path}',
|
||||
|
@ -303,7 +307,8 @@ Future<List<String>> attemptStuff(
|
|||
List<int> resultBytes = dillFile.readAsBytesSync();
|
||||
Component component = loadComponentFromBytes(platformData);
|
||||
component = loadComponentFromBytes(resultBytes, component);
|
||||
verifyComponent(component);
|
||||
verifyComponent(
|
||||
target, VerificationStage.afterModularTransformations, component);
|
||||
logger
|
||||
.log(" => verified in ${stopwatch2.elapsedMilliseconds} ms.");
|
||||
}
|
||||
|
|
|
@ -18,10 +18,12 @@ import 'package:frontend_server/starter.dart';
|
|||
import 'package:kernel/ast.dart' show Component;
|
||||
import 'package:kernel/binary/ast_to_binary.dart';
|
||||
import 'package:kernel/kernel.dart' show loadComponentFromBinary;
|
||||
import 'package:kernel/verifier.dart' show verifyComponent;
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/verifier.dart' show VerificationStage, verifyComponent;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm/incremental_compiler.dart';
|
||||
import 'package:vm/kernel_front_end.dart';
|
||||
|
||||
class _MockedBinaryPrinter implements BinaryPrinter {
|
||||
@override
|
||||
|
@ -1164,6 +1166,8 @@ class BarState extends State<FizzWidget> {
|
|||
expect(dillFile.existsSync(), equals(false));
|
||||
|
||||
// First compile app entry point A.
|
||||
final String targetName = 'vm';
|
||||
final Target target = createFrontEndTarget(targetName)!;
|
||||
final List<String> args = <String>[
|
||||
'--sdk-root=${sdkRoot.toFilePath()}',
|
||||
'--incremental',
|
||||
|
@ -1212,7 +1216,8 @@ class BarState extends State<FizzWidget> {
|
|||
// Verifiable (together with the platform file).
|
||||
component =
|
||||
loadComponentFromBinary(platformKernel.toFilePath(), component);
|
||||
verifyComponent(component);
|
||||
verifyComponent(target,
|
||||
VerificationStage.afterModularTransformations, component);
|
||||
}
|
||||
});
|
||||
expect(await result, 0);
|
||||
|
@ -1285,6 +1290,8 @@ class BarState extends State<FizzWidget> {
|
|||
expect(dillFile.existsSync(), equals(false));
|
||||
|
||||
// First compile app entry point A.
|
||||
final String targetName = 'vm';
|
||||
final Target target = createFrontEndTarget(targetName)!;
|
||||
final List<String> args = <String>[
|
||||
'--sdk-root=${sdkRoot.toFilePath()}',
|
||||
'--incremental',
|
||||
|
@ -1332,7 +1339,8 @@ class BarState extends State<FizzWidget> {
|
|||
// Verifiable (together with the platform file).
|
||||
component =
|
||||
loadComponentFromBinary(platformKernel.toFilePath(), component);
|
||||
verifyComponent(component);
|
||||
verifyComponent(target,
|
||||
VerificationStage.afterModularTransformations, component);
|
||||
}
|
||||
});
|
||||
expect(await result, 0);
|
||||
|
@ -2850,6 +2858,8 @@ e() {
|
|||
var dillFile = File('${tempDir.path}/full.dill');
|
||||
var incrementalDillFile = File('${tempDir.path}/incremental.dill');
|
||||
expect(dillFile.existsSync(), equals(false));
|
||||
final String targetName = 'vm';
|
||||
final Target target = createFrontEndTarget(targetName)!;
|
||||
final List<String> args = <String>[
|
||||
'--sdk-root=${sdkRoot.toFilePath()}',
|
||||
'--incremental',
|
||||
|
@ -2915,7 +2925,7 @@ e() {
|
|||
component =
|
||||
loadComponentFromBinary(platformKernel.toFilePath(), component);
|
||||
expect(component.mainMethod, isNotNull);
|
||||
verifyComponent(component);
|
||||
verifyComponent(target, VerificationStage.afterModularTransformations, component);
|
||||
|
||||
count += 1;
|
||||
|
||||
|
@ -2940,7 +2950,7 @@ e() {
|
|||
component =
|
||||
loadComponentFromBinary(platformKernel.toFilePath(), component);
|
||||
expect(component.mainMethod, isNotNull);
|
||||
verifyComponent(component);
|
||||
verifyComponent(target, VerificationStage.afterModularTransformations, component);
|
||||
|
||||
count += 1;
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -8,6 +8,7 @@ import '../ast.dart';
|
|||
import '../class_hierarchy.dart';
|
||||
import '../core_types.dart';
|
||||
import '../reference_from_index.dart';
|
||||
import '../verifier.dart';
|
||||
import 'changed_structure_notifier.dart';
|
||||
|
||||
final List<String> targetNames = targets.keys.toList();
|
||||
|
@ -539,6 +540,9 @@ abstract class Target {
|
|||
|
||||
ConstantsBackend get constantsBackend;
|
||||
|
||||
/// Object that defines how AST nodes are verified for this [Target].
|
||||
Verification get verification => const Verification();
|
||||
|
||||
/// Returns an [DartLibrarySupport] the defines which, if any, of the
|
||||
/// `dart:` libraries supported in the platform, that should not be
|
||||
/// considered supported when queried in conditional imports and
|
||||
|
|
|
@ -5,17 +5,82 @@
|
|||
library kernel.checks;
|
||||
|
||||
import 'ast.dart';
|
||||
import 'src/redirecting_factory_body.dart';
|
||||
import 'target/targets.dart';
|
||||
import 'transformations/flags.dart';
|
||||
import 'type_environment.dart' show StatefulStaticTypeContext, TypeEnvironment;
|
||||
|
||||
void verifyComponent(Component component,
|
||||
{bool? isOutline,
|
||||
bool? afterConst,
|
||||
bool constantsAreAlwaysInlined = true}) {
|
||||
VerifyingVisitor.check(component,
|
||||
isOutline: isOutline,
|
||||
afterConst: afterConst,
|
||||
constantsAreAlwaysInlined: constantsAreAlwaysInlined);
|
||||
/// Stages at which verification can occur.
|
||||
///
|
||||
/// These can be used to enforce different invariants during different stages
|
||||
/// of the compilation.
|
||||
enum VerificationStage {
|
||||
/// Verification after the outline compilation.
|
||||
outline,
|
||||
|
||||
/// Verification after the body, aka full, compilation, but before pre-
|
||||
/// constant evaluation transformations have been performed.
|
||||
beforePreConstantEvaluationTransformations,
|
||||
|
||||
/// Verification after pre- constant evaluation transformations have been
|
||||
/// performed but before constant evaluation.
|
||||
beforeConstantEvaluation,
|
||||
|
||||
/// Verification after constant evaluation but before modular transformations
|
||||
/// have been performed.
|
||||
afterConstantEvaluation,
|
||||
|
||||
/// Verification after modular transformations have been performed.
|
||||
///
|
||||
/// This is final stage of a normal compilation.
|
||||
afterModularTransformations,
|
||||
|
||||
/// Verification after global transformations have been performed.
|
||||
///
|
||||
/// The global transformation is an additional step performed by some
|
||||
/// backends which is not triggered by the front end compilation itself.
|
||||
afterGlobalTransformations,
|
||||
;
|
||||
|
||||
bool operator <(VerificationStage other) => index < other.index;
|
||||
bool operator <=(VerificationStage other) => index <= other.index;
|
||||
bool operator >(VerificationStage other) => index > other.index;
|
||||
bool operator >=(VerificationStage other) => index >= other.index;
|
||||
}
|
||||
|
||||
/// Interface that defines how the AST is verified.
|
||||
class Verification {
|
||||
const Verification();
|
||||
|
||||
/// Returns `true` if [node] is allowed to have no file offset.
|
||||
bool allowNoFileOffset(VerificationStage stage, TreeNode node) {
|
||||
return node is Library;
|
||||
}
|
||||
|
||||
/// Returns `true` if [node] is allowed to have location with a file offset
|
||||
/// that is not in the range of the enclosing file.
|
||||
bool allowInvalidLocation(VerificationStage stage, TreeNode node) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void verifyComponent(
|
||||
Target target, VerificationStage stage, Component component,
|
||||
{bool skipPlatform = false}) {
|
||||
VerifyingVisitor.check(target, stage, component, skipPlatform: skipPlatform);
|
||||
}
|
||||
|
||||
class VerificationErrorListener {
|
||||
const VerificationErrorListener();
|
||||
|
||||
void reportError(String details,
|
||||
{required TreeNode? node,
|
||||
required Uri? problemUri,
|
||||
required int? problemOffset,
|
||||
required TreeNode? context,
|
||||
required TreeNode? origin}) {
|
||||
throw new VerificationError(context, node, details);
|
||||
}
|
||||
}
|
||||
|
||||
class VerificationError {
|
||||
|
@ -53,6 +118,15 @@ enum TypedefState { Done, BeingChecked }
|
|||
///
|
||||
/// This does not include any kind of type checking.
|
||||
class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
||||
final Target target;
|
||||
|
||||
Uri? fileUri;
|
||||
|
||||
final VerificationErrorListener listener;
|
||||
|
||||
final List<TreeNode> treeNodeStack = <TreeNode>[];
|
||||
final bool skipPlatform;
|
||||
|
||||
final Set<Class> classes = new Set<Class>();
|
||||
final Set<Typedef> typedefs = new Set<Typedef>();
|
||||
Set<TypeParameter> typeParametersInScope = new Set<TypeParameter>();
|
||||
|
@ -63,17 +137,8 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
final Set<Constant> seenConstants = <Constant>{};
|
||||
bool classTypeParametersAreInScope = false;
|
||||
|
||||
/// If true, relax certain checks for *outline* mode. For example, don't
|
||||
/// attempt to validate constructor initializers.
|
||||
final bool isOutline;
|
||||
|
||||
/// If true, assume that constant evaluation has been performed (with a
|
||||
/// target that did not opt out of any of the constant inlining) and report
|
||||
/// a verification error for anything that should have been removed by it.
|
||||
final bool afterConst;
|
||||
|
||||
/// If true, constant fields and local variables are expected to be inlined.
|
||||
final bool constantsAreAlwaysInlined;
|
||||
/// The compilation stage at which this verification is performed.
|
||||
final VerificationStage stage;
|
||||
|
||||
AsyncMarker currentAsyncMarker = AsyncMarker.Sync;
|
||||
|
||||
|
@ -98,26 +163,35 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
TreeNode? get currentClassOrExtensionOrMember =>
|
||||
currentMember ?? currentClass ?? currentExtension ?? currentInlineClass;
|
||||
|
||||
static void check(Component component,
|
||||
{bool? isOutline,
|
||||
bool? afterConst,
|
||||
required bool constantsAreAlwaysInlined}) {
|
||||
component.accept(new VerifyingVisitor(
|
||||
isOutline: isOutline,
|
||||
afterConst: afterConst,
|
||||
constantsAreAlwaysInlined: constantsAreAlwaysInlined));
|
||||
static void check(Target target, VerificationStage stage, Component component,
|
||||
{required bool skipPlatform}) {
|
||||
component.accept(
|
||||
new VerifyingVisitor(target, stage, skipPlatform: skipPlatform));
|
||||
}
|
||||
|
||||
VerifyingVisitor(
|
||||
{bool? isOutline,
|
||||
bool? afterConst,
|
||||
required this.constantsAreAlwaysInlined})
|
||||
: isOutline = isOutline ?? false,
|
||||
afterConst = afterConst ?? !(isOutline ?? false);
|
||||
VerifyingVisitor(this.target, this.stage,
|
||||
{required this.skipPlatform,
|
||||
VerificationErrorListener this.listener =
|
||||
const VerificationErrorListener()});
|
||||
|
||||
/// If true, relax certain checks for *outline* mode. For example, don't
|
||||
/// attempt to validate constructor initializers.
|
||||
bool get isOutline => stage == VerificationStage.outline;
|
||||
|
||||
/// If true, assume that constant evaluation has been performed (with a
|
||||
/// target that did not opt out of any of the constant inlining) and report
|
||||
/// a verification error for anything that should have been removed by it.
|
||||
bool get afterConst => stage >= VerificationStage.afterConstantEvaluation;
|
||||
|
||||
/// If true, constant fields and local variables are expected to be inlined.
|
||||
bool get constantsAreAlwaysInlined =>
|
||||
target.constantsBackend.alwaysInlineConstants;
|
||||
|
||||
@override
|
||||
void defaultTreeNode(TreeNode node) {
|
||||
enterTreeNode(node);
|
||||
visitChildren(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -132,9 +206,21 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
constant.visitChildren(this);
|
||||
}
|
||||
|
||||
void problem(TreeNode? node, String details, {TreeNode? context}) {
|
||||
context ??= currentClassOrExtensionOrMember;
|
||||
throw new VerificationError(context, node, details);
|
||||
void problem(TreeNode? node, String details,
|
||||
{TreeNode? context, TreeNode? origin}) {
|
||||
TreeNode? problemNode = node ?? context ?? currentClassOrExtensionOrMember;
|
||||
int offset = problemNode?.fileOffset ?? -1;
|
||||
Location? location = problemNode != null
|
||||
? _getLocation(problemNode, allowInvalidLocation: true)
|
||||
: null;
|
||||
Uri? file = location?.file ?? fileUri;
|
||||
Uri? uri = file == null ? null : file;
|
||||
listener.reportError(details,
|
||||
problemUri: uri,
|
||||
problemOffset: offset,
|
||||
node: node,
|
||||
context: context ?? currentClassOrExtensionOrMember,
|
||||
origin: origin);
|
||||
}
|
||||
|
||||
TreeNode? enterParent(TreeNode node) {
|
||||
|
@ -171,9 +257,11 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
}
|
||||
|
||||
void visitWithLocalScope(TreeNode node) {
|
||||
enterTreeNode(node);
|
||||
int stackHeight = enterLocalScope();
|
||||
visitChildren(node);
|
||||
exitLocalScope(stackHeight);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
void declareMember(Member member) {
|
||||
|
@ -261,13 +349,27 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
|
||||
@override
|
||||
void visitLibrary(Library node) {
|
||||
// Issue(http://dartbug.com/32530)
|
||||
// 'dart:test' is used in the unit tests and isn't an actual part of the
|
||||
// platform.
|
||||
if (skipPlatform &&
|
||||
node.importUri.isScheme('dart') &&
|
||||
node.importUri.path != 'test') {
|
||||
return;
|
||||
}
|
||||
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name, node.fileUri);
|
||||
currentLibrary = node;
|
||||
super.visitLibrary(node);
|
||||
currentLibrary = null;
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitExtension(Extension node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name, node.fileUri);
|
||||
currentExtension = node;
|
||||
declareTypeParameters(node.typeParameters);
|
||||
final TreeNode? oldParent = enterParent(node);
|
||||
|
@ -275,10 +377,13 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
exitParent(oldParent);
|
||||
undeclareTypeParameters(node.typeParameters);
|
||||
currentExtension = null;
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitInlineClass(InlineClass node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name, node.fileUri);
|
||||
currentInlineClass = node;
|
||||
declareTypeParameters(node.typeParameters);
|
||||
final TreeNode? oldParent = enterParent(node);
|
||||
|
@ -286,6 +391,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
exitParent(oldParent);
|
||||
undeclareTypeParameters(node.typeParameters);
|
||||
currentInlineClass = null;
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
void checkTypedef(Typedef node) {
|
||||
|
@ -295,6 +401,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
problem(node, "The typedef '$node' refers to itself", context: node);
|
||||
}
|
||||
assert(state == null);
|
||||
enterTreeNode(node);
|
||||
typedefState[node] = TypedefState.BeingChecked;
|
||||
Set<TypeParameter> savedTypeParameters = typeParametersInScope;
|
||||
typeParametersInScope = node.typeParameters.toSet();
|
||||
|
@ -306,17 +413,23 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
currentParent = savedParent;
|
||||
typeParametersInScope = savedTypeParameters;
|
||||
typedefState[node] = TypedefState.Done;
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitTypedef(Typedef node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name, node.fileUri);
|
||||
checkTypedef(node);
|
||||
// Enter and exit the node to check the parent pointer on the typedef node.
|
||||
exitParent(enterParent(node));
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitField(Field node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name.text, node.fileUri);
|
||||
currentMember = node;
|
||||
TreeNode? oldParent = enterParent(node);
|
||||
bool isTopLevel = node.parent == currentLibrary;
|
||||
|
@ -356,10 +469,27 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
visitList(node.annotations, this);
|
||||
exitParent(oldParent);
|
||||
currentMember = null;
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitProcedure(Procedure node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name.text, node.fileUri);
|
||||
|
||||
// TODO(cstefantsova): Investigate why some redirecting factory bodies
|
||||
// retain the shape, but aren't of the RedirectingFactoryBody type.
|
||||
bool hasBody = isRedirectingFactory(node) ||
|
||||
RedirectingFactoryBody.hasRedirectingFactoryBodyShape(node);
|
||||
bool hasFlag = node.isRedirectingFactory;
|
||||
if (hasFlag && !hasBody) {
|
||||
problem(
|
||||
node,
|
||||
"Procedure '${node.name}' doesn't have a body "
|
||||
"of a redirecting factory, but has the "
|
||||
"'isRedirectingFactory' bit set.");
|
||||
}
|
||||
|
||||
currentMember = node;
|
||||
TreeNode? oldParent = enterParent(node);
|
||||
classTypeParametersAreInScope = !node.isStatic;
|
||||
|
@ -423,10 +553,13 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
}
|
||||
}*/
|
||||
currentMember = null;
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitConstructor(Constructor node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name.text, node.fileUri);
|
||||
currentMember = node;
|
||||
classTypeParametersAreInScope = true;
|
||||
// The constructor member needs special treatment due to parameters being
|
||||
|
@ -456,10 +589,13 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
}*/
|
||||
classTypeParametersAreInScope = false;
|
||||
currentMember = null;
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitClass(Class node) {
|
||||
enterTreeNode(node);
|
||||
fileUri = checkLocation(node, node.name, node.fileUri);
|
||||
currentClass = node;
|
||||
declareTypeParameters(node.typeParameters);
|
||||
TreeNode? oldParent = enterParent(node);
|
||||
|
@ -473,10 +609,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
exitParent(oldParent);
|
||||
undeclareTypeParameters(node.typeParameters);
|
||||
currentClass = null;
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitFunctionNode(FunctionNode node) {
|
||||
enterTreeNode(node);
|
||||
declareTypeParameters(node.typeParameters);
|
||||
bool savedInCatchBlock = inCatchBlock;
|
||||
AsyncMarker savedAsyncMarker = currentAsyncMarker;
|
||||
|
@ -492,10 +630,21 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
inCatchBlock = savedInCatchBlock;
|
||||
currentAsyncMarker = savedAsyncMarker;
|
||||
undeclareTypeParameters(node.typeParameters);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitFunctionType(FunctionType node) {
|
||||
if (node.typeParameters.isNotEmpty) {
|
||||
for (TypeParameter typeParameter in node.typeParameters) {
|
||||
if (typeParameter.parent != null) {
|
||||
problem(
|
||||
localContext,
|
||||
"Type parameters of function types shouldn't have parents: "
|
||||
"$node.");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < node.namedParameters.length; ++i) {
|
||||
if (node.namedParameters[i - 1].compareTo(node.namedParameters[i]) >= 0) {
|
||||
problem(currentParent,
|
||||
|
@ -544,6 +693,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
|
||||
@override
|
||||
void visitBlockExpression(BlockExpression node) {
|
||||
enterTreeNode(node);
|
||||
int stackHeight = enterLocalScope();
|
||||
// Do not visit the block directly because the value expression needs to
|
||||
// be in its scope.
|
||||
|
@ -556,6 +706,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
node.value.accept(this);
|
||||
exitParent(oldParent);
|
||||
exitLocalScope(stackHeight);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -613,6 +764,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
|
||||
@override
|
||||
void visitVariableDeclaration(VariableDeclaration node) {
|
||||
enterTreeNode(node);
|
||||
TreeNode? parent = node.parent;
|
||||
if (parent is! Block &&
|
||||
!(parent is Catch && parent.body != node) &&
|
||||
|
@ -640,25 +792,31 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
}
|
||||
}
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitVariableGet(VariableGet node) {
|
||||
enterTreeNode(node);
|
||||
checkVariableInScope(node.variable, node);
|
||||
visitChildren(node);
|
||||
if (constantsAreAlwaysInlined && afterConst && node.variable.isConst) {
|
||||
problem(node, "VariableGet of const variable '${node.variable}'.");
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitVariableSet(VariableSet node) {
|
||||
enterTreeNode(node);
|
||||
checkVariableInScope(node.variable, node);
|
||||
visitChildren(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitStaticGet(StaticGet node) {
|
||||
enterTreeNode(node);
|
||||
visitChildren(node);
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (node.target == null) {
|
||||
|
@ -684,10 +842,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
node.target.isConst) {
|
||||
problem(node, "StaticGet of const field '${node.target}'.");
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitStaticSet(StaticSet node) {
|
||||
enterTreeNode(node);
|
||||
visitChildren(node);
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (node.target == null) {
|
||||
|
@ -699,10 +859,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
if (node.target.isInstanceMember) {
|
||||
problem(node, "StaticSet to '${node.target}' that's an instance member.");
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitStaticInvocation(StaticInvocation node) {
|
||||
enterTreeNode(node);
|
||||
checkTargetedInvocation(node.target, node);
|
||||
if (node.target.isInstanceMember) {
|
||||
problem(node,
|
||||
|
@ -720,10 +882,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
if (afterConst && node.isConst && !inUnevaluatedConstant) {
|
||||
problem(node, "Constant StaticInvocation.");
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitTypedefTearOff(TypedefTearOff node) {
|
||||
_checkTypedefTearOff(node);
|
||||
declareTypeParameters(node.typeParameters);
|
||||
super.visitTypedefTearOff(node);
|
||||
undeclareTypeParameters(node.typeParameters);
|
||||
|
@ -755,6 +919,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
|
||||
@override
|
||||
void visitConstructorInvocation(ConstructorInvocation node) {
|
||||
enterTreeNode(node);
|
||||
checkTargetedInvocation(node.target, node);
|
||||
if (node.target.enclosingClass.isAbstract) {
|
||||
problem(node, "ConstructorInvocation of abstract class.");
|
||||
|
@ -768,6 +933,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
if (afterConst && node.isConst) {
|
||||
problem(node, "Invocation of const constructor '${node.target}'.");
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
bool areArgumentsCompatible(Arguments arguments, FunctionNode function) {
|
||||
|
@ -791,37 +957,46 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
|
||||
@override
|
||||
void visitListLiteral(ListLiteral node) {
|
||||
enterTreeNode(node);
|
||||
visitChildren(node);
|
||||
if (afterConst && node.isConst) {
|
||||
problem(node, "Constant list literal.");
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSetLiteral(SetLiteral node) {
|
||||
enterTreeNode(node);
|
||||
visitChildren(node);
|
||||
if (afterConst && node.isConst) {
|
||||
problem(node, "Constant set literal.");
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitMapLiteral(MapLiteral node) {
|
||||
enterTreeNode(node);
|
||||
visitChildren(node);
|
||||
if (afterConst && node.isConst) {
|
||||
problem(node, "Constant map literal.");
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSymbolLiteral(SymbolLiteral node) {
|
||||
enterTreeNode(node);
|
||||
if (afterConst) {
|
||||
problem(node, "Symbol literal.");
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitContinueSwitchStatement(ContinueSwitchStatement node) {
|
||||
enterTreeNode(node);
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (node.target == null) {
|
||||
problem(node, "No target.");
|
||||
|
@ -830,10 +1005,14 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
} else {
|
||||
SwitchStatement statement = node.target.parent as SwitchStatement;
|
||||
for (SwitchCase switchCase in statement.cases) {
|
||||
if (switchCase == node.target) return;
|
||||
if (switchCase == node.target) {
|
||||
exitTreeNode(node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
problem(node, "Switch case isn't child of parent.");
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -930,6 +1109,9 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
|
||||
@override
|
||||
void visitInterfaceType(InterfaceType node) {
|
||||
if (isNullType(node) && node.nullability != Nullability.nullable) {
|
||||
problem(localContext, "Found a not nullable Null type: ${node}");
|
||||
}
|
||||
node.visitChildren(this);
|
||||
if (node.typeArguments.length != node.classNode.typeParameters.length) {
|
||||
problem(
|
||||
|
@ -972,10 +1154,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
|
||||
@override
|
||||
void visitConstantExpression(ConstantExpression node) {
|
||||
enterTreeNode(node);
|
||||
bool oldInConstant = inConstant;
|
||||
inConstant = true;
|
||||
visitChildren(node);
|
||||
inConstant = oldInConstant;
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -990,6 +1174,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
|
||||
@override
|
||||
void visitTypedefTearOffConstant(TypedefTearOffConstant node) {
|
||||
_checkTypedefTearOff(node);
|
||||
declareTypeParameters(node.parameters);
|
||||
super.visitTypedefTearOffConstant(node);
|
||||
undeclareTypeParameters(node.parameters);
|
||||
|
@ -1038,6 +1223,349 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
|||
}
|
||||
super.visitInstanceSet(node);
|
||||
}
|
||||
|
||||
/// Invoked by all visit methods if the visited node is a [TreeNode].
|
||||
// TODO(johnniwinther): Merge this with enter/exitParent.
|
||||
void enterTreeNode(TreeNode node) {
|
||||
treeNodeStack.add(node);
|
||||
}
|
||||
|
||||
/// Invoked by all visit methods if the visited node is a [TreeNode].
|
||||
void exitTreeNode(TreeNode node) {
|
||||
if (treeNodeStack.isEmpty) {
|
||||
throw new StateError("Attempting to exit tree node '${node}' "
|
||||
"when the tree node stack is empty.");
|
||||
}
|
||||
if (!identical(treeNodeStack.last, node)) {
|
||||
throw new StateError("Attempting to exit tree node '${node}' "
|
||||
"when another node '${treeNodeStack.last}' is active.");
|
||||
}
|
||||
treeNodeStack.removeLast();
|
||||
}
|
||||
|
||||
TreeNode? getLastSeenTreeNode({bool withLocation = false}) {
|
||||
assert(treeNodeStack.isNotEmpty);
|
||||
for (int i = treeNodeStack.length - 1; i >= 0; --i) {
|
||||
TreeNode node = treeNodeStack[i];
|
||||
if (withLocation && !_hasLocation(node)) continue;
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
TreeNode? getSameLibraryLastSeenTreeNode({bool withLocation = false}) {
|
||||
if (treeNodeStack.isEmpty) return null;
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (currentLibrary == null || currentLibrary!.fileUri == null) return null;
|
||||
|
||||
for (int i = treeNodeStack.length - 1; i >= 0; --i) {
|
||||
TreeNode node = treeNodeStack[i];
|
||||
if (withLocation && !_hasLocation(node)) continue;
|
||||
Location? location = _getLocation(node);
|
||||
if (location != null && location.file == currentLibrary!.fileUri) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns the `TreeNode.location` while handling [RangeError]s caused by
|
||||
/// file offsets not within the range of the enclosing file.
|
||||
Location? _getLocation(TreeNode node, {bool allowInvalidLocation = false}) {
|
||||
try {
|
||||
return node.location;
|
||||
} on RangeError catch (e) {
|
||||
if (allowInvalidLocation ||
|
||||
target.verification.allowInvalidLocation(stage, node)) {
|
||||
return null;
|
||||
}
|
||||
problem(
|
||||
node,
|
||||
"Invalid location with target '${target.name}' on "
|
||||
"${node} (${node.runtimeType}): $e");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _hasLocation(TreeNode node) {
|
||||
Location? location = _getLocation(node);
|
||||
return location != null && node.fileOffset != TreeNode.noOffset;
|
||||
}
|
||||
|
||||
bool _isInSameLibrary(Library? library, TreeNode node) {
|
||||
if (library == null) return false;
|
||||
Location? location = _getLocation(node);
|
||||
if (location == null) return false;
|
||||
return library.fileUri == location.file;
|
||||
}
|
||||
|
||||
TreeNode? get localContext {
|
||||
TreeNode? result = getSameLibraryLastSeenTreeNode(withLocation: true);
|
||||
if (result == null &&
|
||||
currentClassOrExtensionOrMember != null &&
|
||||
_isInSameLibrary(currentLibrary, currentClassOrExtensionOrMember!)) {
|
||||
result = currentClassOrExtensionOrMember;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TreeNode? get remoteContext {
|
||||
TreeNode? result = getLastSeenTreeNode(withLocation: true);
|
||||
if (result != null && _isInSameLibrary(currentLibrary, result)) {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Uri checkLocation(TreeNode node, String? name, Uri fileUri) {
|
||||
if (name == null || name.contains("#")) {
|
||||
// TODO(ahe): Investigate if these checks can be enabled:
|
||||
// if (node.fileUri != null && node is! Library) {
|
||||
// problem(node, "A synthetic node shouldn't have a fileUri",
|
||||
// context: node);
|
||||
// }
|
||||
// if (node.fileOffset != -1) {
|
||||
// problem(node, "A synthetic node shouldn't have a fileOffset",
|
||||
// context: node);
|
||||
// }
|
||||
return fileUri;
|
||||
} else {
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (fileUri == null) {
|
||||
problem(node, "'$name' has no fileUri", context: node);
|
||||
return fileUri;
|
||||
}
|
||||
if (node.fileOffset == TreeNode.noOffset &&
|
||||
!target.verification.allowNoFileOffset(stage, node)) {
|
||||
problem(node, "'$name' has no fileOffset", context: node);
|
||||
}
|
||||
return fileUri;
|
||||
}
|
||||
}
|
||||
|
||||
void checkSuperInvocation(TreeNode node) {
|
||||
Member? containingMember = getContainingMember(node);
|
||||
if (containingMember == null) {
|
||||
problem(node, 'Super call outside of any member');
|
||||
} else {
|
||||
if (!containingMember.containsSuperCalls) {
|
||||
problem(
|
||||
node, 'Super call in a member lacking TransformerFlag.superCalls');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Member? getContainingMember(TreeNode? node) {
|
||||
while (node != null) {
|
||||
if (node is Member) return node;
|
||||
node = node.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void visitAsExpression(AsExpression node) {
|
||||
enterTreeNode(node);
|
||||
super.visitAsExpression(node);
|
||||
if (node.fileOffset == TreeNode.noOffset &&
|
||||
!target.verification.allowNoFileOffset(stage, node)) {
|
||||
TreeNode? parent = node.parent;
|
||||
while (parent != null) {
|
||||
if (parent.fileOffset != TreeNode.noOffset) break;
|
||||
parent = parent.parent;
|
||||
}
|
||||
problem(parent, "No offset for $node", context: node);
|
||||
}
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitExpressionStatement(ExpressionStatement node) {
|
||||
// Bypass verification of the [StaticGet] in [RedirectingFactoryBody] as
|
||||
// this is a static get without a getter.
|
||||
if (node is! RedirectingFactoryBody) {
|
||||
enterTreeNode(node);
|
||||
super.visitExpressionStatement(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
bool isNullType(DartType node) => node is NullType;
|
||||
|
||||
bool isObjectClass(Class c) {
|
||||
return c.name == "Object" &&
|
||||
c.enclosingLibrary.importUri.isScheme("dart") &&
|
||||
c.enclosingLibrary.importUri.path == "core";
|
||||
}
|
||||
|
||||
bool isTopType(DartType node) {
|
||||
return node is DynamicType ||
|
||||
node is VoidType ||
|
||||
node is InterfaceType &&
|
||||
isObjectClass(node.classNode) &&
|
||||
(node.nullability == Nullability.nullable ||
|
||||
node.nullability == Nullability.legacy) ||
|
||||
node is FutureOrType && isTopType(node.typeArgument);
|
||||
}
|
||||
|
||||
bool isFutureOrNull(DartType node) {
|
||||
return isNullType(node) ||
|
||||
node is FutureOrType && isFutureOrNull(node.typeArgument);
|
||||
}
|
||||
|
||||
@override
|
||||
void defaultDartType(DartType node) {
|
||||
final TreeNode? localContext = this.localContext;
|
||||
final TreeNode? remoteContext = this.remoteContext;
|
||||
|
||||
if (!KnownTypes.isKnown(node)) {
|
||||
problem(localContext, "Unexpected appearance of the unknown type.",
|
||||
origin: remoteContext);
|
||||
}
|
||||
|
||||
// TODO(johnniwinther): This check wasn't called from InterfaceType and
|
||||
// is currently very broken. Disabling for now.
|
||||
/*bool isTypeCast = localContext != null &&
|
||||
localContext is AsExpression &&
|
||||
localContext.isTypeError;
|
||||
// Don't check cases like foo(x as{TypeError} T). In cases where foo comes
|
||||
// from a library with a different opt-in status than the current library,
|
||||
// the check may not be necessary. For now, just skip all type-error casts.
|
||||
// TODO(cstefantsova): Implement a more precise analysis.
|
||||
bool isFromAnotherLibrary = remoteContext != null || isTypeCast;
|
||||
|
||||
// Checking for non-legacy types in opt-out libraries.
|
||||
{
|
||||
bool neverLegacy = isNullType(node) ||
|
||||
isFutureOrNull(node) ||
|
||||
isTopType(node) ||
|
||||
node is InvalidType ||
|
||||
node is NeverType ||
|
||||
node is BottomType;
|
||||
// TODO(cstefantsova): Consider checking types coming from other
|
||||
// libraries.
|
||||
bool expectedLegacy = !isFromAnotherLibrary &&
|
||||
!currentLibrary.isNonNullableByDefault &&
|
||||
!neverLegacy;
|
||||
if (expectedLegacy && node.nullability != Nullability.legacy) {
|
||||
problem(localContext,
|
||||
"Found a non-legacy type '${node}' in an opted-out library.",
|
||||
origin: remoteContext);
|
||||
}
|
||||
}
|
||||
|
||||
// Checking for legacy types in opt-in libraries.
|
||||
{
|
||||
Nullability nodeNullability =
|
||||
node is InvalidType ? Nullability.undetermined : node.nullability;
|
||||
// TODO(cstefantsova): Consider checking types coming from other
|
||||
// libraries.
|
||||
if (!isFromAnotherLibrary &&
|
||||
currentLibrary.isNonNullableByDefault &&
|
||||
nodeNullability == Nullability.legacy) {
|
||||
problem(localContext,
|
||||
"Found a legacy type '${node}' in an opted-in library.",
|
||||
origin: remoteContext);
|
||||
}
|
||||
}*/
|
||||
|
||||
super.defaultDartType(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSuperMethodInvocation(SuperMethodInvocation node) {
|
||||
enterTreeNode(node);
|
||||
checkSuperInvocation(node);
|
||||
super.visitSuperMethodInvocation(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSuperPropertyGet(SuperPropertyGet node) {
|
||||
enterTreeNode(node);
|
||||
checkSuperInvocation(node);
|
||||
super.visitSuperPropertyGet(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSuperPropertySet(SuperPropertySet node) {
|
||||
enterTreeNode(node);
|
||||
checkSuperInvocation(node);
|
||||
super.visitSuperPropertySet(node);
|
||||
exitTreeNode(node);
|
||||
}
|
||||
|
||||
void _checkConstructorTearOff(Node node, Member tearOffTarget) {
|
||||
if (tearOffTarget.enclosingLibrary.importUri.isScheme('dart')) {
|
||||
// Platform libraries are not compilation with test flags and might
|
||||
// contain tear-offs not expected when testing lowerings.
|
||||
return;
|
||||
}
|
||||
if (currentMember != null && isRedirectingFactoryField(currentMember!)) {
|
||||
// The encoding of the redirecting factory field uses
|
||||
// [ConstructorTearOffConstant] nodes also when lowerings are enabled.
|
||||
return;
|
||||
}
|
||||
if (tearOffTarget is Constructor &&
|
||||
target.isConstructorTearOffLoweringEnabled) {
|
||||
problem(
|
||||
node is TreeNode ? node : getLastSeenTreeNode(),
|
||||
'${node.runtimeType} nodes for generative constructors should be '
|
||||
'lowered for target "${target.name}".');
|
||||
}
|
||||
if (tearOffTarget is Procedure &&
|
||||
tearOffTarget.isFactory &&
|
||||
target.isFactoryTearOffLoweringEnabled) {
|
||||
problem(
|
||||
node is TreeNode ? node : getLastSeenTreeNode(),
|
||||
'${node.runtimeType} nodes for factory constructors should be '
|
||||
'lowered for target "${target.name}".');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitConstructorTearOff(ConstructorTearOff node) {
|
||||
_checkConstructorTearOff(node, node.target);
|
||||
super.visitConstructorTearOff(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitConstructorTearOffConstant(ConstructorTearOffConstant node) {
|
||||
_checkConstructorTearOff(node, node.target);
|
||||
super.visitConstructorTearOffConstant(node);
|
||||
}
|
||||
|
||||
void _checkTypedefTearOff(Node node) {
|
||||
if (target.isTypedefTearOffLoweringEnabled) {
|
||||
problem(
|
||||
node is TreeNode ? node : getLastSeenTreeNode(),
|
||||
'${node.runtimeType} nodes for typedefs should be '
|
||||
'lowered for target "${target.name}".');
|
||||
}
|
||||
}
|
||||
|
||||
void _checkRedirectingFactoryTearOff(Node node) {
|
||||
if (target.isRedirectingFactoryTearOffLoweringEnabled) {
|
||||
problem(
|
||||
node is TreeNode ? node : getLastSeenTreeNode(),
|
||||
'ConstructorTearOff nodes for redirecting factories should be '
|
||||
'lowered for target "${target.name}".');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) {
|
||||
_checkRedirectingFactoryTearOff(node);
|
||||
super.visitRedirectingFactoryTearOff(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRedirectingFactoryTearOffConstant(
|
||||
RedirectingFactoryTearOffConstant node) {
|
||||
_checkRedirectingFactoryTearOff(node);
|
||||
super.visitRedirectingFactoryTearOffConstant(node);
|
||||
}
|
||||
}
|
||||
|
||||
void verifyGetStaticType(TypeEnvironment env, Component component) {
|
||||
|
@ -1143,3 +1671,56 @@ void checkInitializers(Constructor constructor) {
|
|||
bool _isCompileTimeErrorEncoding(TreeNode? node) {
|
||||
return node is Let && node.variable.initializer is InvalidExpression;
|
||||
}
|
||||
|
||||
class KnownTypes implements DartTypeVisitor<bool> {
|
||||
static bool isKnown(DartType type) {
|
||||
return type.accept(const KnownTypes());
|
||||
}
|
||||
|
||||
const KnownTypes();
|
||||
|
||||
@override
|
||||
bool defaultDartType(DartType node) => false;
|
||||
|
||||
@override
|
||||
bool visitDynamicType(DynamicType node) => true;
|
||||
|
||||
@override
|
||||
bool visitExtensionType(ExtensionType node) => true;
|
||||
|
||||
@override
|
||||
bool visitFunctionType(FunctionType node) => true;
|
||||
|
||||
@override
|
||||
bool visitFutureOrType(FutureOrType node) => true;
|
||||
|
||||
@override
|
||||
bool visitInlineType(InlineType node) => true;
|
||||
|
||||
@override
|
||||
bool visitInterfaceType(InterfaceType node) => true;
|
||||
|
||||
@override
|
||||
bool visitIntersectionType(IntersectionType node) => true;
|
||||
|
||||
@override
|
||||
bool visitInvalidType(InvalidType node) => true;
|
||||
|
||||
@override
|
||||
bool visitNeverType(NeverType node) => true;
|
||||
|
||||
@override
|
||||
bool visitNullType(NullType node) => true;
|
||||
|
||||
@override
|
||||
bool visitRecordType(RecordType node) => true;
|
||||
|
||||
@override
|
||||
bool visitTypeParameterType(TypeParameterType node) => true;
|
||||
|
||||
@override
|
||||
bool visitTypedefType(TypedefType node) => true;
|
||||
|
||||
@override
|
||||
bool visitVoidType(VoidType node) => true;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:kernel/kernel.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/verifier.dart';
|
||||
|
||||
final String usage = '''
|
||||
|
@ -18,18 +19,22 @@ void main(List<String> args) {
|
|||
print(usage);
|
||||
exit(1);
|
||||
}
|
||||
var target = new NoneTarget(new TargetFlags());
|
||||
var component = loadComponentFromBinary(args[0]);
|
||||
var watch = new Stopwatch()..start();
|
||||
verifyComponent(component);
|
||||
verifyComponent(
|
||||
target, VerificationStage.afterModularTransformations, component);
|
||||
print('Cold: ${watch.elapsedMilliseconds} ms');
|
||||
const int warmUpTrials = 20;
|
||||
for (int i = 0; i < warmUpTrials; ++i) {
|
||||
verifyComponent(component);
|
||||
verifyComponent(
|
||||
target, VerificationStage.afterModularTransformations, component);
|
||||
}
|
||||
watch.reset();
|
||||
const int numberOfTrials = 100;
|
||||
for (int i = 0; i < numberOfTrials; ++i) {
|
||||
verifyComponent(component);
|
||||
verifyComponent(
|
||||
target, VerificationStage.afterModularTransformations, component);
|
||||
}
|
||||
double millisecondsPerRun = watch.elapsedMilliseconds / numberOfTrials;
|
||||
print('Hot: $millisecondsPerRun ms');
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/text/ast_to_text.dart';
|
||||
import 'package:kernel/verifier.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
const int dummyFileOffset = 4312;
|
||||
|
||||
/// Checks that the verifier correctly find errors in invalid components.
|
||||
///
|
||||
/// The frontend should never generate invalid components, so we have to test
|
||||
|
@ -81,7 +84,8 @@ void main() {
|
|||
name: 'Test',
|
||||
supertype: test.objectClass.asRawSupertype,
|
||||
fields: [field, field],
|
||||
fileUri: dummyUri));
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset);
|
||||
return field;
|
||||
},
|
||||
(Node? node) => "Member '$node' has been declared more than once.",
|
||||
|
@ -104,7 +108,8 @@ void main() {
|
|||
name: 'Test',
|
||||
supertype: test.objectClass.asRawSupertype,
|
||||
typeParameters: [parameter, parameter],
|
||||
fileUri: dummyUri));
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset);
|
||||
return parameter;
|
||||
},
|
||||
(Node? node) => "Type parameter '$node' redeclared.",
|
||||
|
@ -118,7 +123,8 @@ void main() {
|
|||
ProcedureKind.Method,
|
||||
new FunctionNode(new ReturnStatement(new NullLiteral()),
|
||||
typeParameters: [parameter, parameter]),
|
||||
fileUri: dummyUri));
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset);
|
||||
|
||||
return parameter;
|
||||
},
|
||||
|
@ -158,7 +164,8 @@ void main() {
|
|||
new FunctionNode(new ReturnStatement(new TypeLiteral(
|
||||
new TypeParameterType(node, Nullability.legacy)))),
|
||||
isStatic: true,
|
||||
fileUri: dummyUri));
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset);
|
||||
|
||||
return [node, test.enclosingClass];
|
||||
},
|
||||
|
@ -174,7 +181,8 @@ void main() {
|
|||
initializer:
|
||||
new TypeLiteral(new TypeParameterType(node, Nullability.legacy)),
|
||||
isStatic: true,
|
||||
fileUri: dummyUri));
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset);
|
||||
return [node, test.enclosingClass];
|
||||
},
|
||||
(Node? node, Node? parent) =>
|
||||
|
@ -192,15 +200,18 @@ void main() {
|
|||
supertype: test.objectClass.asRawSupertype,
|
||||
procedures: [
|
||||
new Procedure(new Name('generic'), ProcedureKind.Method, parent,
|
||||
fileUri: dummyUri),
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset,
|
||||
new Procedure(
|
||||
new Name('use'),
|
||||
ProcedureKind.Method,
|
||||
new FunctionNode(new ReturnStatement(new TypeLiteral(
|
||||
new TypeParameterType(parameter, Nullability.legacy)))),
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset
|
||||
],
|
||||
fileUri: dummyUri));
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset);
|
||||
|
||||
return [parameter, parent];
|
||||
},
|
||||
|
@ -233,7 +244,8 @@ void main() {
|
|||
negative1Test(
|
||||
'Dangling interface type',
|
||||
(TestHarness test) {
|
||||
Class orphan = new Class(name: 'Class', fileUri: dummyUri);
|
||||
Class orphan = new Class(name: 'Class', fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.addNode(
|
||||
new TypeLiteral(new InterfaceType(orphan, Nullability.legacy)));
|
||||
return orphan;
|
||||
|
@ -243,7 +255,8 @@ void main() {
|
|||
negative1Test(
|
||||
'Dangling field get',
|
||||
(TestHarness test) {
|
||||
Field orphan = new Field.mutable(new Name('foo'), fileUri: dummyUri);
|
||||
Field orphan = new Field.mutable(new Name('foo'), fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.addNode(new InstanceGet(
|
||||
InstanceAccessKind.Instance, new NullLiteral(), orphan.name,
|
||||
interfaceTarget: orphan, resultType: orphan.getterType));
|
||||
|
@ -268,7 +281,8 @@ void main() {
|
|||
(TestHarness test) {
|
||||
var procedure = new Procedure(
|
||||
new Name('bar'), ProcedureKind.Method, dummyFunctionNode,
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
procedure.function = new FunctionNode(new EmptyStatement());
|
||||
test.addNode(procedure);
|
||||
},
|
||||
|
@ -282,7 +296,8 @@ void main() {
|
|||
new FunctionNode(new EmptyStatement(),
|
||||
positionalParameters: [new VariableDeclaration('p')]),
|
||||
isStatic: true,
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingClass.addProcedure(method);
|
||||
test.addNode(
|
||||
StaticInvocation(method, new Arguments([new NullLiteral()])));
|
||||
|
@ -293,7 +308,8 @@ void main() {
|
|||
(TestHarness test) {
|
||||
var method = new Procedure(new Name('bar'), ProcedureKind.Method,
|
||||
new FunctionNode(new EmptyStatement()),
|
||||
isStatic: true, fileUri: dummyUri);
|
||||
isStatic: true, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingClass.addProcedure(method);
|
||||
test.addNode(
|
||||
StaticInvocation(method, new Arguments([new NullLiteral()])));
|
||||
|
@ -311,7 +327,8 @@ void main() {
|
|||
new FunctionNode(new EmptyStatement(),
|
||||
positionalParameters: [new VariableDeclaration('p')]),
|
||||
isStatic: true,
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingClass.addProcedure(method);
|
||||
test.addNode(StaticInvocation(method, new Arguments.empty()));
|
||||
return method;
|
||||
|
@ -323,7 +340,8 @@ void main() {
|
|||
(TestHarness test) {
|
||||
var method = new Procedure(new Name('bar'), ProcedureKind.Method,
|
||||
new FunctionNode(new EmptyStatement()),
|
||||
isStatic: true, fileUri: dummyUri);
|
||||
isStatic: true, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingClass.addProcedure(method);
|
||||
test.addNode(StaticInvocation(
|
||||
method,
|
||||
|
@ -343,7 +361,8 @@ void main() {
|
|||
new FunctionNode(new EmptyStatement(),
|
||||
typeParameters: [test.makeTypeParameter()]),
|
||||
isStatic: true,
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingClass.addProcedure(method);
|
||||
test.addNode(StaticInvocation(method, new Arguments.empty()));
|
||||
return method;
|
||||
|
@ -355,7 +374,8 @@ void main() {
|
|||
'ConstructorInvocation with missing type argument',
|
||||
(TestHarness test) {
|
||||
var constructor = new Constructor(new FunctionNode(new EmptyStatement()),
|
||||
name: new Name('foo'), fileUri: dummyUri);
|
||||
name: new Name('foo'), fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingClass.addConstructor(constructor);
|
||||
test.addNode(ConstructorInvocation(constructor, new Arguments.empty()));
|
||||
return constructor;
|
||||
|
@ -371,7 +391,8 @@ void main() {
|
|||
'Foo',
|
||||
new FunctionType(
|
||||
[test.otherLegacyRawType], const VoidType(), Nullability.legacy),
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.addNode(typedef_);
|
||||
},
|
||||
);
|
||||
|
@ -382,15 +403,18 @@ void main() {
|
|||
'Foo',
|
||||
new InterfaceType(
|
||||
test.otherClass, Nullability.legacy, [const DynamicType()]),
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.addNode(typedef_);
|
||||
},
|
||||
);
|
||||
positiveTest(
|
||||
'Valid typedefs Foo = Bar, Bar = C',
|
||||
(TestHarness test) {
|
||||
var foo = new Typedef('Foo', null, fileUri: dummyUri);
|
||||
var bar = new Typedef('Bar', null, fileUri: dummyUri);
|
||||
var foo = new Typedef('Foo', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
var bar = new Typedef('Bar', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
foo.type = new TypedefType(bar, Nullability.legacy);
|
||||
bar.type = test.otherLegacyRawType;
|
||||
test.enclosingLibrary.addTypedef(foo);
|
||||
|
@ -400,8 +424,10 @@ void main() {
|
|||
positiveTest(
|
||||
'Valid typedefs Foo = C<Bar>, Bar = C',
|
||||
(TestHarness test) {
|
||||
var foo = new Typedef('Foo', null, fileUri: dummyUri);
|
||||
var bar = new Typedef('Bar', null, fileUri: dummyUri);
|
||||
var foo = new Typedef('Foo', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
var bar = new Typedef('Bar', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
foo.type = new InterfaceType(test.otherClass, Nullability.legacy,
|
||||
[new TypedefType(bar, Nullability.legacy)]);
|
||||
bar.type = test.otherLegacyRawType;
|
||||
|
@ -416,11 +442,13 @@ void main() {
|
|||
'Foo',
|
||||
new FunctionType(
|
||||
[test.otherLegacyRawType], const VoidType(), Nullability.legacy),
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
var field = new Field.mutable(new Name('field'),
|
||||
type: new TypedefType(typedef_, Nullability.legacy),
|
||||
isStatic: true,
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingLibrary.addTypedef(typedef_);
|
||||
test.enclosingLibrary.addField(field);
|
||||
},
|
||||
|
@ -428,7 +456,8 @@ void main() {
|
|||
negative1Test(
|
||||
'Invalid typedef Foo = Foo',
|
||||
(TestHarness test) {
|
||||
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri);
|
||||
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
typedef_.type = new TypedefType(typedef_, Nullability.legacy);
|
||||
test.addNode(typedef_);
|
||||
return typedef_;
|
||||
|
@ -438,7 +467,8 @@ void main() {
|
|||
negative1Test(
|
||||
'Invalid typedef Foo = `(Foo) => void`',
|
||||
(TestHarness test) {
|
||||
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri);
|
||||
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
typedef_.type = new FunctionType(
|
||||
[new TypedefType(typedef_, Nullability.legacy)],
|
||||
const VoidType(),
|
||||
|
@ -451,7 +481,8 @@ void main() {
|
|||
negative1Test(
|
||||
'Invalid typedef Foo = `() => Foo`',
|
||||
(TestHarness test) {
|
||||
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri);
|
||||
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
typedef_.type = new FunctionType([],
|
||||
new TypedefType(typedef_, Nullability.legacy), Nullability.legacy);
|
||||
test.addNode(typedef_);
|
||||
|
@ -462,7 +493,8 @@ void main() {
|
|||
negative1Test(
|
||||
'Invalid typedef Foo = C<Foo>',
|
||||
(TestHarness test) {
|
||||
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri);
|
||||
var typedef_ = new Typedef('Foo', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
typedef_.type = new InterfaceType(test.otherClass, Nullability.legacy,
|
||||
[new TypedefType(typedef_, Nullability.legacy)]);
|
||||
test.addNode(typedef_);
|
||||
|
@ -473,8 +505,10 @@ void main() {
|
|||
negative1Test(
|
||||
'Invalid typedefs Foo = Bar, Bar = Foo',
|
||||
(TestHarness test) {
|
||||
var foo = new Typedef('Foo', null, fileUri: dummyUri);
|
||||
var bar = new Typedef('Bar', null, fileUri: dummyUri);
|
||||
var foo = new Typedef('Foo', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
var bar = new Typedef('Bar', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
foo.type = new TypedefType(bar, Nullability.legacy);
|
||||
bar.type = new TypedefType(foo, Nullability.legacy);
|
||||
test.enclosingLibrary.addTypedef(foo);
|
||||
|
@ -486,8 +520,10 @@ void main() {
|
|||
negative1Test(
|
||||
'Invalid typedefs Foo = Bar, Bar = C<Foo>',
|
||||
(TestHarness test) {
|
||||
var foo = new Typedef('Foo', null, fileUri: dummyUri);
|
||||
var bar = new Typedef('Bar', null, fileUri: dummyUri);
|
||||
var foo = new Typedef('Foo', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
var bar = new Typedef('Bar', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
foo.type = new TypedefType(bar, Nullability.legacy);
|
||||
bar.type = new InterfaceType(test.otherClass, Nullability.legacy,
|
||||
[new TypedefType(foo, Nullability.legacy)]);
|
||||
|
@ -500,8 +536,10 @@ void main() {
|
|||
negative1Test(
|
||||
'Invalid typedefs Foo = C<Bar>, Bar = C<Foo>',
|
||||
(TestHarness test) {
|
||||
var foo = new Typedef('Foo', null, fileUri: dummyUri);
|
||||
var bar = new Typedef('Bar', null, fileUri: dummyUri);
|
||||
var foo = new Typedef('Foo', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
var bar = new Typedef('Bar', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
foo.type = new InterfaceType(test.otherClass, Nullability.legacy,
|
||||
[new TypedefType(bar, Nullability.legacy)]);
|
||||
bar.type = new InterfaceType(test.otherClass, Nullability.legacy,
|
||||
|
@ -515,12 +553,14 @@ void main() {
|
|||
positiveTest(
|
||||
'Valid long typedefs C20 = C19 = ... = C1 = C0 = dynamic',
|
||||
(TestHarness test) {
|
||||
var typedef_ = new Typedef('C0', const DynamicType(), fileUri: dummyUri);
|
||||
var typedef_ = new Typedef('C0', const DynamicType(), fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingLibrary.addTypedef(typedef_);
|
||||
for (int i = 1; i < 20; ++i) {
|
||||
typedef_ = new Typedef(
|
||||
'C$i', new TypedefType(typedef_, Nullability.legacy),
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingLibrary.addTypedef(typedef_);
|
||||
}
|
||||
},
|
||||
|
@ -528,14 +568,16 @@ void main() {
|
|||
negative1Test(
|
||||
'Invalid long typedefs C20 = C19 = ... = C1 = C0 = C20',
|
||||
(TestHarness test) {
|
||||
Typedef firstTypedef = new Typedef('C0', null, fileUri: dummyUri);
|
||||
Typedef firstTypedef = new Typedef('C0', null, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
Typedef typedef_ = firstTypedef;
|
||||
test.enclosingLibrary.addTypedef(typedef_);
|
||||
var first = typedef_;
|
||||
for (int i = 1; i < 20; ++i) {
|
||||
typedef_ = new Typedef(
|
||||
'C$i', new TypedefType(typedef_, Nullability.legacy),
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingLibrary.addTypedef(typedef_);
|
||||
}
|
||||
first.type = new TypedefType(typedef_, Nullability.legacy);
|
||||
|
@ -552,7 +594,8 @@ void main() {
|
|||
new InterfaceType(test.otherClass, Nullability.legacy,
|
||||
[new TypeParameterType(param, Nullability.legacy)]),
|
||||
typeParameters: [param],
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.addNode(foo);
|
||||
},
|
||||
);
|
||||
|
@ -567,7 +610,8 @@ void main() {
|
|||
new InterfaceType(test.otherClass, Nullability.legacy,
|
||||
[new TypeParameterType(param, Nullability.legacy)]),
|
||||
typeParameters: [param],
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.addNode(foo);
|
||||
},
|
||||
);
|
||||
|
@ -576,7 +620,8 @@ void main() {
|
|||
(TestHarness test) {
|
||||
var fooParam = test.makeTypeParameter('T');
|
||||
var foo = new Typedef('Foo', const DynamicType(),
|
||||
typeParameters: [fooParam], fileUri: dummyUri);
|
||||
typeParameters: [fooParam], fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
var barParam = new TypeParameter('T', null);
|
||||
barParam.bound = new TypedefType(foo, Nullability.legacy,
|
||||
[new TypeParameterType(barParam, Nullability.legacy)]);
|
||||
|
@ -585,7 +630,8 @@ void main() {
|
|||
new InterfaceType(test.otherClass, Nullability.legacy,
|
||||
[new TypeParameterType(barParam, Nullability.legacy)]),
|
||||
typeParameters: [barParam],
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingLibrary.addTypedef(foo);
|
||||
test.enclosingLibrary.addTypedef(bar);
|
||||
},
|
||||
|
@ -595,7 +641,8 @@ void main() {
|
|||
(TestHarness test) {
|
||||
var fooParam = test.makeTypeParameter('T');
|
||||
var foo = new Typedef('Foo', const DynamicType(),
|
||||
typeParameters: [fooParam], fileUri: dummyUri);
|
||||
typeParameters: [fooParam], fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
var barParam = new TypeParameter('T', null);
|
||||
barParam.bound = new TypedefType(foo, Nullability.legacy,
|
||||
[new TypeParameterType(barParam, Nullability.legacy)]);
|
||||
|
@ -622,7 +669,8 @@ void main() {
|
|||
new InterfaceType(test.otherClass, Nullability.legacy,
|
||||
[new TypeParameterType(param, Nullability.legacy)]),
|
||||
typeParameters: [param],
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
param.bound =
|
||||
new TypedefType(foo, Nullability.legacy, [const DynamicType()]);
|
||||
test.addNode(foo);
|
||||
|
@ -635,10 +683,12 @@ void main() {
|
|||
(TestHarness test) {
|
||||
var param = test.makeTypeParameter('T');
|
||||
var foo = new Typedef('Foo', test.otherLegacyRawType,
|
||||
typeParameters: [param], fileUri: dummyUri);
|
||||
typeParameters: [param], fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
var typedefType = new TypedefType(foo, Nullability.legacy, []);
|
||||
var field = new Field.mutable(new Name('field'),
|
||||
type: typedefType, isStatic: true, fileUri: dummyUri);
|
||||
type: typedefType, isStatic: true, fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingLibrary.addTypedef(foo);
|
||||
test.enclosingLibrary.addField(field);
|
||||
return typedefType;
|
||||
|
@ -651,11 +701,13 @@ void main() {
|
|||
'Dangling typedef reference',
|
||||
(TestHarness test) {
|
||||
var foo = new Typedef('Foo', test.otherLegacyRawType,
|
||||
typeParameters: [], fileUri: dummyUri);
|
||||
typeParameters: [], fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
var field = new Field.mutable(new Name('field'),
|
||||
type: new TypedefType(foo, Nullability.legacy, []),
|
||||
isStatic: true,
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingLibrary.addField(field);
|
||||
return foo;
|
||||
},
|
||||
|
@ -664,17 +716,63 @@ void main() {
|
|||
negative1Test(
|
||||
'Non-static top-level field',
|
||||
(TestHarness test) {
|
||||
var field = new Field.mutable(new Name('field'), fileUri: dummyUri);
|
||||
var field = new Field.mutable(new Name('field'), fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
test.enclosingLibrary.addField(field);
|
||||
return null;
|
||||
},
|
||||
(Node? node) => "The top-level field 'field' should be static",
|
||||
);
|
||||
positiveTest('No library file offset', (TestHarness test) {
|
||||
var library = new Library(dummyUri, fileUri: dummyUri)
|
||||
..parent = test.component;
|
||||
test.component.libraries.add(library);
|
||||
return null;
|
||||
});
|
||||
negative1Test(
|
||||
'Class file offset',
|
||||
(TestHarness test) {
|
||||
var cls = new Class(name: 'Class', fileUri: dummyUri);
|
||||
test.enclosingLibrary.addClass(cls);
|
||||
return null;
|
||||
},
|
||||
(Node? node) => "'Class' has no fileOffset",
|
||||
);
|
||||
negative1Test(
|
||||
'Extension file offset',
|
||||
(TestHarness test) {
|
||||
var extension = new Extension(name: 'Extension', fileUri: dummyUri);
|
||||
test.enclosingLibrary.addExtension(extension);
|
||||
return null;
|
||||
},
|
||||
(Node? node) => "'Extension' has no fileOffset",
|
||||
);
|
||||
negative1Test(
|
||||
'Procedure file offset',
|
||||
(TestHarness test) {
|
||||
var method = new Procedure(
|
||||
new Name('method'), ProcedureKind.Method, new FunctionNode(null),
|
||||
fileUri: dummyUri);
|
||||
test.enclosingClass.addProcedure(method);
|
||||
return null;
|
||||
},
|
||||
(Node? node) => "'method' has no fileOffset",
|
||||
);
|
||||
negative1Test(
|
||||
'Field file offset',
|
||||
(TestHarness test) {
|
||||
var field = new Field.mutable(new Name('field'), fileUri: dummyUri);
|
||||
test.enclosingClass.addField(field);
|
||||
return null;
|
||||
},
|
||||
(Node? node) => "'field' has no fileOffset",
|
||||
);
|
||||
}
|
||||
|
||||
void checkHasError(Component component, Matcher matcher) {
|
||||
void checkHasError(Target target, Component component, Matcher matcher) {
|
||||
try {
|
||||
verifyComponent(component);
|
||||
verifyComponent(
|
||||
target, VerificationStage.afterModularTransformations, component);
|
||||
} on VerificationError catch (e) {
|
||||
expect(e.details, matcher);
|
||||
return;
|
||||
|
@ -683,6 +781,7 @@ void checkHasError(Component component, Matcher matcher) {
|
|||
}
|
||||
|
||||
class TestHarness {
|
||||
late Target target;
|
||||
late Component component;
|
||||
late Class objectClass;
|
||||
late Library stubLibrary;
|
||||
|
@ -766,12 +865,14 @@ class TestHarness {
|
|||
}
|
||||
|
||||
void setupComponent() {
|
||||
target = new NoneTarget(new TargetFlags());
|
||||
component = new Component();
|
||||
Uri dartCoreUri = Uri.parse('dart:core');
|
||||
stubLibrary = new Library(dartCoreUri, fileUri: dartCoreUri);
|
||||
component.libraries.add(stubLibrary..parent = component);
|
||||
stubLibrary.name = 'dart.core';
|
||||
objectClass = new Class(name: 'Object', fileUri: dartCoreUri);
|
||||
objectClass = new Class(name: 'Object', fileUri: dartCoreUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
objectLegacyRawType =
|
||||
new InterfaceType(objectClass, Nullability.legacy, const <DartType>[]);
|
||||
stubLibrary.addClass(objectClass);
|
||||
|
@ -784,19 +885,22 @@ class TestHarness {
|
|||
name: 'TestClass',
|
||||
typeParameters: [classTypeParameter],
|
||||
supertype: objectClass.asRawSupertype,
|
||||
fileUri: testUri);
|
||||
fileUri: testUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
enclosingLegacyRawType = new InterfaceType(enclosingClass,
|
||||
Nullability.legacy, const <DartType>[const DynamicType()]);
|
||||
enclosingLibrary.addClass(enclosingClass);
|
||||
enclosingMember = new Procedure(new Name('test'), ProcedureKind.Method,
|
||||
new FunctionNode(new EmptyStatement()),
|
||||
fileUri: dummyUri);
|
||||
fileUri: dummyUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
enclosingClass.addProcedure(enclosingMember);
|
||||
otherClass = new Class(
|
||||
name: 'OtherClass',
|
||||
typeParameters: [makeTypeParameter('OtherT')],
|
||||
supertype: objectClass.asRawSupertype,
|
||||
fileUri: testUri);
|
||||
fileUri: testUri)
|
||||
..fileOffset = dummyFileOffset;
|
||||
otherLegacyRawType = new InterfaceType(
|
||||
otherClass, Nullability.legacy, const <DartType>[const DynamicType()]);
|
||||
enclosingLibrary.addClass(otherClass);
|
||||
|
@ -814,7 +918,7 @@ void negative1Test(String name, Node? Function(TestHarness test) nodeProvider,
|
|||
if (matcherResult is String) {
|
||||
matcherResult = equals(matcherResult);
|
||||
}
|
||||
checkHasError(testHarness.component, matcherResult);
|
||||
checkHasError(testHarness.target, testHarness.component, matcherResult);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -833,7 +937,7 @@ void negative2Test(
|
|||
if (matcherResult is String) {
|
||||
matcherResult = equals(matcherResult);
|
||||
}
|
||||
checkHasError(testHarness.component, matcherResult);
|
||||
checkHasError(testHarness.target, testHarness.component, matcherResult);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -848,7 +952,7 @@ void simpleNegativeTest(String name, dynamic matcher,
|
|||
if (matcher is String) {
|
||||
matcher = equals(matcher);
|
||||
}
|
||||
checkHasError(testHarness.component, matcher);
|
||||
checkHasError(testHarness.target, testHarness.component, matcher);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -859,7 +963,8 @@ void positiveTest(String name, void makeTestCase(TestHarness test)) {
|
|||
() {
|
||||
var test = new TestHarness();
|
||||
makeTestCase(test);
|
||||
verifyComponent(test.component);
|
||||
verifyComponent(test.target,
|
||||
VerificationStage.afterModularTransformations, test.component);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/transformations/flags.dart';
|
||||
|
||||
/// Transformer/visitor for toString
|
||||
transformComponent(Component component, List<String> packageUris) {
|
||||
|
@ -84,6 +85,7 @@ class ToStringVisitor extends RecursiveVisitor {
|
|||
return findSuperMethod(cls.superclass!);
|
||||
}
|
||||
|
||||
node.transformerFlags |= TransformerFlag.superCalls;
|
||||
node.function.body!.replaceWith(
|
||||
ReturnStatement(
|
||||
SuperMethodInvocation(
|
||||
|
|
|
@ -22,8 +22,6 @@ import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
|
|||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:vm/target/vm.dart' show VmTarget;
|
||||
|
||||
/// Environment define to update expectation files on failures.
|
||||
const kUpdateExpectations = 'updateExpectations';
|
||||
|
||||
|
@ -31,14 +29,13 @@ const kUpdateExpectations = 'updateExpectations';
|
|||
const kDumpActualResult = 'dump.actual.result';
|
||||
|
||||
Future<Component> compileTestCaseToKernelProgram(Uri sourceUri,
|
||||
{Target? target,
|
||||
{required Target target,
|
||||
List<String>? experimentalFlags,
|
||||
Map<String, String>? environmentDefines,
|
||||
Uri? packagesFileUri,
|
||||
List<Uri>? linkedDependencies}) async {
|
||||
Directory? tempDirectory;
|
||||
try {
|
||||
target ??= new VmTarget(new TargetFlags());
|
||||
final platformFileName = (target is WasmTarget)
|
||||
? 'dart2wasm_platform.dill'
|
||||
: 'vm_platform_strong.dill';
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@ import 'dart:io';
|
|||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/kernel.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/verifier.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm/target/vm.dart';
|
||||
import 'package:vm/transformations/to_string_transformer.dart'
|
||||
show transformComponent;
|
||||
|
||||
|
@ -16,16 +18,19 @@ import '../common_test_utils.dart';
|
|||
final Uri pkgVmUri = Platform.script.resolve('../..');
|
||||
|
||||
runTestCase(List<String> packageUris, String expectationName) async {
|
||||
final target = new VmTarget(new TargetFlags());
|
||||
final testCasesUri =
|
||||
pkgVmUri.resolve('testcases/transformations/to_string_transformer/');
|
||||
final packagesFileUri =
|
||||
testCasesUri.resolve('.dart_tool/package_config.json');
|
||||
Component component = await compileTestCaseToKernelProgram(
|
||||
Uri.parse('package:to_string_transformer_test/main.dart'),
|
||||
target: target,
|
||||
packagesFileUri: packagesFileUri);
|
||||
|
||||
transformComponent(component, packageUris);
|
||||
verifyComponent(component);
|
||||
verifyComponent(
|
||||
target, VerificationStage.afterModularTransformations, component);
|
||||
|
||||
final actual = kernelLibraryToString(component.mainMethod!.enclosingLibrary);
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ class TestOptions {
|
|||
runTestCase(Uri source, List<String>? experimentalFlags) async {
|
||||
final Target target = new VmTarget(new TargetFlags());
|
||||
final Component component = await compileTestCaseToKernelProgram(source,
|
||||
experimentalFlags: experimentalFlags);
|
||||
target: target, experimentalFlags: experimentalFlags);
|
||||
final Library library = component.mainMethod!.enclosingLibrary;
|
||||
final CoreTypes coreTypes = new CoreTypes(component);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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}';
|
||||
|
|
Loading…
Reference in a new issue