// RUN: %empty-directory(%t)
// RUN: %build-irgen-test-overlays
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -disable-objc-attr-requires-foundation-module -emit-module %S/Inputs/objc_extension_base.swift -o %t
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir -g | %FileCheck %s

// REQUIRES: CPU=x86_64 || CPU=arm64
// REQUIRES: objc_interop

import Foundation
import gizmo
import objc_extension_base

// Check that metadata for nested enums added in extensions to imported classes
// gets emitted concretely.

// CHECK: [[CATEGORY_NAME:@.*]] = private unnamed_addr constant [16 x i8] c"objc_extensions\00"
// CHECK: [[METHOD_TYPE:@.*]] = private unnamed_addr constant [8 x i8] c"v16@0:8\00"

// CHECK-LABEL: @"_CATEGORY_PROTOCOLS_Gizmo_$_objc_extensions" = internal constant
// CHECK-SAME:   i64 1,
// CHECK-SAME:   @_PROTOCOL__TtP15objc_extensions11NewProtocol_

// CHECK-LABEL: @"_CATEGORY_Gizmo_$_objc_extensions" = internal constant
// CHECK-SAME:   ptr [[CATEGORY_NAME]],
// CHECK-SAME:   ptr @"OBJC_CLASS_$_Gizmo",
// CHECK-SAME:   @"_CATEGORY_INSTANCE_METHODS_Gizmo_$_objc_extensions",
// CHECK-SAME:   @"_CATEGORY_CLASS_METHODS_Gizmo_$_objc_extensions",
// CHECK-SAME:   @"_CATEGORY_PROTOCOLS_Gizmo_$_objc_extensions",
// CHECK-SAME:   ptr null
// CHECK-SAME: }, section "__DATA, {{.*}}", align 8

@objc protocol NewProtocol {
  func brandNewInstanceMethod()
}

extension NSObject {
  @objc func someMethod() -> String { return "Hello" }
}

extension Gizmo: NewProtocol {
  @objc func brandNewInstanceMethod() {
  }

  @objc class func brandNewClassMethod() {
  }

  // Overrides an instance method of NSObject
  override func someMethod() -> String {
    return super.someMethod()
  }

  // Overrides a class method of NSObject
  @objc override class func hasOverride() {}
}

/*
 * Make sure that two extensions of the same ObjC class in the same module can
 * coexist by having different category names.
 */

// CHECK: [[CATEGORY_NAME_1:@.*]] = private unnamed_addr constant [17 x i8] c"objc_extensions1\00"

// CHECK: @"_CATEGORY_Gizmo_$_objc_extensions1" = internal constant
// CHECK:   ptr [[CATEGORY_NAME_1]],
// CHECK:   ptr @"OBJC_CLASS_$_Gizmo",
// CHECK:   {{.*}} @"_CATEGORY_INSTANCE_METHODS_Gizmo_$_objc_extensions1",
// CHECK:   {{.*}} @"_CATEGORY_CLASS_METHODS_Gizmo_$_objc_extensions1",
// CHECK:   ptr null,
// CHECK:   ptr null
// CHECK: }, section "__DATA, {{.*}}", align 8

extension Gizmo {
  @objc func brandSpankingNewInstanceMethod() {
  }

  @objc class func brandSpankingNewClassMethod() {
  }
}

/*
 * Make sure that extensions of an ObjC class with `@objc(CustomName)` get the
 * indicated category name.
 */
// CHECK: @"_CATEGORY_Gizmo_$_WidgetMaker" = internal constant
// CHECK:   ptr @.str.11.WidgetMaker,
// CHECK:   ptr @"OBJC_CLASS_$_Gizmo",
// CHECK:   {{.*}} @"_CATEGORY_INSTANCE_METHODS_Gizmo_$_WidgetMaker",
// CHECK:   {{.*}} ptr null,
// CHECK:   ptr null,
// CHECK:   ptr null
// CHECK: }, section "__DATA, {{.*}}", align 8

@objc(WidgetMaker) extension Gizmo {
  func makeWidget() {
  }
}

/*
 * Check that extensions of Swift subclasses of ObjC objects get categories.
 */

class Hoozit : NSObject {
}

