Merge pull request #19416 from jckarter/better-missing-metadata-warnings-4.2

[4.2] Make runtime warnings about missing metadata more descriptive.
diff --git a/stdlib/public/SDK/Foundation/BundleLookup.mm b/stdlib/public/SDK/Foundation/BundleLookup.mm
new file mode 100644
index 0000000..e19207d
--- /dev/null
+++ b/stdlib/public/SDK/Foundation/BundleLookup.mm
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+
+#import <Foundation/Foundation.h>
+
+#include <objc/runtime.h>
+
+// This method is only used on "embedded" targets. It's not necessary on
+// Mac or simulators.
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+
+/// CoreFoundation SPI for finding the enclosing bundle. This is only
+/// ever called on older OSes, so there's no worry of running into
+/// trouble if the implementation is changed later on.
+extern "C" CFURLRef _CFBundleCopyBundleURLForExecutableURL(CFURLRef url);
+
+@implementation NSBundle (SwiftAdditions)
+
+/// Given an executable path as a C string, look up the corresponding
+/// NSBundle instance, if any.
++ (NSBundle *)_swift_bundleWithExecutablePath: (const char *)path {
+  NSString *nspath = [[NSFileManager defaultManager]
+    stringWithFileSystemRepresentation:path length:strlen(path)];
+  NSURL *executableURL = [NSURL fileURLWithPath:nspath];
+  NSURL *bundleURL =
+    (NSURL *)_CFBundleCopyBundleURLForExecutableURL((CFURLRef)executableURL);
+  if (!bundleURL)
+    return nil;
+  
+  NSBundle *bundle = [NSBundle bundleWithURL: bundleURL];
+  [bundleURL release];
+  return bundle;
+}
+
+@end
+
+#endif
diff --git a/stdlib/public/SDK/Foundation/CMakeLists.txt b/stdlib/public/SDK/Foundation/CMakeLists.txt
index c9d3ef8..a42c512 100644
--- a/stdlib/public/SDK/Foundation/CMakeLists.txt
+++ b/stdlib/public/SDK/Foundation/CMakeLists.txt
@@ -4,6 +4,7 @@
 add_swift_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
   AffineTransform.swift
   Boxing.swift
+  BundleLookup.mm
   Calendar.swift
   CharacterSet.swift
   Codable.swift
diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt
index a798cb0..7741b5c 100644
--- a/stdlib/public/runtime/CMakeLists.txt
+++ b/stdlib/public/runtime/CMakeLists.txt
@@ -31,7 +31,7 @@
     SwiftObject.mm
     SwiftValue.mm
     ReflectionMirror.mm
-    ObjCRuntimeGetImageNameFromClass.cpp
+    ObjCRuntimeGetImageNameFromClass.mm
     "${SWIFT_SOURCE_DIR}/lib/Demangling/OldRemangler.cpp"
     "${SWIFT_SOURCE_DIR}/lib/Demangling/Remangler.cpp"
     "${SWIFT_SOURCE_DIR}/lib/Demangling/TypeDecoder.cpp"
diff --git a/stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.cpp b/stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.mm
similarity index 75%
rename from stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.cpp
rename to stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.mm
index c25beaf..46bbf00 100644
--- a/stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.cpp
+++ b/stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.mm
@@ -25,10 +25,23 @@
 
 #include <dlfcn.h>
 #include <objc/runtime.h>
+#include <objc/message.h>
+#include <TargetConditionals.h>
 
 // Note: There are more #includes below under "Function patching machinery".
 // Those are only relevant to the function patching machinery.
 
+// On "embedded" targets (i.e. iOS/tvOS/watchOS devices), we need to
+// patch +[NSBundle bundleForClass:] directly. The symbol table patch
+// does not work for calls within the shared cache on those platforms,
+// so the call within +bundleForClass: does not get patched. Instead,
+// swizzle out the whole method with one that does the appropriate
+// lookup for Swift classes. The symbol table patch handles this on Mac
+// and simulators so this is not necessary there.
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#define PATCH_NSBUNDLE 1
+#endif
+
 using namespace swift;
 
 
@@ -40,12 +53,8 @@
 /// \see customGetImageNameFromClass
 static objc_hook_getImageName defaultGetImageNameFromClass = nullptr;
 
-/// A custom implementation of Objective-C's class_getImageName for Swift
-/// classes, which knows how to handle dynamically-initialized class metadata.
-///
-/// Per the documentation for objc_setHook_getImageName, any non-Swift classes
-/// will still go through the normal implementation of class_getImageName,
-/// which is stored in defaultGetImageNameFromClass.
+/// Get the image name corresponding to a Swift class, accounting for
+/// dynamically-initialized class metadata. Returns NO for ObjC classes.
 static BOOL
 getImageNameFromSwiftClass(Class _Nonnull objcClass,
                            const char * _Nullable * _Nonnull outImageName) {
@@ -63,8 +72,21 @@
     *outImageName = imageInfo.dli_fname;
     return imageInfo.dli_fname != nullptr;
   }
+  
+  return NO;
+}
 
-  // If not, fall back to the default implementation.
+/// A custom implementation of Objective-C's class_getImageName for Swift
+/// classes, which knows how to handle dynamically-initialized class metadata.
+///
+/// Per the documentation for objc_setHook_getImageName, any non-Swift classes
+/// will still go through the normal implementation of class_getImageName,
+/// which is stored in defaultGetImageNameFromClass.
+static BOOL
+replacementGetImageNameFromClass(Class _Nonnull objcClass,
+                                 const char * _Nullable * _Nonnull outImageName) {
+  if (getImageNameFromSwiftClass(objcClass, outImageName))
+    return YES;
   return defaultGetImageNameFromClass(objcClass, outImageName);
 }
 
