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

// part of "core_patch.dart";

// This type corresponds to the VM-internal class LibraryPrefix.
@pragma("vm:entry-point")
class _LibraryPrefix {
  factory _LibraryPrefix._uninstantiable() {
    throw "Unreachable";
  }

  bool _isLoaded() native "LibraryPrefix_isLoaded";
  void _setLoaded() native "LibraryPrefix_setLoaded";
  Object _loadingUnit() native "LibraryPrefix_loadingUnit";
  static void _issueLoad(Object unit) native "LibraryPrefix_issueLoad";

  static final _loads = new Map<Object, Completer<void>>();
}

class _DeferredNotLoadedError extends Error implements NoSuchMethodError {
  final _LibraryPrefix prefix;

  _DeferredNotLoadedError(this.prefix);

  String toString() {
    return "Deferred library $prefix was not loaded.";
  }
}

@pragma("vm:entry-point")
void _completeLoads(Object unit, String? errorMessage, bool transientError) {
  Completer<void> load = _LibraryPrefix._loads[unit]!;
  if (errorMessage == null) {
    load.complete(null);
  } else {
    if (transientError) {
      _LibraryPrefix._loads.remove(unit);
    }
    load.completeError(new DeferredLoadException(errorMessage));
  }
}

@pragma("vm:entry-point")
@pragma("vm:never-inline") // Don't duplicate prefix checking code.
Future<void> _loadLibrary(_LibraryPrefix prefix) async {
  if (!prefix._isLoaded()) {
    Object unit = prefix._loadingUnit();
    // Don't issue a load request for the root unit. A deferred prefix can
    // point to a library in the root unit if there is also an immediate import
    // of that library.
    if (unit != 1) {
      Completer<void>? load = _LibraryPrefix._loads[unit];
      if (load == null) {
        _LibraryPrefix._loads[unit] = load = new Completer<void>();
        _LibraryPrefix._issueLoad(unit);
      }
      await load.future;
    }
  }
  // Ensure the prefix's future does not complete until the next Turn even
  // when loading is a no-op or synchronous. Helps applications avoid writing
  // code that only works when loading isn't really deferred.
  await new Future<void>(() {
    prefix._setLoaded();
  });
}

@pragma("vm:entry-point")
@pragma("vm:never-inline") // Don't duplicate prefix checking code.
void _checkLoaded(_LibraryPrefix prefix) {
  if (!prefix._isLoaded()) {
    throw new _DeferredNotLoadedError(prefix);
  }
}