// CHECK-LABEL: @"_CATEGORY_INSTANCE_METHODS__TtC15objc_extensions6Hoozit_$_objc_extensions" = internal constant
// CHECK:   i32 24,
// CHECK:   i32 1,
// CHECK:   [1 x { ptr, ptr, ptr }] [{ ptr, ptr, ptr } {
// CHECK:     ptr @"\01L_selector_data(blibble)",
// CHECK:     ptr [[STR:@[^,]*]],
// CHECK:     ptr @"$s15objc_extensions6HoozitC7blibbleyyFTo"
// CHECK:   }]
// CHECK: }, section "__DATA, {{.*}}", align 8

// CHECK-LABEL: @"_CATEGORY_CLASS_METHODS__TtC15objc_extensions6Hoozit_$_objc_extensions" = internal constant
// CHECK:   i32 24,
// CHECK:   i32 1,
// CHECK:   [1 x { ptr, ptr, ptr }] [{ ptr, ptr, ptr } {
// CHECK:     ptr @"\01L_selector_data(blobble)",
// CHECK:     ptr [[STR]],
// CHECK:     ptr @"$s15objc_extensions6HoozitC7blobbleyyFZTo"
// CHECK:   }]
// CHECK: }, section "__DATA, {{.*}}", align 8

// CHECK-LABEL: @"_CATEGORY__TtC15objc_extensions6Hoozit_$_objc_extensions" = internal constant
// CHECK:   ptr [[CATEGORY_NAME]],
// CHECK:   ptr {{.*}} @"$s15objc_extensions6HoozitCMf",
// CHECK:   {{.*}} @"_CATEGORY_INSTANCE_METHODS__TtC15objc_extensions6Hoozit_$_objc_extensions",
// CHECK:   {{.*}} @"_CATEGORY_CLASS_METHODS__TtC15objc_extensions6Hoozit_$_objc_extensions",
// CHECK:   ptr null,
// CHECK:   ptr null
// CHECK: }, section "__DATA, {{.*}}", align 8

extension Hoozit {
  @objc func blibble() { }
  @objc class func blobble() { }
}

class SwiftOnly { }

// CHECK-LABEL: @"_CATEGORY_INSTANCE_METHODS__TtC15objc_extensions9SwiftOnly_$_objc_extensions" = internal constant
// CHECK:   i32 24,
// CHECK:   i32 1,
// CHECK:   [1 x { ptr, ptr, ptr }] [{ ptr, ptr, ptr } {
// CHECK:     ptr @"\01L_selector_data(wibble)",
// CHECK:     ptr [[STR]],
// CHECK:     ptr @"$s15objc_extensions9SwiftOnlyC6wibbleyyFTo"
// CHECK:   }] }, section "__DATA, {{.*}}", align 8
extension SwiftOnly {
  @objc func wibble() { }
}

class Wotsit: Hoozit {}

extension Hoozit {
  @objc func overriddenByExtensionInSubclass() {}
}

extension Wotsit {
  @objc override func overriddenByExtensionInSubclass() {}
}

extension NSObject {
  private enum SomeEnum { case X }

  @objc public func needMetadataOfSomeEnum() {
    print(NSObject.SomeEnum.X)
  }

  @objc class func hasOverride() {}
}


// CHECK-LABEL: @"_CATEGORY__TtCC15objc_extensions5Outer5Inner_$_objc_extensions" = internal constant
// CHECK-SAME:   ptr [[CATEGORY_NAME]],
// CHECK-SAME:   @"_CATEGORY_INSTANCE_METHODS__TtCC15objc_extensions5Outer5Inner_$_objc_extensions",
// CHECK-SAME:   ptr null
// CHECK-SAME: }, section "__DATA, {{.*}}", align 8

class Outer : NSObject {
  class Inner : NSObject {}
}

extension Outer.Inner {
  @objc func innerExtensionMethod() {}
}


/*
 * Make sure that @NSManaged causes a category to be generated.
 */
class NSDogcow : NSObject {}

// CHECK: [[NAME:@.*]] = private unnamed_addr constant [5 x i8] c"woof\00"
// CHECK: [[ATTR:@.*]] = private unnamed_addr constant [7 x i8] c"Tq,N,D\00"
// CHECK: @"_CATEGORY_PROPERTIES__TtC15objc_extensions8NSDogcow_$_objc_extensions" = internal constant {{.*}} [[NAME]], {{.*}} [[ATTR]] {{.*}}, section "__DATA, {{.*}}", align 8
extension NSDogcow {
  @NSManaged var woof: Int
}

