diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 361bd1cddba..8298f3b3708 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -115,6 +115,8 @@ ERROR(error_immediate_mode_missing_library,none, (unsigned, StringRef)) ERROR(error_immediate_mode_primary_file,none, "immediate mode is incompatible with -primary-file", ()) +WARNING(warning_immediate_mode_cannot_load_foundation,none, + "immediate mode failed to load Foundation: %0", (StringRef)) ERROR(error_missing_frontend_action,none, "no frontend action was selected", ()) ERROR(error_invalid_source_location_str,none, diff --git a/include/swift/Immediate/Immediate.h b/include/swift/Immediate/Immediate.h index 10f0425290b..8518ba0ff67 100644 --- a/include/swift/Immediate/Immediate.h +++ b/include/swift/Immediate/Immediate.h @@ -24,6 +24,7 @@ namespace swift { class CompilerInstance; + class DiagnosticEngine; class IRGenOptions; class SILOptions; class SILModule; @@ -44,6 +45,9 @@ namespace swift { int RunImmediatelyFromAST(CompilerInstance &CI); + /// On platforms that support ObjC bridging from the Foundation framework, + /// ensure that Foundation is loaded early enough. Otherwise does nothing. + void loadFoundationIfNeeded(DiagnosticEngine &Diags); } // end namespace swift #endif // SWIFT_IMMEDIATE_IMMEDIATE_H diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 703ccd99c3d..c3be25afb8e 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1955,6 +1955,13 @@ int swift::performFrontend(ArrayRef Args, return finishDiagProcessing(1, /*verifierEnabled*/ false); } + // Scripts that use the Foundation framework need it loaded early for bridging + // to work correctly on Darwin platforms. On other platforms this is a no-op. + if (Invocation.getFrontendOptions().RequestedAction == + FrontendOptions::ActionType::Immediate) { + loadFoundationIfNeeded(Instance->getDiags()); + } + // Don't ask clients to report bugs when running a script in immediate mode. // When a script asserts the compiler reports the error with the same // stacktrace as a compiler crash. From here we can't tell which is which, diff --git a/lib/Immediate/Immediate.cpp b/lib/Immediate/Immediate.cpp index f83b1b05df7..cfbfc5ad772 100644 --- a/lib/Immediate/Immediate.cpp +++ b/lib/Immediate/Immediate.cpp @@ -432,3 +432,30 @@ int swift::RunImmediatelyFromAST(CompilerInstance &CI) { return *Result; } + +void swift::loadFoundationIfNeeded(DiagnosticEngine &Diags) { +#if defined(__APPLE__) + const char *FoundationPath = + "/System/Library/Frameworks/Foundation.framework/Foundation"; + void *handle = dlopen(FoundationPath, RTLD_NOLOAD); + if (handle) { + // Foundation is already loaded. Use dlclose to release the ref-count that + // was incremented by dlopen and return. + dlclose(handle); + return; + } else { + // Foundation is not yet loaded. Load it now and leak the handle. + // FIXME: it is fragile to load here, as there is no guarantee the swift + // runtime has not initialized already. As the compiler adds more swift code + // we may need to move this or find another solution. + handle = dlopen(FoundationPath, RTLD_LAZY | RTLD_GLOBAL); + if (!handle) + Diags.diagnose(SourceLoc(), + diag::warning_immediate_mode_cannot_load_foundation, + dlerror()); + } + +#else + // Nothing to do. +#endif +} diff --git a/test/Interpreter/foundation-bridge-error.swift b/test/Interpreter/foundation-bridge-error.swift new file mode 100644 index 00000000000..bfb3a7c80d2 --- /dev/null +++ b/test/Interpreter/foundation-bridge-error.swift @@ -0,0 +1,25 @@ +// Check that we can access localizedDescription, which crashes in the runtime +// if Foundation is loaded after the runtime is already initialized on Darwin. + +// REQUIRES: executable_test +// REQUIRES: objc_interop +// REQUIRES: OS=macosx + +// FIXME: There's a separate bridging error with the just-built stdlib on CI +// nodes. +// REQUIRES: use_os_stdlib + +// RUN: %target-jit-run %s +// RUN: DYLD_INSERT_LIBRARIES=/System/Library/Frameworks/Foundation.framework/Foundation %target-jit-run %s + +import Foundation + +print("Insert Libraries: \(ProcessInfo.processInfo.environment["DYLD_INSERT_LIBRARIES"] ?? "")") + +enum SomeError: LocalizedError { + case fail +} + +let err = SomeError.fail +let path = (#file as NSString).lastPathComponent +let desc = err.localizedDescription \ No newline at end of file