// 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 'dart:async' show Future;

import 'package:kernel/ast.dart'
    show
        Arguments,
        CanonicalName,
        Class,
        Component,
        Constructor,
        DartType,
        EmptyStatement,
        Expression,
        Field,
        FieldInitializer,
        FunctionNode,
        Initializer,
        InterfaceType,
        InvalidInitializer,
        Library,
        ListLiteral,
        Name,
        NamedExpression,
        NullLiteral,
        Procedure,
        RedirectingInitializer,
        Source,
        StringLiteral,
        SuperInitializer,
        TypeParameter,
        TypeParameterType,
        VariableDeclaration,
        VariableGet;

import 'package:kernel/clone.dart' show CloneVisitor;

import 'package:kernel/type_algebra.dart' show substitute;

import '../../api_prototype/file_system.dart' show FileSystem;

import '../compiler_context.dart' show CompilerContext;

import '../crash.dart' show withCrashReporting;

import '../dill/dill_target.dart' show DillTarget;

import '../dill/dill_member_builder.dart' show DillMemberBuilder;

import '../messages.dart'
    show
        LocatedMessage,
        messageConstConstructorNonFinalField,
        messageConstConstructorNonFinalFieldCause,
        messageConstConstructorRedirectionToNonConst,
        noLength,
        templateFinalFieldNotInitialized,
        templateFinalFieldNotInitializedByConstructor,
        templateInferredPackageUri,
        templateMissingImplementationCause,
        templateSuperclassHasNoDefaultConstructor;

import '../problems.dart' show unhandled;

import '../severity.dart' show Severity;

import '../scope.dart' show AmbiguousBuilder;

import '../source/source_class_builder.dart' show SourceClassBuilder;

import '../source/source_loader.dart' show SourceLoader;

import '../target_implementation.dart' show TargetImplementation;

import '../uri_translator.dart' show UriTranslator;

import 'kernel_builder.dart'
    show
        ClassBuilder,
        Declaration,
        InvalidTypeBuilder,
        KernelClassBuilder,
        KernelFieldBuilder,
        KernelLibraryBuilder,
        KernelNamedTypeBuilder,
        KernelProcedureBuilder,
        LibraryBuilder,
        NamedTypeBuilder,
        TypeBuilder,
        TypeDeclarationBuilder;

import 'metadata_collector.dart' show MetadataCollector;