// CHECK: @"$sSo8NSObjectC15objc_extensionsE8SomeEnum33_1F05E59585E0BB585FCA206FBFF1A92DLLOSQACMc" =

class SwiftSubGizmo : SwiftBaseGizmo {

  // Don't crash on this call. Emit an objC method call to super.
  //
  // CHECK-LABEL: define {{.*}} @"$s15objc_extensions13SwiftSubGizmoC4frobyyF"
  // CHECK: $s15objc_extensions13SwiftSubGizmoCMa
  // CHECK: objc_msgSendSuper2
  // CHECK: ret
  public override func frob() {
    super.frob()
  }
}

@inline(never) func opaquePrint(_ value: Any) { print(value) }

/*
 * Check that we can extend ObjC generics and use both the type and metatype of
 * the generic parameter. Specifically, we're checking that we emit debug info
 * if we look up the existential bound, and that we derive the argument to
 * `opaquePrint(_:)` from the actual parameter, not just the fixed metadata.
 */
extension FungingArray {
  // CHECK-LABEL: define {{.*}} @"$sSo12FungingArrayC15objc_extensionsEyAByxGxcfC"
  // CHECK-SAME: (ptr %0, ptr swiftself %1)
  // CHECK: @__swift_instantiateConcreteTypeFromMangledName{{.*}}@"$sSo9NSFunging_pMD"{{.*}}!dbg

  // CHECK-LABEL: define {{.*}} @"$sSo12FungingArrayC15objc_extensionsEyAByxGxcfc"
  // CHECK-SAME: (ptr %0, ptr swiftself %1)
  // CHECK: [[ALLOCA:%[^, =]+]] = alloca %Any, align 8
  // CHECK: @__swift_instantiateConcreteTypeFromMangledName{{.*}}@"$sSo9NSFunging_pMD"{{.*}}!dbg
  // CHECK: {{%[^, =]+}} = getelementptr inbounds{{.*}} %Any, ptr [[ALLOCA]], i32 0, i32 0
  // CHECK: [[ANYBUF:%[^, =]+]] = getelementptr inbounds{{.*}} %Any, ptr [[ALLOCA]], i32 0, i32 0
  // CHECK: [[BUFPTR:%[^, =]+]] = {{.*}} [[ANYBUF]]
  // CHECK: store {{.*}} %0, {{.*}} [[BUFPTR]]
  // CHECK: call swiftcc void @"$s15objc_extensions11opaquePrintyyypF"(ptr {{.*}} [[ALLOCA]])
  @objc public convenience init(_ elem: Element) {
    opaquePrint(elem)
    self.init()
  }

  // CHECK-LABEL: define {{.*}} @"$sSo12FungingArrayC15objc_extensionsE7pinningAByxGxm_tcfC"
  // CHECK-SAME: (ptr %0, ptr swiftself %1)
  // CHECK: @__swift_instantiateConcreteTypeFromMangledName{{.*}}@"$sSo9NSFunging_pMD"{{.*}}!dbg

  // CHECK-LABEL: define {{.*}} @"$sSo12FungingArrayC15objc_extensionsE7pinningAByxGxm_tcfc"
  // CHECK-SAME: (ptr %0, ptr swiftself %1)
  // CHECK: [[ALLOCA:%[^, =]+]] = alloca %Any, align 8
  // CHECK: @__swift_instantiateConcreteTypeFromMangledName{{.*}}@"$sSo9NSFunging_pMD"{{.*}}!dbg
  // CHECK: [[OBJC_CLASS:%[^, =]+]] = call ptr @swift_getObjCClassFromMetadata(ptr %0)
  // CHECK: {{%[^, =]+}} = getelementptr inbounds{{.*}} %Any, ptr [[ALLOCA]], i32 0, i32 0
  // CHECK: [[ANYBUF:%[^, =]+]] = getelementptr inbounds{{.*}} %Any, ptr [[ALLOCA]], i32 0, i32 0
  // CHECK: [[BUFPTR:%[^, =]+]] = {{.*}} [[ANYBUF]]
  // CHECK: store {{.*}} [[OBJC_CLASS]], {{.*}} [[BUFPTR]]
  // CHECK: call swiftcc void @"$s15objc_extensions11opaquePrintyyypF"(ptr {{.*}} [[ALLOCA]])
  @objc public convenience init(pinning: Element.Type) {
    opaquePrint(pinning as AnyObject)
    self.init()
  }
}