@@ -100,15 +122,6 @@
   typedef struct segment_command    macho_segment_command;
 #endif
 
-  struct patch_t {
-    const char *name;
-    const void *fn;
-
-    template<typename T>
-    patch_t(const char *newName, const T *newFn)
-      : name(newName), fn((const void*)newFn) { 
-    }
-  };
 } // end anonymous namespace
 
 /// Overwrite a cross-image symbol reference by directly editing symbol tables
@@ -121,7 +134,8 @@
 ///
 /// Also, if the symbol being patched has references within the image where it
 /// was originaly defined, those references will \e not be patched.
-static void patchLazyPointers(const mach_header *mh, patch_t patch) {
+static void patchLazyPointers(const mach_header *mh, const char *symbolName,
+                              const void *newValue) {
   // Get linkEditBase
   const uint32_t cmd_count = mh->ncmds;
   const load_command * const cmds =
@@ -205,10 +219,10 @@
           // Found symbol for this lazy pointer, now lookup address.
           const char *lazyTargetName = 
               &stringTable[symbolTable[symbolIndex].n_un.n_strx];
-          if (strcmp(patch.name, lazyTargetName) == 0) {
+          if (strcmp(symbolName, lazyTargetName) == 0) {
             // Can't use the value currently stored here because it may 
             // be a dyld stub binder that will undo our patch if called.
-            symbolPointers[lazyIndex] = (uintptr_t)patch.fn;
+            symbolPointers[lazyIndex] = (uintptr_t)newValue;
           }
         }
       }
@@ -246,18 +260,73 @@
   if (!cls)
     return nullptr;
   const char *result;
-  if (getImageNameFromSwiftClass(cls, &result))
+  if (replacementGetImageNameFromClass(cls, &result))
     return result;
   return nullptr;
 }
 
+#if PATCH_NSBUNDLE
+/// Selectors for the target method, patch method, and helper method.
+#define BUNDLE_FOR_CLASS_SEL @selector(bundleForClass:)
+#define PATCHED_BUNDLE_FOR_CLASS_SEL @selector(_swift_bundleForClass:)
+#define BUNDLE_WITH_EXECUTABLE_PATH_SEL @selector(_swift_bundleWithExecutablePath:)
+
+/// Whether the patch has already been done.
+static bool didPatchNSBundle = false;
+
+/// The patched version of +[NSBundle bundleForClass:]. If the class is
+/// actually a Swift class and an image name can be retrieved from it,
+/// look up the bundle based on that image name. Otherwise fall back to
+/// the original version.
+static id patchedBundleForClass(id self, SEL _cmd, Class objcClass) {
+  const char *imageName;
+  if (getImageNameFromSwiftClass(objcClass, &imageName)) {
+    return ((id (*)(id, SEL, const char *))objc_msgSend)(
+      self, BUNDLE_WITH_EXECUTABLE_PATH_SEL, imageName);
+  }
+  
+  // Call through to the original, which is now found under the patched
+  // selector.
+  return ((id (*)(id, SEL, Class))objc_msgSend)(
+    self, PATCHED_BUNDLE_FOR_CLASS_SEL, objcClass);
+}
+
+/// Install the patched +[NSBundle bundleForClass:].
+static void patchNSBundle(void) {
+  if (didPatchNSBundle) return;
+  
+  Class NSBundle = objc_getClass("NSBundle");
+  if (!NSBundle) return;
+  
+  Method origMethod = class_getClassMethod(NSBundle, BUNDLE_FOR_CLASS_SEL);
+  if (!origMethod) return;
+  
+  // Stuff can fail below, but if it does then we can't reasonably try again.
+  didPatchNSBundle = true;
+  
+  BOOL success = class_addMethod(
+    object_getClass(NSBundle), PATCHED_BUNDLE_FOR_CLASS_SEL,
+    reinterpret_cast<IMP>(patchedBundleForClass), method_getTypeEncoding(origMethod));
+  if (!success) return;
+  
+  Method patchMethod = class_getClassMethod(NSBundle, PATCHED_BUNDLE_FOR_CLASS_SEL);
+  if (!patchMethod) return;
+  
+  method_exchangeImplementations(origMethod, patchMethod);
+}
+#endif
+
 /// A hook for _dyld_register_func_for_add_image that overwrites any references
 /// to class_getImageName with our custom implementation.
 static void patchGetImageNameInImage(const struct mach_header *mh,
                                      intptr_t vmaddr_slide) {
   (void)vmaddr_slide;
-  patchLazyPointers(mh, patch_t("_class_getImageName",
-                                &patchedGetImageNameFromClassForOldOSs));
+  const void *newImplementationAddr =
+      reinterpret_cast<const void *>(&patchedGetImageNameFromClassForOldOSs);
+  patchLazyPointers(mh, "_class_getImageName", newImplementationAddr);
+#if PATCH_NSBUNDLE
+  patchNSBundle();
+#endif
 }
 
 /***************************************************************************/
@@ -274,7 +343,7 @@
     auto setHook = reinterpret_cast<
         void(*)(objc_hook_getImageName _Nonnull,
                 objc_hook_getImageName _Nullable * _Nonnull)>(setHookPtr);
-    setHook(getImageNameFromSwiftClass, &defaultGetImageNameFromClass);
+    setHook(replacementGetImageNameFromClass, &defaultGetImageNameFromClass);
 
   } else {
     // On older OSs, manually patch in our new implementation of