Skip to content

Commit 8a824d7

Browse files
committed
[immediate] Load Foundation early enough for bridging
Foundation needs to be loaded early in the process for Swift's runtime to properly initialize bridging support; otherwise it may cause issues like unrecognized selectors. When scripting, load Foundation early in performFrontend before any swift code runs. rdar://129528115
1 parent 66e2f97 commit 8a824d7

File tree

5 files changed

+65
-0
lines changed

5 files changed

+65
-0
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ ERROR(error_immediate_mode_missing_library,none,
115115
(unsigned, StringRef))
116116
ERROR(error_immediate_mode_primary_file,none,
117117
"immediate mode is incompatible with -primary-file", ())
118+
WARNING(warning_immediate_mode_cannot_load_foundation,none,
119+
"immediate mode failed to load Foundation: %0", (StringRef))
118120
ERROR(error_missing_frontend_action,none,
119121
"no frontend action was selected", ())
120122
ERROR(error_invalid_source_location_str,none,

include/swift/Immediate/Immediate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
namespace swift {
2626
class CompilerInstance;
27+
class DiagnosticEngine;
2728
class IRGenOptions;
2829
class SILOptions;
2930
class SILModule;
@@ -44,6 +45,9 @@ namespace swift {
4445

4546
int RunImmediatelyFromAST(CompilerInstance &CI);
4647

48+
/// On platforms that support ObjC bridging from the Foundation framework,
49+
/// ensure that Foundation is loaded early enough. Otherwise does nothing.
50+
void loadFoundationIfNeeded(DiagnosticEngine &Diags);
4751
} // end namespace swift
4852

4953
#endif // SWIFT_IMMEDIATE_IMMEDIATE_H

lib/FrontendTool/FrontendTool.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,6 +1955,13 @@ int swift::performFrontend(ArrayRef<const char *> Args,
19551955
return finishDiagProcessing(1, /*verifierEnabled*/ false);
19561956
}
19571957

1958+
// Scripts that use the Foundation framework need it loaded early for bridging
1959+
// to work correctly on Darwin platforms. On other platforms this is a no-op.
1960+
if (Invocation.getFrontendOptions().RequestedAction ==
1961+
FrontendOptions::ActionType::Immediate) {
1962+
loadFoundationIfNeeded(Instance->getDiags());
1963+
}
1964+
19581965
// Don't ask clients to report bugs when running a script in immediate mode.
19591966
// When a script asserts the compiler reports the error with the same
19601967
// stacktrace as a compiler crash. From here we can't tell which is which,

lib/Immediate/Immediate.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,30 @@ int swift::RunImmediatelyFromAST(CompilerInstance &CI) {
432432

433433
return *Result;
434434
}
435+
436+
void swift::loadFoundationIfNeeded(DiagnosticEngine &Diags) {
437+
#if defined(__APPLE__)
438+
const char *FoundationPath =
439+
"/System/Library/Frameworks/Foundation.framework/Foundation";
440+
void *handle = dlopen(FoundationPath, RTLD_NOLOAD);
441+
if (handle) {
442+
// Foundation is already loaded. Use dlclose to release the ref-count that
443+
// was incremented by dlopen and return.
444+
dlclose(handle);
445+
return;
446+
} else {
447+
// Foundation is not yet loaded. Load it now and leak the handle.
448+
// FIXME: it is fragile to load here, as there is no guarantee the swift
449+
// runtime has not initialized already. As the compiler adds more swift code
450+
// we may need to move this or find another solution.
451+
handle = dlopen(FoundationPath, RTLD_LAZY | RTLD_GLOBAL);
452+
if (!handle)
453+
Diags.diagnose(SourceLoc(),
454+
diag::warning_immediate_mode_cannot_load_foundation,
455+
dlerror());
456+
}
457+
458+
#else
459+
// Nothing to do.
460+
#endif
461+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Check that we can access localizedDescription, which crashes in the runtime
2+
// if Foundation is loaded after the runtime is already initialized on Darwin.
3+
4+
// REQUIRES: executable_test
5+
// REQUIRES: objc_interop
6+
// REQUIRES: OS=macosx
7+
8+
// FIXME: There's a separate bridging error with the just-built stdlib on CI
9+
// nodes.
10+
// REQUIRES: use_os_stdlib
11+
12+
// RUN: %target-jit-run %s
13+
// RUN: DYLD_INSERT_LIBRARIES=/System/Library/Frameworks/Foundation.framework/Foundation %target-jit-run %s
14+
15+
import Foundation
16+
17+
print("Insert Libraries: \(ProcessInfo.processInfo.environment["DYLD_INSERT_LIBRARIES"] ?? "<nil>")")
18+
19+
enum SomeError: LocalizedError {
20+
case fail
21+
}
22+
23+
let err = SomeError.fail
24+
let path = (#file as NSString).lastPathComponent
25+
let desc = err.localizedDescription

0 commit comments

Comments
 (0)