| // 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. |
| |
| // @dart = 2.9 |
| |
| library fasta.kernel_target; |
| |
| import 'package:front_end/src/api_prototype/experimental_flags.dart'; |
| import 'package:front_end/src/fasta/dill/dill_library_builder.dart' |
| show DillLibraryBuilder; |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/class_hierarchy.dart' show ClassHierarchy; |
| import 'package:kernel/clone.dart' show CloneVisitorNotMembers; |
| 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; |
| import 'package:kernel/transformations/value_class.dart' as valueClass; |
| import 'package:kernel/type_algebra.dart' show substitute; |
| import 'package:kernel/type_environment.dart' show TypeEnvironment; |
| import 'package:package_config/package_config.dart' hide LanguageVersion; |
| |
| import '../../api_prototype/file_system.dart' show FileSystem; |
| import '../../base/nnbd_mode.dart'; |
| 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_member_builder.dart' show DillMemberBuilder; |
| import '../dill/dill_target.dart' show DillTarget; |
| import '../fasta_codes.dart' show LocatedMessage, Message; |
| import '../loader.dart' show Loader; |
| import '../messages.dart' |
| show |
| FormattedMessage, |
| messageAgnosticWithStrongDillLibrary, |
| messageAgnosticWithWeakDillLibrary, |
| messageConstConstructorLateFinalFieldCause, |
| messageConstConstructorLateFinalFieldError, |
| messageConstConstructorNonFinalField, |
| messageConstConstructorNonFinalFieldCause, |
| messageConstConstructorRedirectionToNonConst, |
| messageInvalidNnbdDillLibrary, |
| messageStrongWithWeakDillLibrary, |
| messageWeakWithStrongDillLibrary, |
| noLength, |
| templateFieldNonNullableNotInitializedByConstructorError, |
| templateFieldNonNullableWithoutInitializerError, |
| templateFinalFieldNotInitialized, |
| templateFinalFieldNotInitializedByConstructor, |
| templateInferredPackageUri, |
| templateMissingImplementationCause, |
| templateSuperclassHasNoDefaultConstructor; |
| import '../problems.dart' show unhandled; |
| import '../scope.dart' show AmbiguousBuilder; |
| import '../source/source_class_builder.dart' show SourceClassBuilder; |
| import '../source/source_library_builder.dart' |
| show LanguageVersion, SourceLibraryBuilder; |
| import '../source/source_loader.dart' show SourceLoader; |
| import '../target_implementation.dart' show TargetImplementation; |
| import '../uri_translator.dart' show UriTranslator; |
| import 'constant_evaluator.dart' as constants |
| show |
| EvaluationMode, |
| transformLibraries, |
| transformProcedure, |
| ConstantCoverage; |
| import 'kernel_constants.dart' show KernelConstantErrorReporter; |
| import 'verifier.dart' show verifyComponent, verifyGetStaticType; |
| |
| class KernelTarget extends TargetImplementation { |
| /// 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; |
| |
| 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.nullable(), |
| /* arguments = */ null, |
| /* fileUri = */ null, |
| /* charOffset = */ null); |
| |
| final NamedTypeBuilder objectType = new NamedTypeBuilder( |
| "Object", |
| const NullabilityBuilder.omitted(), |
| /* arguments = */ null, |
| /* fileUri = */ null, |
| /* charOffset = */ null); |
| |
| // Null is always nullable. |
| // TODO(johnniwinther): This could (maybe) use a FixedTypeBuilder when we |
| // have NullType? |
| final TypeBuilder nullType = new NamedTypeBuilder( |
| "Null", |
| const NullabilityBuilder.nullable(), |
| /* arguments = */ null, |
| /* fileUri = */ null, |
| /* charOffset = */ null); |
| |
| // TODO(johnniwinther): Why isn't this using a FixedTypeBuilder? |
| final TypeBuilder bottomType = new NamedTypeBuilder( |
| "Never", |
| const NullabilityBuilder.omitted(), |
| /* arguments = */ null, |
| /* fileUri = */ null, |
| /* charOffset = */ null); |
| |
| 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<ClonedFunctionNode> clonedFunctionNodes = <ClonedFunctionNode>[]; |
| |
| KernelTarget(this.fileSystem, this.includeComments, DillTarget dillTarget, |
| UriTranslator uriTranslator) |
| : dillTarget = dillTarget, |
| super(dillTarget.ticker, uriTranslator, dillTarget.backendTarget) { |
| loader = createLoader(); |
| } |
| |
| 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.read(translatedEntryPoint, -1, |
| accessor: loader.first, |
| 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)) { |
| if (issueProblem) { |
| loader.addProblem( |
| templateInferredPackageUri.withArguments(reversed), |
| -1, |
| 1, |
| entryPoint); |
| } |
| entryPoint = reversed; |
| break; |
| } |
| } |
| } |
| } |
| return entryPoint; |
| } |
| |
| @override |
| LibraryBuilder createLibraryBuilder( |
| Uri uri, |
| Uri fileUri, |
| Uri packageUri, |
| LanguageVersion packageLanguageVersion, |
| SourceLibraryBuilder origin, |
| Library referencesFrom, |
| bool referenceIsPartOwner) { |
| if (dillTarget.isLoaded) { |
| LibraryBuilder builder = dillTarget.loader.builders[uri]; |
| if (builder != null) { |
| if (!builder.isNonNullableByDefault && |
| (loader.nnbdMode == NnbdMode.Strong || |
| loader.nnbdMode == NnbdMode.Agnostic)) { |
| loader.registerStrongOptOutLibrary(builder); |
| } else if (builder is DillLibraryBuilder) { |
| NonNullableByDefaultCompiledMode libraryMode = |
| builder.library.nonNullableByDefaultCompiledMode; |
| if (libraryMode == NonNullableByDefaultCompiledMode.Invalid) { |
| loader.registerNnbdMismatchLibrary( |
| builder, messageInvalidNnbdDillLibrary); |
| } else { |
| switch (loader.nnbdMode) { |
| case NnbdMode.Weak: |
| if (libraryMode != NonNullableByDefaultCompiledMode.Agnostic && |
| libraryMode != NonNullableByDefaultCompiledMode.Weak) { |
| loader.registerNnbdMismatchLibrary( |
| builder, messageWeakWithStrongDillLibrary); |
| } |
| break; |
| case NnbdMode.Strong: |
| if (libraryMode != NonNullableByDefaultCompiledMode.Agnostic && |
| libraryMode != NonNullableByDefaultCompiledMode.Strong) { |
| loader.registerNnbdMismatchLibrary( |
| builder, messageStrongWithWeakDillLibrary); |
| } |
| break; |
| case NnbdMode.Agnostic: |
| if (libraryMode != NonNullableByDefaultCompiledMode.Agnostic) { |
| if (libraryMode == NonNullableByDefaultCompiledMode.Strong) { |
| loader.registerNnbdMismatchLibrary( |
| builder, messageAgnosticWithStrongDillLibrary); |
| } else { |
| loader.registerNnbdMismatchLibrary( |
| builder, messageAgnosticWithWeakDillLibrary); |
| } |
| } |
| break; |
| } |
| } |
| } |
| return builder; |
| } |
| } |
| return new SourceLibraryBuilder( |
| uri, fileUri, packageUri, packageLanguageVersion, loader, origin, |
| referencesFrom: referencesFrom, |
| referenceIsPartOwner: referenceIsPartOwner); |
| } |
| |
| /// Returns classes defined in libraries in [loader]. |
| List<SourceClassBuilder> collectMyClasses() { |
| List<SourceClassBuilder> result = <SourceClassBuilder>[]; |
| for (LibraryBuilder library in loader.builders.values) { |
| if (library.loader == loader) { |
| Iterator<Builder> iterator = library.iterator; |
| while (iterator.moveNext()) { |
| Builder member = iterator.current; |
| if (member is SourceClassBuilder && !member.isPatch) { |
| result.add(member); |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| 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) |
| ..bind(objectClassBuilder); |
| builder.interfaceBuilders = null; |
| builder.mixedInTypeBuilder = null; |
| } |
| |
| @override |
| Future<Component> buildOutlines({CanonicalName nameRoot}) async { |
| if (loader.first == null) return null; |
| return withCrashReporting<Component>(() async { |
| await loader.buildOutlines(); |
| loader.createTypeInferenceEngine(); |
| loader.coreLibrary.becomeCoreLibrary(); |
| loader.resolveParts(); |
| loader.computeLibraryScopes(); |
| setupTopAndBottomTypes(); |
| loader.resolveTypes(); |
| loader.computeVariances(); |
| loader.computeDefaultTypes( |
| dynamicType, nullType, bottomType, objectClassBuilder); |
| List<SourceClassBuilder> myClasses = |
| loader.checkSemantics(objectClassBuilder); |
| loader.finishTypeVariables(objectClassBuilder, dynamicType); |
| loader.buildComponent(); |
| installDefaultSupertypes(); |
| installSyntheticConstructors(myClasses); |
| loader.resolveConstructors(); |
| component = |
| link(new List<Library>.from(loader.libraries), nameRoot: nameRoot); |
| computeCoreTypes(); |
| loader.buildClassHierarchy(myClasses, objectClassBuilder); |
| loader.computeHierarchy(); |
| loader.performTopLevelInference(myClasses); |
| loader.checkSupertypes(myClasses); |
| loader.checkOverrides(myClasses); |
| loader.checkAbstractMembers(myClasses); |
| loader.addNoSuchMethodForwarders(myClasses); |
| loader.checkMixins(myClasses); |
| loader.buildOutlineExpressions(loader.coreTypes); |
| loader.checkTypes(); |
| loader.checkRedirectingFactories(myClasses); |
| loader.checkMainMethods(); |
| _updateDelayedParameterTypes(); |
| installAllComponentProblems(loader.allComponentProblems); |
| loader.allComponentProblems.clear(); |
| return component; |
| }, () => 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. |
| @override |
| Future<Component> buildComponent({bool verify: false}) async { |
| if (loader.first == null) return null; |
| return withCrashReporting<Component>(() async { |
| ticker.logMs("Building component"); |
| await loader.buildBodies(); |
| finishClonedParameters(); |
| loader.finishDeferredLoadTearoffs(); |
| loader.finishNoSuchMethodForwarders(); |
| List<SourceClassBuilder> myClasses = collectMyClasses(); |
| loader.finishNativeMethods(); |
| loader.finishPatchMethods(); |
| finishAllConstructors(myClasses); |
| runBuildTransformations(); |
| |
| if (verify) this.verify(); |
| installAllComponentProblems(loader.allComponentProblems); |
| return component; |
| }, () => 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, null); |
| if (declaration is AmbiguousBuilder) { |
| AmbiguousBuilder problem = declaration; |
| declaration = problem.getFirstDeclaration(); |
| } |
| if (declaration is ProcedureBuilder) { |
| mainReference = declaration.actualProcedure?.reference; |
| } else if (declaration is DillMemberBuilder) { |
| if (declaration.member is Procedure) { |
| mainReference = declaration.member?.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 (LibraryBuilder library in loader.builders.values) { |
| if (library.loader == loader) { |
| Iterator<Builder> iterator = library.iterator; |
| while (iterator.moveNext()) { |
| Builder declaration = iterator.current; |
| if (declaration is SourceClassBuilder) { |
| Class cls = declaration.cls; |
| if (cls != objectClass) { |
| cls.supertype ??= objectClass.asRawSupertype; |
| declaration.supertypeBuilder ??= new NamedTypeBuilder( |
| "Object", |
| const NullabilityBuilder.omitted(), |
| /* arguments = */ null, |
| /* fileUri = */ null, |
| /* charOffset = */ null) |
| ..bind(objectClassBuilder); |
| } |
| if (declaration.isMixinApplication) { |
| cls.mixedInType = declaration.mixedInTypeBuilder.buildMixedInType( |
| library, declaration.charOffset, declaration.fileUri); |
| } |
| } |
| } |
| } |
| } |
| 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"); |
| } |
| |
| List<DelayedParameterType> _delayedParameterTypes = <DelayedParameterType>[]; |
| |
| /// Update the type of parameters cloned from parameters with inferred |
| /// parameter types. |
| void _updateDelayedParameterTypes() { |
| for (DelayedParameterType delayedParameterType in _delayedParameterTypes) { |
| delayedParameterType.updateType(); |
| } |
| _delayedParameterTypes.clear(); |
| } |
| |
| ClassBuilder get objectClassBuilder => objectType.declaration; |
| |
| Class get objectClass => objectClassBuilder.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.redirectingFactoryConstructors.isNotEmpty) return; |
| for (Procedure proc in builder.cls.procedures) { |
| if (proc.isFactory) return; |
| } |
| |
| IndexedClass indexedClass = builder.referencesFromIndexed; |
| Constructor referenceFrom; |
| if (indexedClass != null) { |
| referenceFrom = indexedClass.lookupConstructor(new Name("")); |
| } |
| |
| /// 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, referenceFrom)); |
| } |
| |
| 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; |
| Constructor referenceFrom; |
| if (indexedClass != null) { |
| referenceFrom = indexedClass.lookupConstructor(new Name("")); |
| } |
| |
| 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); |
| Constructor referenceFrom = indexedClass |
| ?.lookupConstructor(new Name(name, indexedClass.library)); |
| builder.addSyntheticConstructor(_makeMixinApplicationConstructor( |
| builder, |
| builder.cls.mixin, |
| memberBuilder, |
| substitutionMap, |
| referenceFrom)); |
| isConstructorAdded = true; |
| } |
| } |
| |
| superclassBuilder.forEachConstructor(addSyntheticConstructor, |
| includeInjectedConstructors: true); |
| |
| if (!isConstructorAdded) { |
| builder.addSyntheticConstructor( |
| _makeDefaultConstructor(builder, referenceFrom)); |
| } |
| } else if (supertype is InvalidTypeDeclarationBuilder || |
| supertype is TypeVariableBuilder || |
| supertype is DynamicTypeDeclarationBuilder || |
| supertype is VoidTypeDeclarationBuilder || |
| supertype is NeverTypeDeclarationBuilder) { |
| builder.addSyntheticConstructor( |
| _makeDefaultConstructor(builder, referenceFrom)); |
| } else { |
| unhandled("${supertype.runtimeType}", "installForwardingConstructors", |
| builder.charOffset, builder.fileUri); |
| } |
| } |
| |
| SyntheticConstructorBuilder _makeMixinApplicationConstructor( |
| SourceClassBuilder classBuilder, |
| Class mixin, |
| MemberBuilderImpl memberBuilder, |
| Map<TypeParameter, DartType> substitutionMap, |
| Constructor referenceFrom) { |
| VariableDeclaration copyFormal(VariableDeclaration formal) { |
| VariableDeclaration copy = new VariableDeclaration(formal.name, |
| isFinal: formal.isFinal, isConst: formal.isConst); |
| if (formal.type != null) { |
| copy.type = substitute(formal.type, substitutionMap); |
| } else { |
| _delayedParameterTypes |
| .add(new DelayedParameterType(formal, copy, substitutionMap)); |
| } |
| return copy; |
| } |
| |
| Class cls = classBuilder.cls; |
| Constructor constructor = memberBuilder.member; |
| bool isConst = constructor.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 constructor.function.positionalParameters) { |
| positionalParameters.add(copyFormal(formal)); |
| positional.add(new VariableGet(positionalParameters.last)); |
| } |
| for (VariableDeclaration formal in constructor.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: constructor.function.requiredParameterCount, |
| returnType: makeConstructorReturnType(cls)); |
| SuperInitializer initializer = new SuperInitializer( |
| constructor, new Arguments(positional, named: named)); |
| ClonedFunctionNode clonedFunctionNode = |
| new ClonedFunctionNode(substitutionMap, constructor.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 [clonedFunctionNodes]. |
| clonedFunctionNodes.add(clonedFunctionNode); |
| } |
| return new SyntheticConstructorBuilder( |
| classBuilder, |
| new Constructor(function, |
| name: constructor.name, |
| initializers: <Initializer>[initializer], |
| isSynthetic: true, |
| isConst: isConst, |
| reference: referenceFrom?.reference, |
| fileUri: cls.fileUri) |
| ..isNonNullableByDefault = |
| cls.enclosingLibrary.isNonNullableByDefault, |
| // If the constructor is constant, the default values must be part of |
| // the outline expressions. We pass on the original constructor and |
| // cloned function nodes to ensure that the default values are computed |
| // and cloned for the outline. |
| origin: isConst ? memberBuilder : null, |
| clonedFunctionNode: isConst ? clonedFunctionNode : null); |
| } |
| |
| void finishClonedParameters() { |
| for (ClonedFunctionNode clonedFunctionNode in clonedFunctionNodes) { |
| clonedFunctionNode.cloneDefaultValues(); |
| } |
| clonedFunctionNodes.clear(); |
| ticker.logMs("Cloned default values of formals"); |
| } |
| |
| SyntheticConstructorBuilder _makeDefaultConstructor( |
| SourceClassBuilder classBuilder, Constructor referenceFrom) { |
| Class enclosingClass = classBuilder.cls; |
| return new SyntheticConstructorBuilder( |
| classBuilder, |
| new Constructor( |
| new FunctionNode(new EmptyStatement(), |
| returnType: makeConstructorReturnType(enclosingClass)), |
| name: new Name(""), |
| isSynthetic: true, |
| reference: referenceFrom?.reference, |
| fileUri: enclosingClass.fileUri) |
| ..isNonNullableByDefault = |
| enclosingClass.enclosingLibrary.isNonNullableByDefault); |
| } |
| |
| 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)); |
| dynamicType |
| .bind(loader.coreLibrary.lookupLocalMember("dynamic", required: true)); |
| ClassBuilder nullClassBuilder = |
| loader.coreLibrary.lookupLocalMember("Null", required: true); |
| nullType.bind(nullClassBuilder..isNullClass = true); |
| bottomType |
| .bind(loader.coreLibrary.lookupLocalMember("Never", required: true)); |
| } |
| |
| 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.builders[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<FieldBuilder> uninitializedFields = <FieldBuilder>[]; |
| List<FieldBuilder> nonFinalFields = <FieldBuilder>[]; |
| List<FieldBuilder> lateFinalFields = <FieldBuilder>[]; |
| |
| builder.forEachDeclaredField((String name, FieldBuilder 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. |
| FieldBuilder earliest = fieldBuilder; |
| Builder current = fieldBuilder; |
| while (current.next != null) { |
| current = current.next; |
| if (current is FieldBuilder && !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.constructors.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(); |
| constructor.function.body.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<FieldBuilder>> constructorInitializedFields = |
| new Map<ConstructorBuilder, Set<FieldBuilder>>.identity(); |
| Set<FieldBuilder> initializedFields = null; |
| |
| builder.forEachDeclaredConstructor( |
| (String name, ConstructorBuilder 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. |
| ConstructorBuilder earliest = constructorBuilder; |
| while (earliest.next != null) { |
| earliest = earliest.next; |
| } |
| |
| bool isRedirecting = false; |
| for (Initializer initializer in earliest.constructor.initializers) { |
| if (initializer is RedirectingInitializer) { |
| isRedirecting = true; |
| } |
| } |
| if (!isRedirecting) { |
| Set<FieldBuilder> fields = earliest.takeInitializedFields() ?? const {}; |
| constructorInitializedFields[earliest] = fields; |
| (initializedFields ??= new Set<FieldBuilder>.identity()).addAll(fields); |
| } |
| }); |
| |
| // Run through all fields that aren't initialized by any constructor, and |
| // set their initializer to `null`. |
| for (FieldBuilder 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 (FieldBuilder 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, FieldBuilder fieldBuilder) { |
| patchFieldNames.add(SourceFieldBuilder.createFieldName( |
| FieldNameType.Field, |
| name, |
| isInstanceMember: fieldBuilder.isClassInstanceMember, |
| className: builder.name, |
| isSynthesized: |
| fieldBuilder is SourceFieldBuilder && 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.ConstantCoverage coverage = constants.transformLibraries( |
| loader.libraries, |
| backendTarget.constantsBackend(loader.coreTypes), |
| environmentDefines, |
| environment, |
| new KernelConstantErrorReporter(loader), |
| evaluationMode, |
| evaluateAnnotations: true, |
| enableTripleShift: |
| isExperimentEnabledGlobally(ExperimentalFlag.tripleShift), |
| enableConstFunctions: |
| isExperimentEnabledGlobally(ExperimentalFlag.constFunctions), |
| errorOnUnevaluatedConstant: errorOnUnevaluatedConstant); |
| ticker.logMs("Evaluated constants"); |
| |
| coverage.constructorCoverage.forEach((Uri fileUri, Set<Reference> value) { |
| Source source = uriToSource[fileUri]; |
| 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, 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.constantsBackend(loader.coreTypes), |
| environmentDefines, |
| environment, |
| new KernelConstantErrorReporter(loader), |
| evaluationMode, |
| evaluateAnnotations: true, |
| enableTripleShift: |
| isExperimentEnabledGlobally(ExperimentalFlag.tripleShift), |
| enableConstFunctions: |
| isExperimentEnabledGlobally(ExperimentalFlag.constFunctions), |
| 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, |
| 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); |
| } |
| |
| @override |
| void readPatchFiles(SourceLibraryBuilder library) { |
| assert(library.importUri.scheme == "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); |
| } 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); |
| first.parts.add(part); |
| first.partOffsets.add(-1); |
| part.partOfUri = first.importUri; |
| } |
| } |
| } |
| } |
| |
| void releaseAncillaryResources() { |
| component = null; |
| } |
| } |
| |
| /// 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 Loader loader; |
| |
| KernelDiagnosticReporter(this.loader); |
| |
| void report(Message message, int charOffset, int length, Uri fileUri, |
| {List<LocatedMessage> context}) { |
| loader.addProblem(message, charOffset, noLength, fileUri, context: context); |
| } |
| } |
| |
| /// Data for updating cloned parameters of parameters with inferred parameter |
| /// types. |
| /// |
| /// The type of [source] is not declared so the type of [target] needs to be |
| /// updated when the type of [source] has been inferred. |
| class DelayedParameterType { |
| final VariableDeclaration source; |
| final VariableDeclaration target; |
| final Map<TypeParameter, DartType> substitutionMap; |
| |
| DelayedParameterType(this.source, this.target, this.substitutionMap); |
| |
| void updateType() { |
| assert(source.type != null, "No type computed for $source."); |
| target.type = substitute(source.type, substitutionMap); |
| } |
| } |
| |
| class ClonedFunctionNode { |
| final Map<TypeParameter, DartType> _typeSubstitution; |
| final FunctionNode _original; |
| final FunctionNode _clone; |
| |
| ClonedFunctionNode(this._typeSubstitution, this._original, this._clone); |
| |
| void cloneDefaultValues() { |
| // TODO(ahe): It is unclear if it is legal to use type variables in |
| // default values, but Fasta is currently allowing it, and the VM |
| // accepts it. If it isn't legal, the we can speed this up by using a |
| // single cloner without substitution. |
| CloneVisitorNotMembers cloner; |
| |
| void cloneInitializer(VariableDeclaration originalParameter, |
| VariableDeclaration clonedParameter) { |
| if (originalParameter.initializer != null) { |
| cloner ??= |
| new CloneVisitorNotMembers(typeSubstitution: _typeSubstitution); |
| clonedParameter.initializer = cloner |
| .clone(originalParameter.initializer) |
| ..parent = clonedParameter; |
| } |
| } |
| |
| for (int i = 0; i < _original.positionalParameters.length; i++) { |
| cloneInitializer( |
| _original.positionalParameters[i], _clone.positionalParameters[i]); |
| } |
| |
| for (int i = 0; i < _original.namedParameters.length; i++) { |
| cloneInitializer(_original.namedParameters[i], _clone.namedParameters[i]); |
| } |
| } |
| } |