// Copyright (c) 2017, 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.

/// Utility to dump a truncated .dill file generated by the incremental kernel
/// generator or by the VM's kernel service.

import 'dart:io' show File, exitCode;

import 'package:kernel/kernel.dart'
    show
        Component,
        loadComponentFromBinary,
        loadComponentFromBytes,
        writeComponentToText;

import 'package:front_end/src/fasta/kernel/utils.dart' show serializeComponent;

main(List<String> args) {
  if (args.length == 0) {
    print('usage: pkg/front_end/tool/fasta dump_partial '
        'partial.dill [extra1.dill] ... [extraN.dill]');
    exitCode = 1;
    return;
  }

  var component = _loadComponent(args);
  writeComponentToText(component);
}

/// Creates a component that contains all of the context code marked as
/// external, and all libraries defined in partial.dill as they are written in
/// that file.
Component _loadComponent(List<String> args) {
  List<int> partialInput = new File(args[0]).readAsBytesSync();

  var context = new Component();
  for (var i = 1; i < args.length; i++) {
    loadComponentFromBinary(args[i], context);
  }

  Set<Uri> libraries = _definedLibraries(partialInput, context);

  // By default `package:kernel/binary/ast_from_binary.dart` merges the contents
  // of libraries that are mentioned in more than one .dill file. In order to
  // keep the contents of partial.dill intact, we build a new context that
  // excludes those libraries defined in partial.dill.
  List<int> contextBytes = serializeComponent(context,
      filter: (l) => !libraries.contains(l.importUri));
  var component = new Component();
  loadComponentFromBytes(contextBytes, component);
  _updateIsExternal(component, true);
  loadComponentFromBytes(partialInput, component);
  return component;
}

/// Compute the set of libraries defined in [partialDill].
///
/// The [context] component contains all other libraries that may be needed to
/// properly deserialize partialDill.
///
/// Note: This function will mutate [context] in place.
// TODO(sigmund): simplify and get rid of [context]. We could do that with a
// custom deserialization, but it will be easier to do once .dill has
// random-access support.
Set<Uri> _definedLibraries(List<int> partialDill, Component context) {
  _updateIsExternal(context, true);

  // This implicitly sets `isExternal = false` on all libraries defined in the
  // partial.dill file.
  loadComponentFromBytes(partialDill, context);
  var result = context.libraries
      .where((l) => !l.isExternal)
      .map((l) => l.importUri)
      .toSet();
  _updateIsExternal(context, false);
  return result;
}

void _updateIsExternal(Component component, bool toValue) {
  component.libraries.forEach((lib) => lib.isExternal = toValue);
}
