// Copyright (c) 2023, 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";

/// Base class for closure objects.
final class _Closure implements Function {
  @pragma("wasm:entry-point")
  WasmStructRef context;

  @pragma("wasm:entry-point")
  _Closure._(this.context);

  @override
  bool operator ==(Object other) {
    if (other is! _Closure) {
      return false;
    }

    if (identical(this, other)) {
      return true;
    }

    final thisIsInstantiation = _isInstantiationClosure;
    final otherIsInstantiation = other._isInstantiationClosure;

    if (thisIsInstantiation != otherIsInstantiation) {
      return false;
    }

    if (thisIsInstantiation) {
      final thisInstantiatedClosure = _instantiatedClosure;
      final otherInstantiatedClosure = other._instantiatedClosure;
      return thisInstantiatedClosure == otherInstantiatedClosure &&
          _instantiationClosureTypeEquals(other);
    }

    if (_vtable != other._vtable) {
      return false;
    }

    final thisIsTearOff = _isInstanceTearOff;
    final otherIsTearOff = other._isInstanceTearOff;

    if (thisIsTearOff != otherIsTearOff) {
      return false;
    }

    if (thisIsTearOff) {
      return _instanceTearOffReceiver == other._instanceTearOffReceiver;
    }

    return false;
  }

  @pragma("wasm:entry-point")
  @pragma("wasm:prefer-inline")
  external static _FunctionType _getClosureRuntimeType(_Closure closure);

  @override
  int get hashCode {
    if (_isInstantiationClosure) {
      return Object.hash(_instantiatedClosure, _instantiationClosureTypeHash());
    }

    if (_isInstanceTearOff) {
      return Object.hash(
          _instanceTearOffReceiver, _getClosureRuntimeType(this));
    }

    return Object._objectHashCode(this); // identity hash
  }

  // Support dynamic tear-off of `.call` on functions
  @pragma("wasm:entry-point")
  Function get call => this;

  @override
  String toString() => 'Closure: $runtimeType';

  // Helpers for implementing `hashCode`, `operator ==`.

  /// Whether the closure is an instantiation.
  external bool get _isInstantiationClosure;

  /// When the closure is an instantiation, get the instantiated closure.
  ///
  /// Traps when the closure is not an instantiation.
  external _Closure get _instantiatedClosure;

  /// When the closure is an instantiation, returns the combined hash code of
  /// the captured types.
  ///
  /// Traps when the closure is not an instantiation.
  external int _instantiationClosureTypeHash();

  /// When [this] and [other] are instantiations, compare captured types for equality.
  ///
  /// Traps when one or both of the closures are not an instantiation.
  external bool _instantiationClosureTypeEquals(_Closure other);

  /// Whether the closure is an instance tear-off.
  ///
  /// Instance tear-offs will have receivers.
  external bool get _isInstanceTearOff;

  /// When the closure is an instance tear-off, returns the receiver.
  ///
  /// Traps when the closure is not an instance tear-off.
  external Object? get _instanceTearOffReceiver;

  /// The vtable of the closure.
  external WasmAnyRef get _vtable;
}
