diff --git a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h index cfd376ab9c..73819dbc01 100644 --- a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h +++ b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h @@ -353,6 +353,9 @@ CF_EXPORT CFStringRef _CFXDGCreateCacheDirectoryPath(void); /// a single base directory relative to which user-specific runtime files and other file objects should be placed. This directory is defined by the environment variable $XDG_RUNTIME_DIR. CF_EXPORT CFStringRef _CFXDGCreateRuntimeDirectoryPath(void); +// The argument is the unsafeBitCast(, to: UnsafeRawPointer.self) +CF_EXPORT __attribute__((visibility("default"))) +CFStringRef _Nullable _CFBundleDlfcnCopyPathToBinaryContainingSwiftClass(const void *_Nullable swiftClass); typedef struct { void *_Nonnull memory; diff --git a/CoreFoundation/PlugIn.subproj/CFBundle.c b/CoreFoundation/PlugIn.subproj/CFBundle.c index c6b9d16b1d..11b058af2e 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle.c @@ -1750,3 +1750,29 @@ CF_EXPORT CFURLRef CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle) { return result; } +#if DEPLOYMENT_RUNTIME_SWIFT + +// SPI from the Swift runtime: +extern const void *swift_getTypeContextDescriptor(const void *swiftClass); + +CF_EXPORT __attribute__((visibility("default"))) +CFStringRef _CFBundleDlfcnCopyPathToBinaryContainingSwiftClass(const void *swiftClass) { + if (!swiftClass) { + return NULL; + } + + Dl_info info = { 0 }; + + // For AOT classes, the type context descriptor is guaranteed to come from the binary that has the symbols for that class. Thus, dladdr() can place it. + const void *descriptor = swift_getTypeContextDescriptor(swiftClass); + int result = dladdr(descriptor, &info); + + if (result == 0 || !info.dli_fname) { + return NULL; + } else { + return CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, info.dli_fname); + } +} + +#endif + diff --git a/Foundation/Bundle.swift b/Foundation/Bundle.swift index 38b395e9cb..2b7bcebfa2 100644 --- a/Foundation/Bundle.swift +++ b/Foundation/Bundle.swift @@ -54,7 +54,26 @@ open class Bundle: NSObject { } public init(for aClass: AnyClass) { - NSUnimplemented() + let pointer = unsafeBitCast(aClass, to: UnsafeRawPointer.self) + + if let path = _CFBundleDlfcnCopyPathToBinaryContainingSwiftClass(pointer) { + let url = unsafeBitCast(NSURL(fileURLWithPath: pathString), to: CFURL.self) + if let bundleURL = _CFBundleCopyBundleURLForExecutableURL(url)?.takeRetainedValue() { + _bundle = CFBundleCreate(kCFAllocatorSystemDefault, bundleURL()) + } else { + fatalError("Class '\(aClass)' resolved to path \(pathNS) but couldn't find a bundle associated with that executable.") + } + } + + if _bundle == nil { + // You get here if: + // - dladdr() returned exactly argv[0]. In this case, we know it's the main bundle. + // - _CFBundleDlfcnCopyPathToBinaryContainingSwiftClass returned nil. This can happen for JIT classes, aka: + // 1. Classes in x.swift when called as swift x.swift + // 2. Classes in the REPL + // In both cases, they're conceptually part of the main program, and both also use the main bundle. + _bundle = Bundle.main._bundle + } } public init?(identifier: String) {