Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-953] Implement Bundle(for: class) #1573

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(<the AnyClass>, to: UnsafeRawPointer.self)
CF_EXPORT __attribute__((visibility("default")))
CFStringRef _Nullable _CFBundleDlfcnCopyPathToBinaryContainingSwiftClass(const void *_Nullable swiftClass);

typedef struct {
void *_Nonnull memory;
Expand Down
26 changes: 26 additions & 0 deletions CoreFoundation/PlugIn.subproj/CFBundle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

21 changes: 20 additions & 1 deletion Foundation/Bundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,26 @@ open class Bundle: NSObject {
}

public init(for aClass: AnyClass) {
NSUnimplemented()
let pointer = unsafeBitCast(aClass, to: UnsafeRawPointer.self)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering, why are we using unsafeBitCast here and not an inout?

var theClass = aClass
if let path = _CFBundleBlah(&theClass) {...}

My understanding is they do the same thing but the latter is more resilient to changes in ABI


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) {
Expand Down