blob: 0c41ecfab1d44201bb8e790f50d3feb42b089dcd [file] [log] [blame]
// Copyright (c) 2016, 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.
library fasta.kernel_target;
import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/core_types.dart';
import 'package:kernel/reference_from_index.dart' show IndexedClass;
import 'package:kernel/target/changed_structure_notifier.dart'
show ChangedStructureNotifier;
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:package_config/package_config.dart' hide LanguageVersion;
import '../../api_prototype/experimental_flags.dart' show ExperimentalFlag;
import '../../api_prototype/file_system.dart' show FileSystem;
import '../../base/nnbd_mode.dart';
import '../../base/processed_options.dart' show ProcessedOptions;
import '../builder/builder.dart';
import '../builder/class_builder.dart';
import '../builder/constructor_builder.dart';
import '../builder/dynamic_type_declaration_builder.dart';
import '../builder/field_builder.dart';
import '../builder/invalid_type_declaration_builder.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
import '../builder/named_type_builder.dart';
import '../builder/never_type_declaration_builder.dart';
import '../builder/nullability_builder.dart';
import '../builder/procedure_builder.dart';
import '../builder/type_alias_builder.dart';
import '../builder/type_builder.dart';
import '../builder/type_declaration_builder.dart';
import '../builder/type_variable_builder.dart';
import '../builder/void_type_declaration_builder.dart';
import '../compiler_context.dart' show CompilerContext;
import '../crash.dart' show withCrashReporting;
import '../dill/dill_target.dart' show DillTarget;
import '../kernel/constructor_tearoff_lowering.dart';
import '../loader.dart' show Loader;
import '../messages.dart'
show
FormattedMessage,
LocatedMessage,
Message,
messageConstConstructorLateFinalFieldCause,
messageConstConstructorLateFinalFieldError,
messageConstConstructorNonFinalField,
messageConstConstructorNonFinalFieldCause,
messageConstConstructorRedirectionToNonConst,
noLength,
templateFieldNonNullableNotInitializedByConstructorError,
templateFieldNonNullableWithoutInitializerError,
templateFinalFieldNotInitialized,
templateFinalFieldNotInitializedByConstructor,
templateMissingImplementationCause,
templateSuperclassHasNoDefaultConstructor;
import '../problems.dart' show unhandled;
import '../scope.dart' show AmbiguousBuilder;
import '../source/name_scheme.dart';
import '../source/source_class_builder.dart' show SourceClassBuilder;
import '../source/source_constructor_builder.dart';
import '../source/source_field_builder.dart';
import '../source/source_library_builder.dart' show SourceLibraryBuilder;
import '../source/source_loader.dart' show SourceLoader;
import '../target_implementation.dart' show TargetImplementation;
import '../ticker.dart' show Ticker;
import '../type_inference/type_schema.dart';
import '../uri_translator.dart' show UriTranslator;
import 'benchmarker.dart' show BenchmarkPhases, Benchmarker;
import 'constant_evaluator.dart' as constants
show
EvaluationMode,
transformLibraries,
transformProcedure,
ConstantCoverage,
ConstantEvaluationData;
import 'kernel_constants.dart' show KernelConstantErrorReporter;
import 'kernel_helper.dart';
import 'macro.dart';
import 'verifier.dart' show verifyComponent, verifyGetStaticType;
class KernelTarget extends TargetImplementation {
final Ticker ticker;
/// The [FileSystem] which should be used to access files.
final FileSystem fileSystem;
/// Whether comments should be scanned and parsed.
final bool includeComments;
final DillTarget dillTarget;
late final SourceLoader loader;
Component? component;
// 'dynamic' is always nullable.
// TODO(johnniwinther): Why isn't this using a FixedTypeBuilder?
final TypeBuilder dynamicType = new NamedTypeBuilder(
"dynamic",
const NullabilityBuilder.inherent(),
/* arguments = */ null,
/* fileUri = */ null,
/* charOffset = */ null,
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
final NamedTypeBuilder objectType = new NamedTypeBuilder(
"Object",
const NullabilityBuilder.omitted(),
/* arguments = */ null,
/* fileUri = */ null,
/* charOffset = */ null,
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
// Null is always nullable.
// TODO(johnniwinther): This could (maybe) use a FixedTypeBuilder when we
// have NullType?
final TypeBuilder nullType = new NamedTypeBuilder(
"Null",
const NullabilityBuilder.inherent(),
/* arguments = */ null,
/* fileUri = */ null,
/* charOffset = */ null,
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
// TODO(johnniwinther): Why isn't this using a FixedTypeBuilder?
final TypeBuilder bottomType = new NamedTypeBuilder(
"Never",
const NullabilityBuilder.omitted(),
/* arguments = */ null,
/* fileUri = */ null,
/* charOffset = */ null,
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
final NamedTypeBuilder enumType = new NamedTypeBuilder(
"Enum",
const NullabilityBuilder.omitted(),
/* arguments = */ null,
/* fileUri = */ null,
/* charOffset = */ null,
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
final NamedTypeBuilder underscoreEnumType = new NamedTypeBuilder(
"_Enum",
const NullabilityBuilder.omitted(),
/* arguments = */ null,
/* fileUri = */ null,
/* charOffset = */ null,
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
final bool excludeSource = !CompilerContext.current.options.embedSourceText;
final Map<String, String>? environmentDefines =
CompilerContext.current.options.environmentDefines;
final bool errorOnUnevaluatedConstant =
CompilerContext.current.options.errorOnUnevaluatedConstant;
final List<SynthesizedFunctionNode> synthesizedFunctionNodes =
<SynthesizedFunctionNode>[];
final UriTranslator uriTranslator;
@override
final Target backendTarget;
@override
final CompilerContext context = CompilerContext.current;
/// Shared with [CompilerContext].
final Map<Uri, Source> uriToSource = CompilerContext.current.uriToSource;
MemberBuilder? _cachedAbstractClassInstantiationError;
MemberBuilder? _cachedCompileTimeError;
MemberBuilder? _cachedDuplicatedFieldInitializerError;
MemberBuilder? _cachedNativeAnnotation;
final ProcessedOptions _options;
final Benchmarker? benchmarker;
KernelTarget(this.fileSystem, this.includeComments, DillTarget dillTarget,
this.uriTranslator)
: dillTarget = dillTarget,
backendTarget = dillTarget.backendTarget,
_options = CompilerContext.current.options,
ticker = dillTarget.ticker,
benchmarker = dillTarget.benchmarker {
loader = createLoader();
}
bool isExperimentEnabledInLibrary(ExperimentalFlag flag, Uri importUri) {
return _options.isExperimentEnabledInLibrary(flag, importUri);
}
Version getExperimentEnabledVersionInLibrary(
ExperimentalFlag flag, Uri importUri) {
return _options.getExperimentEnabledVersionInLibrary(flag, importUri);
}
bool isExperimentEnabledInLibraryByVersion(
ExperimentalFlag flag, Uri importUri, Version version) {
return _options.isExperimentEnabledInLibraryByVersion(
flag, importUri, version);
}
/// Returns `true` if the [flag] is enabled by default.
bool isExperimentEnabledByDefault(ExperimentalFlag flag) {
return _options.isExperimentEnabledByDefault(flag);
}
/// Returns `true` if the [flag] is enabled globally.
///
/// This is `true` either if the [flag] is passed through an explicit
/// `--enable-experiment` option or if the [flag] is expired and on by
/// default.
bool isExperimentEnabledGlobally(ExperimentalFlag flag) {
return _options.isExperimentEnabledGlobally(flag);
}
Uri? translateUri(Uri uri) => uriTranslator.translate(uri);
/// Returns a reference to the constructor of
/// `AbstractClassInstantiationError` error. The constructor is expected to
/// accept a single argument of type String, which is the name of the
/// abstract class.
// TODO: Use some other error before `AbstractClassInstantiationError`
// is removed.
MemberBuilder getAbstractClassInstantiationError(Loader loader) {
return _cachedAbstractClassInstantiationError ??=
loader.coreLibrary.getConstructor("AbstractClassInstantiationError");
}
/// Returns a reference to the constructor used for creating a compile-time
/// error. The constructor is expected to accept a single argument of type
/// String, which is the compile-time error message.
MemberBuilder getCompileTimeError(Loader loader) {
return _cachedCompileTimeError ??= loader.coreLibrary
.getConstructor("_CompileTimeError", bypassLibraryPrivacy: true);
}
/// Returns a reference to the constructor used for creating a runtime error
/// when a final field is initialized twice. The constructor is expected to
/// accept a single argument which is the name of the field.
MemberBuilder getDuplicatedFieldInitializerError(Loader loader) {
return _cachedDuplicatedFieldInitializerError ??= loader.coreLibrary
.getConstructor("_DuplicatedFieldInitializerError",
bypassLibraryPrivacy: true);
}
/// Returns a reference to the constructor used for creating `native`
/// annotations. The constructor is expected to accept a single argument of
/// type String, which is the name of the native method.
MemberBuilder getNativeAnnotation(SourceLoader loader) {
if (_cachedNativeAnnotation != null) return _cachedNativeAnnotation!;
LibraryBuilder internal = loader.read(Uri.parse("dart:_internal"), -1,
accessor: loader.coreLibrary);
return _cachedNativeAnnotation = internal.getConstructor("ExternalName");
}
void loadExtraRequiredLibraries(SourceLoader loader) {
for (String uri in backendTarget.extraRequiredLibraries) {
loader.read(Uri.parse(uri), 0, accessor: loader.coreLibrary);
}
if (context.compilingPlatform) {
for (String uri in backendTarget.extraRequiredLibrariesPlatform) {
loader.read(Uri.parse(uri), 0, accessor: loader.coreLibrary);
}
}
}
FormattedMessage createFormattedMessage(
Message message,
int charOffset,
int length,
Uri? fileUri,
List<LocatedMessage>? messageContext,
Severity severity,
{List<Uri>? involvedFiles}) {
ProcessedOptions processedOptions = context.options;
return processedOptions.format(
fileUri != null
? message.withLocation(fileUri, charOffset, length)
: message.withoutLocation(),
severity,
messageContext,
involvedFiles: involvedFiles);
}
String get currentSdkVersionString {
return CompilerContext.current.options.currentSdkVersion;
}
Version? _currentSdkVersion;
Version get currentSdkVersion {
if (_currentSdkVersion == null) {
_parseCurrentSdkVersion();
}
return _currentSdkVersion!;
}
void _parseCurrentSdkVersion() {
bool good = false;
// ignore: unnecessary_null_comparison
if (currentSdkVersionString != null) {
List<String> dotSeparatedParts = currentSdkVersionString.split(".");
if (dotSeparatedParts.length >= 2) {
_currentSdkVersion = new Version(int.tryParse(dotSeparatedParts[0])!,
int.tryParse(dotSeparatedParts[1])!);
good = true;
}
}
if (!good) {
throw new StateError(
"Unparsable sdk version given: $currentSdkVersionString");
}
}
SourceLoader createLoader() =>
new SourceLoader(fileSystem, includeComments, this);
void addSourceInformation(
Uri importUri, Uri fileUri, List<int> lineStarts, List<int> sourceCode) {
uriToSource[fileUri] =
new Source(lineStarts, sourceCode, importUri, fileUri);
}
/// Return list of same size as input with possibly translated uris.
List<Uri> setEntryPoints(List<Uri> entryPoints) {
List<Uri> result = <Uri>[];
for (Uri entryPoint in entryPoints) {
Uri translatedEntryPoint =
getEntryPointUri(entryPoint, issueProblem: true);
result.add(translatedEntryPoint);
loader.readAsEntryPoint(translatedEntryPoint,
fileUri: translatedEntryPoint != entryPoint ? entryPoint : null);
}
return result;
}
/// Return list of same size as input with possibly translated uris.
Uri getEntryPointUri(Uri entryPoint, {bool issueProblem: false}) {
String scheme = entryPoint.scheme;
switch (scheme) {
case "package":
case "dart":
case "data":
break;
default:
// Attempt to reverse-lookup [entryPoint] in package config.
String asString = "$entryPoint";
Package? package = uriTranslator.packages.packageOf(entryPoint);
if (package != null) {
String packageName = package.name;
Uri packageUri = package.packageUriRoot;
if (packageUri.hasFragment == true) {
packageUri = packageUri.removeFragment();
}
String prefix = "${packageUri}";
if (asString.startsWith(prefix)) {
Uri reversed = Uri.parse(
"package:$packageName/${asString.substring(prefix.length)}");
if (entryPoint == uriTranslator.translate(reversed)) {
entryPoint = reversed;
break;
}
}
}
}
return entryPoint;
}
/// The class [cls] is involved in a cyclic definition. This method should
/// ensure that the cycle is broken, for example, by removing superclass and
/// implemented interfaces.
void breakCycle(ClassBuilder builder) {
Class cls = builder.cls;
cls.implementedTypes.clear();
cls.supertype = null;
cls.mixedInType = null;
builder.supertypeBuilder = new NamedTypeBuilder(
"Object",
const NullabilityBuilder.omitted(),
/* arguments = */ null,
/* fileUri = */ null,
/* charOffset = */ null,
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected)
..bind(objectClassBuilder);
builder.interfaceBuilders = null;
builder.mixedInTypeBuilder = null;
}
bool _hasComputedNeededPrecompilations = false;
Future<NeededPrecompilations?> computeNeededPrecompilations() async {
assert(!_hasComputedNeededPrecompilations,
"Needed precompilations have already been computed.");
_hasComputedNeededPrecompilations = true;
if (loader.first == null) return null;
return withCrashReporting<NeededPrecompilations?>(() async {
benchmarker?.enterPhase(BenchmarkPhases.outline_kernelBuildOutlines);
await loader.buildOutlines();
benchmarker?.enterPhase(BenchmarkPhases.outline_becomeCoreLibrary);
loader.coreLibrary.becomeCoreLibrary();
benchmarker?.enterPhase(BenchmarkPhases.outline_resolveParts);
loader.resolveParts();
benchmarker?.enterPhase(BenchmarkPhases.outline_computeMacroDeclarations);
NeededPrecompilations? result = loader.computeMacroDeclarations();
benchmarker?.enterPhase(BenchmarkPhases.unknown);
return result;
}, () => loader.currentUriForCrashReporting);
}
/// Builds [augmentationLibraries] to the state expected after applying phase
/// 1 macros.
Future<void> _buildForPhase1(
Iterable<SourceLibraryBuilder> augmentationLibraries) async {
await loader.buildOutlines();
// Normally patch libraries are applied in [SourceLoader.resolveParts].
// For augmentation libraries we instead apply them directly here.
for (SourceLibraryBuilder augmentationLibrary in augmentationLibraries) {
augmentationLibrary.applyPatches();
}
loader.computeLibraryScopes(augmentationLibraries);
// TODO(johnniwinther): Support computation of macro applications in
// augmentation libraries?
loader.resolveTypes(augmentationLibraries);
}
/// Builds [augmentationLibraries] to the state expected after applying phase
/// 2 macros.
void _buildForPhase2(List<SourceLibraryBuilder> augmentationLibraries) {
loader.finishTypeVariables(
augmentationLibraries, objectClassBuilder, dynamicType);
for (SourceLibraryBuilder augmentationLibrary in augmentationLibraries) {
augmentationLibrary.build(loader.coreLibrary, modifyTarget: false);
}
loader.resolveConstructors(augmentationLibraries);
}
/// Builds [augmentationLibraries] to the state expected after applying phase
/// 3 macros.
void _buildForPhase3(List<SourceLibraryBuilder> augmentationLibraries) {
// Currently there nothing to do here. The method is left in for symmetry.
}
Future<BuildResult> buildOutlines({CanonicalName? nameRoot}) async {
if (loader.first == null) return new BuildResult();
return withCrashReporting<BuildResult>(() async {
if (!_hasComputedNeededPrecompilations) {
NeededPrecompilations? neededPrecompilations =
await computeNeededPrecompilations();
// To support macros, the needed macro libraries must be compiled be
// they are applied. Any supporting pipeline must therefore call
// [computeNeededPrecompilations] before calling [buildOutlines] in
// order to perform any need compilation in advance.
//
// If [neededPrecompilations] is non-null here, it means that macro
// compilation was needed but not performed.
if (neededPrecompilations != null) {
throw new UnsupportedError('Macro precompilation is not supported.');
}
}
benchmarker?.enterPhase(BenchmarkPhases.outline_computeLibraryScopes);
loader.computeLibraryScopes(loader.libraryBuilders);
benchmarker?.enterPhase(BenchmarkPhases.outline_computeMacroApplications);
MacroApplications? macroApplications =
await loader.computeMacroApplications();
benchmarker?.enterPhase(BenchmarkPhases.outline_setupTopAndBottomTypes);
setupTopAndBottomTypes();
benchmarker?.enterPhase(BenchmarkPhases.outline_resolveTypes);
loader.resolveTypes(loader.sourceLibraryBuilders);
benchmarker?.enterPhase(BenchmarkPhases.outline_computeVariances);
loader.computeVariances(loader.sourceLibraryBuilders);
benchmarker?.enterPhase(BenchmarkPhases.outline_computeDefaultTypes);
loader.computeDefaultTypes(
dynamicType, nullType, bottomType, objectClassBuilder);
if (macroApplications != null) {
benchmarker?.enterPhase(BenchmarkPhases.outline_applyTypeMacros);
List<SourceLibraryBuilder> augmentationLibraries =
await macroApplications.applyTypeMacros();
benchmarker
?.enterPhase(BenchmarkPhases.outline_buildMacroTypesForPhase1);
await _buildForPhase1(augmentationLibraries);
}
benchmarker?.enterPhase(BenchmarkPhases.outline_checkSemantics);
List<SourceClassBuilder>? sourceClassBuilders =
loader.checkSemantics(objectClassBuilder);
benchmarker?.enterPhase(BenchmarkPhases.outline_finishTypeVariables);
loader.finishTypeVariables(
loader.sourceLibraryBuilders, objectClassBuilder, dynamicType);
benchmarker
?.enterPhase(BenchmarkPhases.outline_createTypeInferenceEngine);
loader.createTypeInferenceEngine();
benchmarker?.enterPhase(BenchmarkPhases.outline_buildComponent);
loader.buildComponent();
benchmarker?.enterPhase(BenchmarkPhases.outline_installDefaultSupertypes);
installDefaultSupertypes();
benchmarker
?.enterPhase(BenchmarkPhases.outline_installSyntheticConstructors);
installSyntheticConstructors(sourceClassBuilders);
benchmarker?.enterPhase(BenchmarkPhases.outline_resolveConstructors);
loader.resolveConstructors(loader.sourceLibraryBuilders);
benchmarker?.enterPhase(BenchmarkPhases.outline_link);
component =
link(new List<Library>.of(loader.libraries), nameRoot: nameRoot);
benchmarker?.enterPhase(BenchmarkPhases.outline_computeCoreTypes);
computeCoreTypes();
benchmarker?.enterPhase(BenchmarkPhases.outline_buildClassHierarchy);
loader.buildClassHierarchy(sourceClassBuilders, objectClassBuilder);
benchmarker?.enterPhase(BenchmarkPhases.outline_checkSupertypes);
loader.checkSupertypes(sourceClassBuilders, enumClass);
if (macroApplications != null) {
benchmarker?.enterPhase(BenchmarkPhases.outline_applyDeclarationMacros);
await macroApplications.applyDeclarationsMacros(loader.hierarchyBuilder,
(SourceLibraryBuilder augmentationLibrary) async {
List<SourceLibraryBuilder> augmentationLibraries = [
augmentationLibrary
];
benchmarker?.enterPhase(
BenchmarkPhases.outline_buildMacroDeclarationsForPhase1);
await _buildForPhase1(augmentationLibraries);
benchmarker?.enterPhase(
BenchmarkPhases.outline_buildMacroDeclarationsForPhase2);
_buildForPhase2(augmentationLibraries);
});
}
benchmarker
?.enterPhase(BenchmarkPhases.outline_buildClassHierarchyMembers);
loader.buildClassHierarchyMembers(sourceClassBuilders);
benchmarker?.enterPhase(BenchmarkPhases.outline_computeHierarchy);
loader.computeHierarchy();
benchmarker?.enterPhase(BenchmarkPhases.outline_computeShowHideElements);
loader.computeShowHideElements();
benchmarker?.enterPhase(BenchmarkPhases.outline_installTypedefTearOffs);
loader.installTypedefTearOffs();
benchmarker?.enterPhase(BenchmarkPhases.outline_performTopLevelInference);
loader.performTopLevelInference(sourceClassBuilders);
benchmarker?.enterPhase(BenchmarkPhases.outline_checkOverrides);
loader.checkOverrides(sourceClassBuilders);
benchmarker?.enterPhase(BenchmarkPhases.outline_checkAbstractMembers);
loader.checkAbstractMembers(sourceClassBuilders);
benchmarker
?.enterPhase(BenchmarkPhases.outline_addNoSuchMethodForwarders);
loader.addNoSuchMethodForwarders(sourceClassBuilders);
benchmarker?.enterPhase(BenchmarkPhases.outline_checkMixins);
loader.checkMixins(sourceClassBuilders);
benchmarker?.enterPhase(BenchmarkPhases.outline_buildOutlineExpressions);
loader.buildOutlineExpressions(
loader.hierarchy, synthesizedFunctionNodes);
benchmarker?.enterPhase(BenchmarkPhases.outline_checkTypes);
loader.checkTypes();
benchmarker
?.enterPhase(BenchmarkPhases.outline_checkRedirectingFactories);
loader.checkRedirectingFactories(sourceClassBuilders);
benchmarker
?.enterPhase(BenchmarkPhases.outline_finishSynthesizedParameters);
finishSynthesizedParameters(forOutline: true);
benchmarker?.enterPhase(BenchmarkPhases.outline_checkMainMethods);
loader.checkMainMethods();
benchmarker
?.enterPhase(BenchmarkPhases.outline_installAllComponentProblems);
installAllComponentProblems(loader.allComponentProblems);
loader.allComponentProblems.clear();
benchmarker?.enterPhase(BenchmarkPhases.unknown);
// For whatever reason sourceClassBuilders is kept alive for some amount
// of time, meaning that all source library builders will be kept alive
// (for whatever amount of time) even though we convert them to dill
// library builders. To avoid it we null it out here.
sourceClassBuilders = null;
return new BuildResult(
component: component, macroApplications: macroApplications);
}, () => loader.currentUriForCrashReporting);
}
/// Build the kernel representation of the component loaded by this
/// target. The component will contain full bodies for the code loaded from
/// sources, and only references to the code loaded by the [DillTarget],
/// which may or may not include method bodies (depending on what was loaded
/// into that target, an outline or a full kernel component).
///
/// If [verify], run the default kernel verification on the resulting
/// component.
Future<BuildResult> buildComponent(
{required MacroApplications? macroApplications,
bool verify: false}) async {
if (loader.first == null) {
return new BuildResult(macroApplications: macroApplications);
}
return withCrashReporting<BuildResult>(() async {
ticker.logMs("Building component");
if (macroApplications != null) {
benchmarker?.enterPhase(BenchmarkPhases.body_applyDefinitionMacros);
List<SourceLibraryBuilder> augmentationLibraries =
await macroApplications.applyDefinitionMacros();
benchmarker
?.enterPhase(BenchmarkPhases.body_buildMacroDefinitionsForPhase1);
await _buildForPhase1(augmentationLibraries);
benchmarker
?.enterPhase(BenchmarkPhases.body_buildMacroDefinitionsForPhase2);
_buildForPhase2(augmentationLibraries);
benchmarker
?.enterPhase(BenchmarkPhases.body_buildMacroDefinitionsForPhase3);
_buildForPhase3(augmentationLibraries);
}
benchmarker?.enterPhase(BenchmarkPhases.body_buildBodies);
await loader.buildBodies(loader.sourceLibraryBuilders);
benchmarker?.enterPhase(BenchmarkPhases.body_finishSynthesizedParameters);
finishSynthesizedParameters();
benchmarker?.enterPhase(BenchmarkPhases.body_finishDeferredLoadTearoffs);
loader.finishDeferredLoadTearoffs();
benchmarker
?.enterPhase(BenchmarkPhases.body_finishNoSuchMethodForwarders);
loader.finishNoSuchMethodForwarders();
benchmarker?.enterPhase(BenchmarkPhases.body_collectSourceClasses);
List<SourceClassBuilder>? sourceClasses = loader.collectSourceClasses();
benchmarker?.enterPhase(BenchmarkPhases.body_finishNativeMethods);
loader.finishNativeMethods();
benchmarker?.enterPhase(BenchmarkPhases.body_finishPatchMethods);
loader.finishPatchMethods();
benchmarker?.enterPhase(BenchmarkPhases.body_finishAllConstructors);
finishAllConstructors(sourceClasses);
benchmarker?.enterPhase(BenchmarkPhases.body_runBuildTransformations);
runBuildTransformations();
if (verify) {
benchmarker?.enterPhase(BenchmarkPhases.body_verify);
this.verify();
}
benchmarker?.enterPhase(BenchmarkPhases.body_installAllComponentProblems);
installAllComponentProblems(loader.allComponentProblems);
benchmarker?.enterPhase(BenchmarkPhases.unknown);
// For whatever reason sourceClasses is kept alive for some amount
// of time, meaning that all source library builders will be kept alive
// (for whatever amount of time) even though we convert them to dill
// library builders. To avoid it we null it out here.
sourceClasses = null;
return new BuildResult(
component: component, macroApplications: macroApplications);
}, () => loader.currentUriForCrashReporting);
}
void installAllComponentProblems(
List<FormattedMessage> allComponentProblems) {
if (allComponentProblems.isNotEmpty) {
component!.problemsAsJson ??= <String>[];
}
for (int i = 0; i < allComponentProblems.length; i++) {
FormattedMessage formattedMessage = allComponentProblems[i];
component!.problemsAsJson!.add(formattedMessage.toJsonString());
}
}
/// Creates a component by combining [libraries] with the libraries of
/// `dillTarget.loader.component`.
Component link(List<Library> libraries, {CanonicalName? nameRoot}) {
libraries.addAll(dillTarget.loader.libraries);
Map<Uri, Source> uriToSource = new Map<Uri, Source>();
void copySource(Uri uri, Source source) {
uriToSource[uri] = excludeSource
? new Source(source.lineStarts, const <int>[], source.importUri,
source.fileUri)
: source;
}
this.uriToSource.forEach(copySource);
Component component = backendTarget.configureComponent(new Component(
nameRoot: nameRoot, libraries: libraries, uriToSource: uriToSource));
NonNullableByDefaultCompiledMode? compiledMode = null;
if (isExperimentEnabledGlobally(ExperimentalFlag.nonNullable)) {
switch (loader.nnbdMode) {
case NnbdMode.Weak:
compiledMode = NonNullableByDefaultCompiledMode.Weak;
break;
case NnbdMode.Strong:
compiledMode = NonNullableByDefaultCompiledMode.Strong;
break;
case NnbdMode.Agnostic:
compiledMode = NonNullableByDefaultCompiledMode.Agnostic;
break;
}
} else {
compiledMode = NonNullableByDefaultCompiledMode.Weak;
}
if (loader.hasInvalidNnbdModeLibrary) {
compiledMode = NonNullableByDefaultCompiledMode.Invalid;
}
Reference? mainReference;
if (loader.first != null) {
// TODO(sigmund): do only for full program
Builder? declaration =
loader.first!.exportScope.lookup("main", -1, loader.first!.fileUri);
if (declaration is AmbiguousBuilder) {
AmbiguousBuilder problem = declaration;
declaration = problem.getFirstDeclaration();
}
if (declaration is ProcedureBuilder) {
mainReference = declaration.procedure.reference;
}
}
component.setMainMethodAndMode(mainReference, true, compiledMode);
assert(_getLibraryNnbdModeError(component) == null,
"Got error: ${_getLibraryNnbdModeError(component)}");
ticker.logMs("Linked component");
return component;
}
String? _getLibraryNnbdModeError(Component component) {
if (loader.hasInvalidNnbdModeLibrary) {
// At least 1 library should be invalid or there should be a mix of strong
// and weak. For libraries we've just compiled it will be marked as
// invalid, but for libraries loaded from dill they have their original
// value (i.e. either strong or weak).
bool foundInvalid = false;
bool foundStrong = false;
bool foundWeak = false;
for (Library library in component.libraries) {
if (library.nonNullableByDefaultCompiledMode ==
NonNullableByDefaultCompiledMode.Invalid) {
foundInvalid = true;
break;
} else if (!foundWeak &&
library.nonNullableByDefaultCompiledMode ==
NonNullableByDefaultCompiledMode.Weak) {
foundWeak = true;
if (foundStrong) break;
} else if (!foundStrong &&
library.nonNullableByDefaultCompiledMode ==
NonNullableByDefaultCompiledMode.Strong) {
foundStrong = true;
if (foundWeak) break;
}
}
if (!foundInvalid && !(foundStrong && foundWeak)) {
return "hasInvalidNnbdModeLibrary is true, but no library was invalid "
"and there was no weak/strong mix.";
}
if (component.mode != NonNullableByDefaultCompiledMode.Invalid) {
return "Component mode is not invalid as expected";
}
} else {
// No libraries are allowed to be invalid, and should all be compatible
// with the component nnbd mode setting.
if (component.mode == NonNullableByDefaultCompiledMode.Invalid) {
return "Component mode is invalid which was not expected";
}
if (component.modeRaw == null) {
return "Component mode not set at all";
}
for (Library library in component.libraries) {
if (component.mode == NonNullableByDefaultCompiledMode.Strong) {
if (library.nonNullableByDefaultCompiledMode !=
NonNullableByDefaultCompiledMode.Strong &&
library.nonNullableByDefaultCompiledMode !=
NonNullableByDefaultCompiledMode.Agnostic) {
return "Expected library ${library.importUri} to be strong or "
"agnostic, but was ${library.nonNullableByDefaultCompiledMode}";
}
} else if (component.mode == NonNullableByDefaultCompiledMode.Weak) {
if (library.nonNullableByDefaultCompiledMode !=
NonNullableByDefaultCompiledMode.Weak &&
library.nonNullableByDefaultCompiledMode !=
NonNullableByDefaultCompiledMode.Agnostic) {
return "Expected library ${library.importUri} to be weak or "
"agnostic, but was ${library.nonNullableByDefaultCompiledMode}";
}
} else if (component.mode ==
NonNullableByDefaultCompiledMode.Agnostic) {
if (library.nonNullableByDefaultCompiledMode !=
NonNullableByDefaultCompiledMode.Agnostic) {
return "Expected library ${library.importUri} to be agnostic, "
"but was ${library.nonNullableByDefaultCompiledMode}";
}
} else {
return "Expected component mode to be either strong, "
"weak or agnostic but was ${component.mode}";
}
}
}
return null;
}
void installDefaultSupertypes() {
Class objectClass = this.objectClass;
for (SourceLibraryBuilder library in loader.sourceLibraryBuilders) {
library.installDefaultSupertypes(objectClassBuilder, objectClass);
}
ticker.logMs("Installed Object as implicit superclass");
}
void installSyntheticConstructors(List<SourceClassBuilder> builders) {
Class objectClass = this.objectClass;
for (SourceClassBuilder builder in builders) {
if (builder.cls != objectClass && !builder.isPatch) {
if (builder.isPatch ||
builder.isMixinDeclaration ||
builder.isExtension) {
continue;
}
if (builder.isMixinApplication) {
installForwardingConstructors(builder);
} else {
installDefaultConstructor(builder);
}
}
}
ticker.logMs("Installed synthetic constructors");
}
ClassBuilder get objectClassBuilder => objectType.declaration as ClassBuilder;
Class get objectClass => objectClassBuilder.cls;
ClassBuilder get enumClassBuilder => enumType.declaration as ClassBuilder;
Class get enumClass => enumClassBuilder.cls;
/// If [builder] doesn't have a constructors, install the defaults.
void installDefaultConstructor(SourceClassBuilder builder) {
assert(!builder.isMixinApplication);
assert(!builder.isExtension);
// TODO(askesc): Make this check light-weight in the absence of patches.
if (builder.cls.constructors.isNotEmpty) return;
if (builder.cls.redirectingFactories.isNotEmpty) return;
for (Procedure proc in builder.cls.procedures) {
if (proc.isFactory) return;
}
IndexedClass? indexedClass = builder.referencesFromIndexed;
Reference? constructorReference;
Reference? tearOffReference;
if (indexedClass != null) {
constructorReference =
indexedClass.lookupConstructorReference(new Name(""));
tearOffReference = indexedClass.lookupGetterReference(
constructorTearOffName("", indexedClass.library));
}
/// From [Dart Programming Language Specification, 4th Edition](
/// https://ecma-international.org/publications/files/ECMA-ST/ECMA-408.pdf):
/// >Iff no constructor is specified for a class C, it implicitly has a
/// >default constructor C() : super() {}, unless C is class Object.
// The superinitializer is installed below in [finishConstructors].
builder.addSyntheticConstructor(_makeDefaultConstructor(
builder, constructorReference, tearOffReference));
}
void installForwardingConstructors(SourceClassBuilder builder) {
assert(builder.isMixinApplication);
if (builder.library.loader != loader) return;
if (builder.cls.constructors.isNotEmpty) {
// These were installed by a subclass in the recursive call below.
return;
}
/// From [Dart Programming Language Specification, 4th Edition](
/// https://ecma-international.org/publications/files/ECMA-ST/ECMA-408.pdf):
/// >A mixin application of the form S with M; defines a class C with
/// >superclass S.
/// >...
/// >Let LM be the library in which M is declared. For each generative
/// >constructor named qi(Ti1 ai1, . . . , Tiki aiki), i in 1..n of S
/// >that is accessible to LM , C has an implicitly declared constructor
/// >named q'i = [C/S]qi of the form q'i(ai1,...,aiki) :
/// >super(ai1,...,aiki);.
TypeBuilder? type = builder.supertypeBuilder;
TypeDeclarationBuilder? supertype;
if (type is NamedTypeBuilder) {
supertype = type.declaration;
} else {
unhandled("${type.runtimeType}", "installForwardingConstructors",
builder.charOffset, builder.fileUri);
}
if (supertype is TypeAliasBuilder) {
TypeAliasBuilder aliasBuilder = supertype;
NamedTypeBuilder namedBuilder = type;
supertype = aliasBuilder.unaliasDeclaration(namedBuilder.arguments,
isUsedAsClass: true,
usedAsClassCharOffset: namedBuilder.charOffset,
usedAsClassFileUri: namedBuilder.fileUri);
}
if (supertype is SourceClassBuilder && supertype.isMixinApplication) {
installForwardingConstructors(supertype);
}
IndexedClass? indexedClass = builder.referencesFromIndexed;
Reference? constructorReference;
Reference? tearOffReference;
if (indexedClass != null) {
constructorReference =
indexedClass.lookupConstructorReference(new Name(""));
tearOffReference = indexedClass.lookupGetterReference(
constructorTearOffName("", indexedClass.library));
}
if (supertype is ClassBuilder) {
ClassBuilder superclassBuilder = supertype;
bool isConstructorAdded = false;
Map<TypeParameter, DartType>? substitutionMap;
void addSyntheticConstructor(String name, MemberBuilder memberBuilder) {
if (memberBuilder.member is Constructor) {
substitutionMap ??= builder.getSubstitutionMap(superclassBuilder.cls);
Reference? constructorReference;
Reference? tearOffReference;
if (indexedClass != null) {
constructorReference = indexedClass
// We use the name of the member builder here since it refers to
// the library of the original declaration when private. For
// instance:
//
// // lib1:
// class Super { Super._() }
// class Subclass extends Class {
// Subclass() : super._();
// }
// // lib2:
// class Mixin {}
// class Class = Super with Mixin;
//
// Here `super._()` in `Subclass` targets the forwarding stub
// added to `Class` whose name is `_` private to `lib1`.
.lookupConstructorReference(memberBuilder.member.name);
tearOffReference = indexedClass.lookupGetterReference(
constructorTearOffName(name, indexedClass.library));
}
builder.addSyntheticConstructor(_makeMixinApplicationConstructor(
builder,
builder.cls.mixin,
memberBuilder as MemberBuilderImpl,
substitutionMap!,
constructorReference,
tearOffReference));
isConstructorAdded = true;
}
}
superclassBuilder.forEachConstructor(addSyntheticConstructor);
if (!isConstructorAdded) {
builder.addSyntheticConstructor(_makeDefaultConstructor(
builder, constructorReference, tearOffReference));
}
} else if (supertype is InvalidTypeDeclarationBuilder ||
supertype is TypeVariableBuilder ||
supertype is DynamicTypeDeclarationBuilder ||
supertype is VoidTypeDeclarationBuilder ||
supertype is NeverTypeDeclarationBuilder ||
supertype is TypeAliasBuilder) {
builder.addSyntheticConstructor(_makeDefaultConstructor(
builder, constructorReference, tearOffReference));
} else {
unhandled("${supertype.runtimeType}", "installForwardingConstructors",
builder.charOffset, builder.fileUri);
}
}
SyntheticSourceConstructorBuilder _makeMixinApplicationConstructor(
SourceClassBuilder classBuilder,
Class mixin,
MemberBuilder superConstructorBuilder,
Map<TypeParameter, DartType> substitutionMap,
Reference? constructorReference,
Reference? tearOffReference) {
bool hasTypeDependency = false;
Substitution substitution = Substitution.fromMap(substitutionMap);
VariableDeclaration copyFormal(VariableDeclaration formal) {
VariableDeclaration copy = new VariableDeclaration(formal.name,
isFinal: formal.isFinal,
isConst: formal.isConst,
type: const UnknownType());
if (!hasTypeDependency && formal.type is! UnknownType) {
copy.type = substitution.substituteType(formal.type);
} else {
hasTypeDependency = true;
}
return copy;
}
Class cls = classBuilder.cls;
Constructor superConstructor =
superConstructorBuilder.member as Constructor;
bool isConst = superConstructor.isConst;
if (isConst && mixin.fields.isNotEmpty) {
for (Field field in mixin.fields) {
if (!field.isStatic) {
isConst = false;
break;
}
}
}
List<VariableDeclaration> positionalParameters = <VariableDeclaration>[];
List<VariableDeclaration> namedParameters = <VariableDeclaration>[];
List<Expression> positional = <Expression>[];
List<NamedExpression> named = <NamedExpression>[];
for (VariableDeclaration formal
in superConstructor.function.positionalParameters) {
positionalParameters.add(copyFormal(formal));
positional.add(new VariableGet(positionalParameters.last));
}
for (VariableDeclaration formal
in superConstructor.function.namedParameters) {
VariableDeclaration clone = copyFormal(formal);
namedParameters.add(clone);
named.add(new NamedExpression(
formal.name!, new VariableGet(namedParameters.last)));
}
FunctionNode function = new FunctionNode(new EmptyStatement(),
positionalParameters: positionalParameters,
namedParameters: namedParameters,
requiredParameterCount:
superConstructor.function.requiredParameterCount,
returnType: makeConstructorReturnType(cls));
SuperInitializer initializer = new SuperInitializer(
superConstructor, new Arguments(positional, named: named));
SynthesizedFunctionNode synthesizedFunctionNode =
new SynthesizedFunctionNode(
substitutionMap, superConstructor.function, function);
if (!isConst) {
// For constant constructors default values are computed and cloned part
// of the outline expression and therefore passed to the
// [SyntheticConstructorBuilder] below.
//
// For non-constant constructors default values are cloned as part of the
// full compilation using [synthesizedFunctionNodes].
synthesizedFunctionNodes.add(synthesizedFunctionNode);
}
Constructor constructor = new Constructor(function,
name: superConstructor.name,
initializers: <Initializer>[initializer],
isSynthetic: true,
isConst: isConst,
reference: constructorReference,
fileUri: cls.fileUri)
// TODO(johnniwinther): Should we add file offsets to synthesized
// constructors?
//..fileOffset = cls.fileOffset
//..fileEndOffset = cls.fileOffset
..isNonNullableByDefault = cls.enclosingLibrary.isNonNullableByDefault;
if (hasTypeDependency) {
loader.registerTypeDependency(
constructor,
new TypeDependency(constructor, superConstructor, substitution,
copyReturnType: false));
}
Procedure? constructorTearOff = createConstructorTearOffProcedure(
superConstructor.name.text,
classBuilder.library,
cls.fileUri,
cls.fileOffset,
tearOffReference,
forAbstractClassOrEnum: classBuilder.isAbstract);
if (constructorTearOff != null) {
buildConstructorTearOffProcedure(constructorTearOff, constructor,
classBuilder.cls, classBuilder.library);
}
return new SyntheticSourceConstructorBuilder(
classBuilder, constructor, constructorTearOff,
// We pass on the original constructor and the cloned function nodes to
// ensure that the default values are computed and cloned for the
// outline. It is needed to make the default values a part of the
// outline for const constructors, and additionally it is required for
// a potential subclass using super initializing parameters that will
// required the cloning of the default values.
origin: superConstructorBuilder,
synthesizedFunctionNode: synthesizedFunctionNode);
}
void finishSynthesizedParameters({bool forOutline = false}) {
for (SynthesizedFunctionNode synthesizedFunctionNode
in synthesizedFunctionNodes) {
if (!forOutline || synthesizedFunctionNode.isOutlineNode) {
synthesizedFunctionNode.cloneDefaultValues();
}
}
if (!forOutline) {
synthesizedFunctionNodes.clear();
}
ticker.logMs("Cloned default values of formals");
}
SyntheticSourceConstructorBuilder _makeDefaultConstructor(
SourceClassBuilder classBuilder,
Reference? constructorReference,
Reference? tearOffReference) {
Class enclosingClass = classBuilder.cls;
Constructor constructor = new Constructor(
new FunctionNode(new EmptyStatement(),
returnType: makeConstructorReturnType(enclosingClass)),
name: new Name(""),
isSynthetic: true,
reference: constructorReference,
fileUri: enclosingClass.fileUri)
..fileOffset = enclosingClass.fileOffset
// TODO(johnniwinther): Should we add file end offsets to synthesized
// constructors?
//..fileEndOffset = enclosingClass.fileOffset
..isNonNullableByDefault =
enclosingClass.enclosingLibrary.isNonNullableByDefault;
Procedure? constructorTearOff = createConstructorTearOffProcedure(
'',
classBuilder.library,
enclosingClass.fileUri,
enclosingClass.fileOffset,
tearOffReference,
forAbstractClassOrEnum:
enclosingClass.isAbstract || enclosingClass.isEnum);
if (constructorTearOff != null) {
buildConstructorTearOffProcedure(constructorTearOff, constructor,
classBuilder.cls, classBuilder.library);
}
return new SyntheticSourceConstructorBuilder(
classBuilder, constructor, constructorTearOff);
}
DartType makeConstructorReturnType(Class enclosingClass) {
List<DartType> typeParameterTypes = <DartType>[];
for (int i = 0; i < enclosingClass.typeParameters.length; i++) {
TypeParameter typeParameter = enclosingClass.typeParameters[i];
typeParameterTypes.add(
new TypeParameterType.withDefaultNullabilityForLibrary(
typeParameter, enclosingClass.enclosingLibrary));
}
return new InterfaceType(enclosingClass,
enclosingClass.enclosingLibrary.nonNullable, typeParameterTypes);
}
void setupTopAndBottomTypes() {
objectType.bind(loader.coreLibrary
.lookupLocalMember("Object", required: true) as TypeDeclarationBuilder);
dynamicType.bind(
loader.coreLibrary.lookupLocalMember("dynamic", required: true)
as TypeDeclarationBuilder);
ClassBuilder nullClassBuilder = loader.coreLibrary
.lookupLocalMember("Null", required: true) as ClassBuilder;
nullType.bind(nullClassBuilder..isNullClass = true);
bottomType.bind(loader.coreLibrary
.lookupLocalMember("Never", required: true) as TypeDeclarationBuilder);
enumType.bind(loader.coreLibrary.lookupLocalMember("Enum", required: true)
as TypeDeclarationBuilder);
underscoreEnumType.bind(loader.coreLibrary
.lookupLocalMember("_Enum", required: true) as TypeDeclarationBuilder);
}
void computeCoreTypes() {
List<Library> libraries = <Library>[];
for (String platformLibrary in [
"dart:_internal",
"dart:async",
"dart:core",
"dart:mirrors",
...backendTarget.extraIndexedLibraries
]) {
Uri uri = Uri.parse(platformLibrary);
LibraryBuilder? libraryBuilder = loader.lookupLibraryBuilder(uri);
if (libraryBuilder == null) {
// TODO(ahe): This is working around a bug in kernel_driver_test or
// kernel_driver.
bool found = false;
for (Library target in dillTarget.loader.libraries) {
if (target.importUri == uri) {
libraries.add(target);
found = true;
break;
}
}
if (!found && uri.path != "mirrors") {
// dart:mirrors is optional.
throw "Can't find $uri";
}
} else {
libraries.add(libraryBuilder.library);
}
}
Component platformLibraries =
backendTarget.configureComponent(new Component());
// Add libraries directly to prevent that their parents are changed.
platformLibraries.libraries.addAll(libraries);
loader.computeCoreTypes(platformLibraries);
}
void finishAllConstructors(List<SourceClassBuilder> builders) {
Class objectClass = this.objectClass;
for (SourceClassBuilder builder in builders) {
Class cls = builder.cls;
if (cls != objectClass) {
finishConstructors(builder);
}
}
ticker.logMs("Finished constructors");
}
/// Ensure constructors of [builder] have the correct initializers and other
/// requirements.
void finishConstructors(SourceClassBuilder builder) {
if (builder.isPatch) return;
Class cls = builder.cls;
/// Quotes below are from [Dart Programming Language Specification, 4th
/// Edition](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-408.pdf):
List<SourceFieldBuilder> uninitializedFields = [];
List<SourceFieldBuilder> nonFinalFields = [];
List<SourceFieldBuilder> lateFinalFields = [];
builder
.forEachDeclaredField((String name, SourceFieldBuilder fieldBuilder) {
if (fieldBuilder.isAbstract || fieldBuilder.isExternal) {
// Skip abstract and external fields. These are abstract/external
// getters/setters and have no initialization.
return;
}
if (fieldBuilder.isDeclarationInstanceMember && !fieldBuilder.isFinal) {
nonFinalFields.add(fieldBuilder);
}
if (fieldBuilder.isDeclarationInstanceMember &&
fieldBuilder.isLate &&
fieldBuilder.isFinal) {
lateFinalFields.add(fieldBuilder);
}
if (!fieldBuilder.hasInitializer) {
// In case of duplicating fields the earliest ones (those that
// declared towards the beginning of the file) come last in the list.
// To report errors on the first definition of a field, we need to
// iterate until that last element.
SourceFieldBuilder earliest = fieldBuilder;
Builder current = fieldBuilder;
while (current.next != null) {
current = current.next!;
if (current is SourceFieldBuilder && !fieldBuilder.hasInitializer) {
earliest = current;
}
}
uninitializedFields.add(earliest);
}
});
Constructor? superTarget;
// In the underlying Kernel IR the patches are already applied, so
// cls.constructors should contain both constructors from the original
// declaration and the constructors from the patch. The assert checks that
// it's so.
assert(() {
Set<String> patchConstructorNames = {};
builder.forEachDeclaredConstructor(
(String name, ConstructorBuilder constructorBuilder) {
// Don't add the default constructor's name.
if (name.isNotEmpty) {
patchConstructorNames.add(name);
}
});
builder.constructorScope.forEach((String name, Builder builder) {
if (builder is ConstructorBuilder) {
patchConstructorNames.remove(name);
}
});
Set<String> kernelConstructorNames =
cls.constructors.map((c) => c.name.text).toSet().difference({""});
return kernelConstructorNames.containsAll(patchConstructorNames);
}(),
"Constructors of class '${builder.fullNameForErrors}' "
"aren't fully patched.");
for (Constructor constructor in cls.constructors) {
bool isRedirecting = false;
for (Initializer initializer in constructor.initializers) {
if (initializer is RedirectingInitializer) {
if (constructor.isConst && !initializer.target.isConst) {
builder.addProblem(messageConstConstructorRedirectionToNonConst,
initializer.fileOffset, initializer.target.name.text.length);
}
isRedirecting = true;
break;
}
}
if (!isRedirecting) {
/// >If no superinitializer is provided, an implicit superinitializer
/// >of the form super() is added at the end of k’s initializer list,
/// >unless the enclosing class is class Object.
if (constructor.initializers.isEmpty) {
superTarget ??= defaultSuperConstructor(cls);
Initializer initializer;
if (superTarget == null) {
int offset = constructor.fileOffset;
if (offset == -1 && constructor.isSynthetic) {
offset = cls.fileOffset;
}
builder.addProblem(
templateSuperclassHasNoDefaultConstructor
.withArguments(cls.superclass!.name),
offset,
noLength);
initializer = new InvalidInitializer();
} else {
initializer =
new SuperInitializer(superTarget, new Arguments.empty())
..isSynthetic = true;
}
constructor.initializers.add(initializer);
initializer.parent = constructor;
}
if (constructor.function.body == null) {
/// >If a generative constructor c is not a redirecting constructor
/// >and no body is provided, then c implicitly has an empty body {}.
/// We use an empty statement instead.
constructor.function.body = new EmptyStatement()
..parent = constructor.function;
}
if (constructor.isConst && nonFinalFields.isNotEmpty) {
builder.addProblem(messageConstConstructorNonFinalField,
constructor.fileOffset, noLength,
context: nonFinalFields
.map((field) => messageConstConstructorNonFinalFieldCause
.withLocation(field.fileUri, field.charOffset, noLength))
.toList());
nonFinalFields.clear();
}
SourceLibraryBuilder library = builder.library;
if (library.isNonNullableByDefault) {
if (constructor.isConst && lateFinalFields.isNotEmpty) {
for (FieldBuilder field in lateFinalFields) {
builder.addProblem(messageConstConstructorLateFinalFieldError,
field.charOffset, noLength,
context: [
messageConstConstructorLateFinalFieldCause.withLocation(
constructor.fileUri, constructor.fileOffset, noLength)
]);
}
lateFinalFields.clear();
}
}
}
}
Map<ConstructorBuilder, Set<SourceFieldBuilder>>
constructorInitializedFields = new Map.identity();
Set<SourceFieldBuilder>? initializedFields = null;
builder.forEachDeclaredConstructor(
(String name, DeclaredSourceConstructorBuilder constructorBuilder) {
if (constructorBuilder.isExternal) return;
// In case of duplicating constructors the earliest ones (those that
// declared towards the beginning of the file) come last in the list.
// To report errors on the first definition of a constructor, we need to
// iterate until that last element.
DeclaredSourceConstructorBuilder earliest = constructorBuilder;
Builder earliestBuilder = constructorBuilder;
while (earliestBuilder.next != null) {
earliestBuilder = earliestBuilder.next!;
if (earliestBuilder is DeclaredSourceConstructorBuilder) {
earliest = earliestBuilder;
}
}
bool isRedirecting = false;
for (Initializer initializer in earliest.constructor.initializers) {
if (initializer is RedirectingInitializer) {
isRedirecting = true;
}
}
if (!isRedirecting) {
Set<SourceFieldBuilder> fields =
earliest.takeInitializedFields() ?? const {};
constructorInitializedFields[earliest] = fields;
(initializedFields ??= new Set<SourceFieldBuilder>.identity())
.addAll(fields);
}
});
// Run through all fields that aren't initialized by any constructor, and
// set their initializer to `null`.
for (SourceFieldBuilder fieldBuilder in uninitializedFields) {
if (initializedFields == null ||
!initializedFields!.contains(fieldBuilder)) {
bool uninitializedFinalOrNonNullableFieldIsError =
cls.enclosingLibrary.isNonNullableByDefault ||
(cls.constructors.isNotEmpty || cls.isMixinDeclaration);
if (!fieldBuilder.isLate) {
if (fieldBuilder.isFinal &&
uninitializedFinalOrNonNullableFieldIsError) {
String uri = '${fieldBuilder.library.importUri}';
String file = fieldBuilder.fileUri.pathSegments.last;
if (uri == 'dart:html' ||
uri == 'dart:svg' ||
uri == 'dart:_native_typed_data' ||
uri == 'dart:_interceptors' && file == 'js_string.dart') {
// TODO(johnniwinther): Use external getters instead of final
// fields. See https://github.com/dart-lang/sdk/issues/33762
} else {
builder.library.addProblem(
templateFinalFieldNotInitialized
.withArguments(fieldBuilder.name),
fieldBuilder.charOffset,
fieldBuilder.name.length,
fieldBuilder.fileUri);
}
} else if (fieldBuilder.fieldType is! InvalidType &&
fieldBuilder.fieldType.isPotentiallyNonNullable &&
uninitializedFinalOrNonNullableFieldIsError) {
SourceLibraryBuilder library = builder.library;
if (library.isNonNullableByDefault) {
library.addProblem(
templateFieldNonNullableWithoutInitializerError.withArguments(
fieldBuilder.name,
fieldBuilder.fieldType,
library.isNonNullableByDefault),
fieldBuilder.charOffset,
fieldBuilder.name.length,
fieldBuilder.fileUri);
}
}
}
}
}
// Run through all fields that are initialized by some constructor, and
// make sure that all other constructors also initialize them.
constructorInitializedFields.forEach((ConstructorBuilder constructorBuilder,
Set<FieldBuilder> fieldBuilders) {
for (SourceFieldBuilder fieldBuilder
in initializedFields!.difference(fieldBuilders)) {
if (!fieldBuilder.hasInitializer && !fieldBuilder.isLate) {
FieldInitializer initializer =
new FieldInitializer(fieldBuilder.field, new NullLiteral())
..isSynthetic = true;
initializer.parent = constructorBuilder.constructor;
constructorBuilder.constructor.initializers.insert(0, initializer);
if (fieldBuilder.isFinal) {
builder.library.addProblem(
templateFinalFieldNotInitializedByConstructor
.withArguments(fieldBuilder.name),
constructorBuilder.charOffset,
constructorBuilder.name.length,
constructorBuilder.fileUri,
context: [
templateMissingImplementationCause
.withArguments(fieldBuilder.name)
.withLocation(fieldBuilder.fileUri,
fieldBuilder.charOffset, fieldBuilder.name.length)
]);
} else if (fieldBuilder.field.type is! InvalidType &&
!fieldBuilder.isLate &&
fieldBuilder.field.type.isPotentiallyNonNullable) {
SourceLibraryBuilder library = builder.library;
if (library.isNonNullableByDefault) {
library.addProblem(
templateFieldNonNullableNotInitializedByConstructorError
.withArguments(fieldBuilder.name, fieldBuilder.field.type,
library.isNonNullableByDefault),
constructorBuilder.charOffset,
noLength,
constructorBuilder.fileUri,
context: [
templateMissingImplementationCause
.withArguments(fieldBuilder.name)
.withLocation(fieldBuilder.fileUri,
fieldBuilder.charOffset, fieldBuilder.name.length)
]);
}
}
}
}
});
Set<Field>? initializedFieldsKernel = null;
if (initializedFields != null) {
for (FieldBuilder fieldBuilder in initializedFields!) {
(initializedFieldsKernel ??= new Set<Field>.identity())
.add(fieldBuilder.field);
}
}
// In the underlying Kernel IR the patches are already applied, so
// cls.fields should contain both fields from the original
// declaration and the fields from the patch. The assert checks that
// it's so.
assert(() {
Set<String> patchFieldNames = {};
builder
.forEachDeclaredField((String name, SourceFieldBuilder fieldBuilder) {
patchFieldNames.add(NameScheme.createFieldName(
FieldNameType.Field,
name,
isInstanceMember: fieldBuilder.isClassInstanceMember,
className: builder.name,
isSynthesized: fieldBuilder.isLateLowered,
));
});
builder.forEach((String name, Builder builder) {
if (builder is FieldBuilder) {
patchFieldNames.remove(name);
}
});
Set<String> kernelFieldNames = cls.fields.map((f) => f.name.text).toSet();
return kernelFieldNames.containsAll(patchFieldNames);
}(),
"Fields of class '${builder.fullNameForErrors}' "
"aren't fully patched.");
for (Field field in cls.fields) {
if (field.initializer == null &&
!field.isLate &&
(initializedFieldsKernel == null ||
!initializedFieldsKernel.contains(field))) {
field.initializer = new NullLiteral()..parent = field;
}
}
}
/// Run all transformations that are needed when building a bundle of
/// libraries for the first time.
void runBuildTransformations() {
backendTarget.performPreConstantEvaluationTransformations(
component!,
loader.coreTypes,
loader.libraries,
new KernelDiagnosticReporter(loader),
logger: (String msg) => ticker.logMs(msg),
changedStructureNotifier: changedStructureNotifier);
TypeEnvironment environment =
new TypeEnvironment(loader.coreTypes, loader.hierarchy);
constants.EvaluationMode evaluationMode = _getConstantEvaluationMode();
constants.ConstantEvaluationData constantEvaluationData =
constants.transformLibraries(
component!,
loader.libraries,
backendTarget,
environmentDefines,
environment,
new KernelConstantErrorReporter(loader),
evaluationMode,
evaluateAnnotations: true,
enableTripleShift:
isExperimentEnabledGlobally(ExperimentalFlag.tripleShift),
enableConstFunctions:
isExperimentEnabledGlobally(ExperimentalFlag.constFunctions),
enableConstructorTearOff: isExperimentEnabledGlobally(
ExperimentalFlag.constructorTearoffs),
errorOnUnevaluatedConstant: errorOnUnevaluatedConstant);
ticker.logMs("Evaluated constants");
markLibrariesUsed(constantEvaluationData.visitedLibraries);
constants.ConstantCoverage coverage = constantEvaluationData.coverage;
coverage.constructorCoverage.forEach((Uri fileUri, Set<Reference> value) {
Source? source = uriToSource[fileUri];
// ignore: unnecessary_null_comparison
if (source != null && fileUri != null) {
source.constantCoverageConstructors ??= new Set<Reference>();
source.constantCoverageConstructors!.addAll(value);
}
});
ticker.logMs("Added constant coverage");
if (loader.target.context.options
.isExperimentEnabledGlobally(ExperimentalFlag.valueClass)) {
valueClass.transformComponent(component!, loader.coreTypes,
loader.hierarchy, loader.referenceFromIndex, environment);
ticker.logMs("Lowered value classes");
}
backendTarget.performModularTransformationsOnLibraries(
component!,
loader.coreTypes,
loader.hierarchy,
loader.libraries,
environmentDefines,
new KernelDiagnosticReporter(loader),
loader.referenceFromIndex,
logger: (String msg) => ticker.logMs(msg),
changedStructureNotifier: changedStructureNotifier);
}
ChangedStructureNotifier? get changedStructureNotifier => null;
void runProcedureTransformations(Procedure procedure) {
TypeEnvironment environment =
new TypeEnvironment(loader.coreTypes, loader.hierarchy);
constants.EvaluationMode evaluationMode = _getConstantEvaluationMode();
constants.transformProcedure(
procedure,
backendTarget,
component!,
environmentDefines,
environment,
new KernelConstantErrorReporter(loader),
evaluationMode,
evaluateAnnotations: true,
enableTripleShift:
isExperimentEnabledGlobally(ExperimentalFlag.tripleShift),
enableConstFunctions:
isExperimentEnabledGlobally(ExperimentalFlag.constFunctions),
enableConstructorTearOff:
isExperimentEnabledGlobally(ExperimentalFlag.constructorTearoffs),
errorOnUnevaluatedConstant: errorOnUnevaluatedConstant,
);
ticker.logMs("Evaluated constants");
backendTarget.performTransformationsOnProcedure(
loader.coreTypes, loader.hierarchy, procedure, environmentDefines,
logger: (String msg) => ticker.logMs(msg));
}
constants.EvaluationMode getConstantEvaluationModeForTesting() =>
_getConstantEvaluationMode();
constants.EvaluationMode _getConstantEvaluationMode() {
constants.EvaluationMode evaluationMode;
// If nnbd is not enabled we will use weak evaluation mode. This is needed
// because the SDK might be agnostic and therefore needs to be weakened
// for legacy mode.
assert(
isExperimentEnabledGlobally(ExperimentalFlag.nonNullable) ||
loader.nnbdMode == NnbdMode.Weak,
"Non-weak nnbd mode found without experiment enabled: "
"${loader.nnbdMode}.");
switch (loader.nnbdMode) {
case NnbdMode.Weak:
evaluationMode = constants.EvaluationMode.weak;
break;
case NnbdMode.Strong:
evaluationMode = constants.EvaluationMode.strong;
break;
case NnbdMode.Agnostic:
evaluationMode = constants.EvaluationMode.agnostic;
break;
}
return evaluationMode;
}
void verify() {
// TODO(ahe): How to handle errors.
verifyComponent(component!, context.options.target,
skipPlatform: context.options.skipPlatformVerification);
ClassHierarchy hierarchy =
new ClassHierarchy(component!, new CoreTypes(component!),
onAmbiguousSupertypes: (Class cls, Supertype a, Supertype b) {
// An error has already been reported.
});
verifyGetStaticType(
new TypeEnvironment(loader.coreTypes, hierarchy), component!,
skipPlatform: context.options.skipPlatformVerification);
ticker.logMs("Verified component");
}
/// Return `true` if the given [library] was built by this [KernelTarget]
/// from sources, and not loaded from a [DillTarget].
/// Note that this is meant for debugging etc and that it is slow, each
/// call takes O(# libraries).
bool isSourceLibraryForDebugging(Library library) {
return loader.libraries.contains(library);
}
void readPatchFiles(SourceLibraryBuilder library) {
assert(library.importUri.isScheme("dart"));
List<Uri>? patches = uriTranslator.getDartPatches(library.importUri.path);
if (patches != null) {
SourceLibraryBuilder? first;
for (Uri patch in patches) {
if (first == null) {
first = library.loader.read(patch, -1,
fileUri: patch,
origin: library,
accessor: library) as SourceLibraryBuilder;
} else {
// If there's more than one patch file, it's interpreted as a part of
// the patch library.
SourceLibraryBuilder part = library.loader.read(patch, -1,
origin: library,
fileUri: patch,
accessor: library) as SourceLibraryBuilder;
first.parts.add(part);
first.partOffsets.add(-1);
part.partOfUri = first.importUri;
}
}
}
}
void releaseAncillaryResources() {
component = null;
}
void markLibrariesUsed(Set<Library> visitedLibraries) {
// Default implementation does nothing.
}
}
/// Looks for a constructor call that matches `super()` from a constructor in
/// [cls]. Such a constructor may have optional arguments, but no required
/// arguments.
Constructor? defaultSuperConstructor(Class cls) {
Class? superclass = cls.superclass;
if (superclass != null) {
for (Constructor constructor in superclass.constructors) {
if (constructor.name.text.isEmpty) {
return constructor.function.requiredParameterCount == 0
? constructor
: null;
}
}
}
return null;
}
class KernelDiagnosticReporter
extends DiagnosticReporter<Message, LocatedMessage> {
final SourceLoader loader;
KernelDiagnosticReporter(this.loader);
@override
void report(Message message, int charOffset, int length, Uri? fileUri,
{List<LocatedMessage>? context}) {
loader.addProblem(message, charOffset, noLength, fileUri, context: context);
}
}
class BuildResult {
final Component? component;
final NeededPrecompilations? neededPrecompilations;
final MacroApplications? macroApplications;
BuildResult(
{this.component, this.macroApplications, this.neededPrecompilations});
}