import 'verifier.dart' show verifyComponent;

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;

  /// Shared with [CompilerContext].
  final Map<Uri, Source> uriToSource;

  /// The [MetadataCollector] to write metadata to.
  final MetadataCollector metadataCollector;

  SourceLoader<Library> loader;

  Component component;

  final TypeBuilder dynamicType = new KernelNamedTypeBuilder("dynamic", null);

  final NamedTypeBuilder objectType =
      new KernelNamedTypeBuilder("Object", null);

  final TypeBuilder bottomType = new KernelNamedTypeBuilder("Null", null);

  bool get legacyMode => backendTarget.legacyMode;

  bool get disableTypeInference => backendTarget.disableTypeInference;

  final bool excludeSource = !CompilerContext.current.options.embedSourceText;

  final List<Object> clonedFormals = <Object>[];

  KernelTarget(this.fileSystem, this.includeComments, DillTarget dillTarget,
      UriTranslator uriTranslator,
      {MetadataCollector metadataCollector})
      : dillTarget = dillTarget,
        uriToSource = CompilerContext.current.uriToSource,
        metadataCollector = metadataCollector,
        super(dillTarget.ticker, uriTranslator, dillTarget.backendTarget) {
    loader = createLoader();
  }

  SourceLoader<Library> createLoader() =>
      new SourceLoader<Library>(fileSystem, includeComments, this);

  void addSourceInformation(
      Uri uri, List<int> lineStarts, List<int> sourceCode) {
    uriToSource[uri] = new Source(lineStarts, sourceCode);
  }

  void setEntryPoints(List<Uri> entryPoints) {
    Map<String, Uri> packagesMap;
    for (Uri entryPoint in entryPoints) {
      String scheme = entryPoint.scheme;
      Uri fileUri;
      switch (scheme) {
        case "package":
        case "dart":
        case "data":
          break;
        default:
          // Attempt to reverse-lookup [entryPoint] in package config.
          String asString = "$entryPoint";
          packagesMap ??= uriTranslator.packages.asMap();
          for (String packageName in packagesMap.keys) {
            String prefix = "${packagesMap[packageName]}";
            if (asString.startsWith(prefix)) {
              Uri reversed = Uri.parse(
                  "package:$packageName/${asString.substring(prefix.length)}");
              if (entryPoint == uriTranslator.translate(reversed)) {
                loader.addProblem(
                    templateInferredPackageUri.withArguments(reversed),
                    -1,
                    1,
                    entryPoint);
                fileUri = entryPoint;
                entryPoint = reversed;
                break;
              }
            }
          }
      }
      loader.read(entryPoint, -1, accessor: loader.first, fileUri: fileUri);
    }
  }

  @override
  LibraryBuilder createLibraryBuilder(
      Uri uri, Uri fileUri, KernelLibraryBuilder origin) {
    if (dillTarget.isLoaded) {
      var builder = dillTarget.loader.builders[uri];
      if (builder != null) {
        return builder;
      }
    }
    return new KernelLibraryBuilder(uri, fileUri, loader, origin);
  }

  void forEachDirectSupertype(ClassBuilder cls, void f(NamedTypeBuilder type)) {
    TypeBuilder supertype = cls.supertype;
    if (supertype is NamedTypeBuilder) {
      f(supertype);
    } else if (supertype != null) {
      unhandled("${supertype.runtimeType}", "forEachDirectSupertype",
          cls.charOffset, cls.fileUri);
    }
    if (cls.interfaces != null) {
      for (NamedTypeBuilder t in cls.interfaces) {
        f(t);
      }
    }
    if (cls.library.loader == loader &&
        // TODO(ahe): Implement DillClassBuilder.mixedInType and remove the
        // above check.
        cls.mixedInType != null) {
      f(cls.mixedInType);
    }
  }

  void addDirectSupertype(ClassBuilder cls, Set<ClassBuilder> set) {
    if (cls == null) return;
    forEachDirectSupertype(cls, (NamedTypeBuilder type) {
      Declaration declaration = type.declaration;
      if (declaration is ClassBuilder) {
        set.add(declaration);
      }
    });
  }

  /// Returns classes defined in libraries in [loader].
  List<SourceClassBuilder> collectMyClasses() {
    List<SourceClassBuilder> result = <SourceClassBuilder>[];
    loader.builders.forEach((Uri uri, LibraryBuilder library) {
      if (library.loader == loader) {
        library.forEach((String name, Declaration member) {
          if (member is SourceClassBuilder && !member.isPatch) {
            result.add(member);
          }
        });
      }
    });
    return result;
  }

  void breakCycle(ClassBuilder builder) {
    Class cls = builder.target;
    cls.implementedTypes.clear();
    cls.supertype = null;
    cls.mixedInType = null;
    builder.supertype = new KernelNamedTypeBuilder("Object", null)
      ..bind(objectClassBuilder);
    builder.interfaces = null;
    builder.mixedInType = null;
  }

  @override
  Future<Component> buildOutlines({CanonicalName nameRoot}) async {
    if (loader.first == null) return null;
    return withCrashReporting<Component>(() async {
      loader.createTypeInferenceEngine();
      await loader.buildOutlines();
      loader.coreLibrary.becomeCoreLibrary();
      dynamicType.bind(loader.coreLibrary["dynamic"]);
      loader.resolveParts();
      loader.computeLibraryScopes();
      objectType.bind(loader.coreLibrary["Object"]);
      bottomType.bind(loader.coreLibrary["Null"]);
      loader.resolveTypes();
      loader.computeDefaultTypes(dynamicType, bottomType, objectClassBuilder);
      List<SourceClassBuilder> myClasses = collectMyClasses();
      loader.checkSemantics(myClasses);
      loader.finishTypeVariables(objectClassBuilder, dynamicType);
      loader.buildComponent();
      installDefaultSupertypes();
      installSyntheticConstructors(myClasses);
      loader.resolveConstructors();
      component =
          link(new List<Library>.from(loader.libraries), nameRoot: nameRoot);
      computeCoreTypes();
      loader.computeHierarchy();
      loader.performTopLevelInference(myClasses);
      loader.checkSupertypes(myClasses);
      loader.checkBounds();
      loader.checkOverrides(myClasses);
      loader.checkAbstractMembers(myClasses);
      loader.checkRedirectingFactories(myClasses);
      loader.addNoSuchMethodForwarders(myClasses);
      loader.checkMixins(myClasses);
      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();
      handleRecoverableErrors(loader.unhandledErrors);
      return component;
    }, () => loader?.currentUriForCrashReporting);
  }

  /// Adds a synthetic field named `#errors` to the main library that contains
  /// [recoverableErrors] formatted.
  ///
  /// If [recoverableErrors] is empty, this method does nothing.
  void handleRecoverableErrors(List<LocatedMessage> recoverableErrors) {
    if (recoverableErrors.isEmpty) return;
    KernelLibraryBuilder mainLibrary = loader.first;
    if (mainLibrary == null) return;
    List<Expression> expressions = <Expression>[];
    for (LocatedMessage error in recoverableErrors) {
      expressions.add(new StringLiteral(context.format(error, Severity.error)));
    }
    mainLibrary.library.addMember(new Field(new Name("#errors"),
        initializer: new ListLiteral(expressions, isConst: true),
        isConst: true,
        isStatic: true));
  }

  /// 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;
    }

    this.uriToSource.forEach(copySource);

    Component component = CompilerContext.current.options.target
        .configureComponent(new Component(
            nameRoot: nameRoot,
            libraries: libraries,
            uriToSource: uriToSource));
    if (loader.first != null) {
      // TODO(sigmund): do only for full program
      Declaration declaration =
          loader.first.exportScope.lookup("main", -1, null);
      if (declaration is AmbiguousBuilder) {
        AmbiguousBuilder problem = declaration;
        declaration = problem.getFirstDeclaration();
      }
      if (declaration is KernelProcedureBuilder) {
        component.mainMethod = declaration.procedure;
      } else if (declaration is DillMemberBuilder) {
        if (declaration.member is Procedure) {
          component.mainMethod = declaration.member;
        }
      }
    }

    if (metadataCollector != null) {
      component.addMetadataRepository(metadataCollector.repository);
    }

    ticker.logMs("Linked component");
    return component;
  }

  void installDefaultSupertypes() {
    Class objectClass = this.objectClass;
    loader.builders.forEach((Uri uri, LibraryBuilder library) {
      if (library.loader == loader) {
        library.forEach((String name, Declaration declaration) {
          while (declaration != null) {
            if (declaration is SourceClassBuilder) {
              Class cls = declaration.target;
              if (cls != objectClass) {
                cls.supertype ??= objectClass.asRawSupertype;
                declaration.supertype ??=
                    new KernelNamedTypeBuilder("Object", null)
                      ..bind(objectClassBuilder);
              }
              if (declaration.isMixinApplication) {
                cls.mixedInType = declaration.mixedInType.buildMixedInType(
                    library, declaration.charOffset, declaration.fileUri);
              }
            }
            declaration = declaration.next;
          }
        });
      }
    });
    ticker.logMs("Installed Object as implicit superclass");
  }

  void installSyntheticConstructors(List<SourceClassBuilder> builders) {
    Class objectClass = this.objectClass;
    for (SourceClassBuilder builder in builders) {
      if (builder.target != objectClass) {
        if (builder.isPatch || builder.isMixinDeclaration) continue;
        if (builder.isMixinApplication) {
          installForwardingConstructors(builder);
        } else {
          installDefaultConstructor(builder);
        }
      }
    }
    ticker.logMs("Installed synthetic constructors");
  }

  KernelClassBuilder 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);
    // TODO(askesc): Make this check light-weight in the absence of patches.
    if (builder.target.constructors.isNotEmpty) return;
    if (builder.target.redirectingFactoryConstructors.isNotEmpty) return;
    for (Procedure proc in builder.target.procedures) {
      if (proc.isFactory) return;
    }

    /// 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.target));
  }

  void installForwardingConstructors(SourceClassBuilder builder) {
    assert(builder.isMixinApplication);
    if (builder.library.loader != loader) return;
    if (builder.target.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.supertype;
    TypeDeclarationBuilder supertype;
    if (type is NamedTypeBuilder) {
      supertype = type.declaration;
    } else {
      unhandled("${type.runtimeType}", "installForwardingConstructors",
          builder.charOffset, builder.fileUri);
    }
    if (supertype.isMixinApplication) {
      installForwardingConstructors(supertype);
    }
    if (supertype is KernelClassBuilder) {
      if (supertype.cls.constructors.isEmpty) {
        builder.addSyntheticConstructor(makeDefaultConstructor(builder.target));
      } else {
        Map<TypeParameter, DartType> substitutionMap =
            builder.getSubstitutionMap(supertype.target);
        for (Constructor constructor in supertype.cls.constructors) {
          builder.addSyntheticConstructor(makeMixinApplicationConstructor(
              builder.target, builder.cls.mixin, constructor, substitutionMap));
        }
      }
    } else if (supertype is InvalidTypeBuilder) {
      builder.addSyntheticConstructor(makeDefaultConstructor(builder.target));
    } else {
      unhandled("${supertype.runtimeType}", "installForwardingConstructors",
          builder.charOffset, builder.fileUri);
    }
  }

  Constructor makeMixinApplicationConstructor(Class cls, Class mixin,
      Constructor constructor, Map<TypeParameter, DartType> substitutionMap) {
    VariableDeclaration copyFormal(VariableDeclaration formal) {
      // TODO(ahe): Handle initializers.
      var copy = new VariableDeclaration(formal.name,
          isFinal: formal.isFinal, isConst: formal.isConst);
      if (formal.type != null) {
        copy.type = substitute(formal.type, substitutionMap);
      }
      return copy;
    }

    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);
      clonedFormals..add(formal)..add(clone)..add(substitutionMap);
      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));
    return new Constructor(function,
        name: constructor.name,
        initializers: <Initializer>[initializer],
        isSynthetic: true);
  }

  void finishClonedParameters() {
    for (int i = 0; i < clonedFormals.length; i += 3) {
      // Note that [original] may itself be clone. If so, it was added to
      // [clonedFormals] before [clone], so it's initializers are already in
      // place.
      VariableDeclaration original = clonedFormals[i];
      VariableDeclaration clone = clonedFormals[i + 1];
      if (original.initializer != null) {
        // 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.
        CloneVisitor cloner =
            new CloneVisitor(typeSubstitution: clonedFormals[i + 2]);
        clone.initializer = cloner.clone(original.initializer)..parent = clone;
      }
    }
    clonedFormals.clear();
    ticker.logMs("Cloned default values of formals");
  }

  Constructor makeDefaultConstructor(Class enclosingClass) {
    return new Constructor(
        new FunctionNode(new EmptyStatement(),
            returnType: makeConstructorReturnType(enclosingClass)),
        name: new Name(""),
        isSynthetic: true);
  }

  DartType makeConstructorReturnType(Class enclosingClass) {
    List<DartType> typeParameterTypes = new List<DartType>();
    for (int i = 0; i < enclosingClass.typeParameters.length; i++) {
      TypeParameter typeParameter = enclosingClass.typeParameters[i];
      typeParameterTypes.add(new TypeParameterType(typeParameter));
    }
    return new InterfaceType(enclosingClass, typeParameterTypes);
  }

  void computeCoreTypes() {
    List<Library> libraries = <Library>[];
    for (String platformLibrary in const [
      "dart:_internal",
      "dart:async",
      "dart:core",
      "dart:mirrors"
    ]) {
      Uri uri = Uri.parse(platformLibrary);
      LibraryBuilder library = loader.builders[uri];
      if (library == 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(library.target);
      }
    }
    Component plaformLibraries = CompilerContext.current.options.target
        .configureComponent(new Component());
    // Add libraries directly to prevent that their parents are changed.
    plaformLibraries.libraries.addAll(libraries);
    loader.computeCoreTypes(plaformLibraries);
  }

  void finishAllConstructors(List<SourceClassBuilder> builders) {
    Class objectClass = this.objectClass;
    for (SourceClassBuilder builder in builders) {
      Class cls = builder.target;
      if (cls != objectClass) {
        finishConstructors(builder);
      }
    }
    ticker.logMs("Finished constructors");
  }

  /// Ensure constructors of [cls] have the correct initializers and other
  /// requirements.
  void finishConstructors(SourceClassBuilder builder) {
    if (builder.isPatch) return;
    Class cls = builder.target;

    /// Quotes below are from [Dart Programming Language Specification, 4th
    /// Edition](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-408.pdf):
    List<Field> uninitializedFields = <Field>[];
    List<Field> nonFinalFields = <Field>[];
    for (Field field in cls.fields) {
      if (field.isInstanceMember && !field.isFinal) {
        nonFinalFields.add(field);
      }
      if (field.initializer == null) {
        uninitializedFields.add(field);
      }
    }
    Map<Constructor, Set<Field>> constructorInitializedFields =
        <Constructor, Set<Field>>{};
    Constructor superTarget;
    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.name.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) {
            builder.addProblem(
                templateSuperclassHasNoDefaultConstructor
                    .withArguments(cls.superclass.name),
                constructor.fileOffset,
                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;
        }

        Set<Field> myInitializedFields = new Set<Field>();
        for (Initializer initializer in constructor.initializers) {
          if (initializer is FieldInitializer) {
            myInitializedFields.add(initializer.field);
          }
        }
        for (VariableDeclaration formal
            in constructor.function.positionalParameters) {
          if (formal.isFieldFormal) {
            Declaration fieldBuilder = builder.scope.local[formal.name] ??
                builder.origin.scope.local[formal.name];
            if (fieldBuilder is KernelFieldBuilder) {
              myInitializedFields.add(fieldBuilder.field);
            }
          }
        }
        constructorInitializedFields[constructor] = myInitializedFields;
        if (constructor.isConst && nonFinalFields.isNotEmpty) {
          builder.addProblem(messageConstConstructorNonFinalField,
              constructor.fileOffset, noLength,
              context: nonFinalFields
                  .map((field) => messageConstConstructorNonFinalFieldCause
                      .withLocation(field.fileUri, field.fileOffset, noLength))
                  .toList());
          nonFinalFields.clear();
        }
      }
    }
    Set<Field> initializedFields;
    constructorInitializedFields
        .forEach((Constructor constructor, Set<Field> fields) {
      if (initializedFields == null) {
        initializedFields = new Set<Field>.from(fields);
      } else {
        initializedFields.addAll(fields);
      }
    });

    // Run through all fields that aren't initialized by any constructor, and
    // set their initializer to `null`.
    for (Field field in uninitializedFields) {
      if (initializedFields == null || !initializedFields.contains(field)) {
        field.initializer = new NullLiteral()..parent = field;
        if (field.isFinal &&
            (cls.constructors.isNotEmpty || cls.isMixinDeclaration)) {
          builder.library.addProblem(
              templateFinalFieldNotInitialized.withArguments(field.name.name),
              field.fileOffset,
              field.name.name.length,
              field.fileUri);
        }
      }
    }

    // Run through all fields that are initialized by some constructor, and
    // make sure that all other constructors also initialize them.
    constructorInitializedFields
        .forEach((Constructor constructor, Set<Field> fields) {
      for (Field field in initializedFields.difference(fields)) {
        if (field.initializer == null) {
          FieldInitializer initializer =
              new FieldInitializer(field, new NullLiteral())
                ..isSynthetic = true;
          initializer.parent = constructor;
          constructor.initializers.insert(0, initializer);
          if (field.isFinal) {
            builder.library.addProblem(
                templateFinalFieldNotInitializedByConstructor
                    .withArguments(field.name.name),
                constructor.fileOffset,
                constructor.name.name.length,
                constructor.fileUri,
                context: [
                  templateMissingImplementationCause
                      .withArguments(field.name.name)
                      .withLocation(field.fileUri, field.fileOffset,
                          field.name.name.length)
                ]);
          }
        }
      }
    });
  }

  /// Run all transformations that are needed when building a bundle of
  /// libraries for the first time.
  void runBuildTransformations() {
    backendTarget.performModularTransformationsOnLibraries(
        component, loader.coreTypes, loader.hierarchy, loader.libraries,
        logger: (String msg) => ticker.logMs(msg));
  }

  void runProcedureTransformations(Procedure procedure) {
    backendTarget.performTransformationsOnProcedure(
        loader.coreTypes, loader.hierarchy, procedure,
        logger: (String msg) => ticker.logMs(msg));
  }

  void verify() {
    // TODO(ahe): How to handle errors.
    verifyComponent(component);
    ticker.logMs("Verified component");
  }

  /// Return `true` if the given [library] was built by this [KernelTarget]
  /// from sources, and not loaded from a [DillTarget].
  bool isSourceLibrary(Library library) {
    return loader.libraries.contains(library);
  }

  @override
  void readPatchFiles(KernelLibraryBuilder library) {
    assert(library.uri.scheme == "dart");
    List<Uri> patches = uriTranslator.getDartPatches(library.uri.path);
    if (patches != null) {
      KernelLibraryBuilder 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.
          KernelLibraryBuilder part =
              library.loader.read(patch, -1, fileUri: patch, accessor: first);
          first.parts.add(part);
          part.partOfUri = first.uri;
        }
      }
    }
  }
}

/// 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;
  while (superclass != null && superclass.isMixinApplication) {
    superclass = superclass.superclass;
  }
  for (Constructor constructor in superclass.constructors) {
    if (constructor.name.name.isEmpty) {
      return constructor.function.requiredParameterCount == 0
          ? constructor
          : null;
    }
  }
  return null;
}
