Skip to content

Commit eb88324

Browse files
committed
Fix dynamic import of libraries in Xcode playgrounds.
The Swift expression parser uses the fixit mechanism to force SwiftASTContext to be reinitialized if it contains fatal errors. Playgrounds disable fixit application and thus defeated the mechanism. This became a problem after the late dylib notification was fixed in 1348ae3. This patch detects the situation where the fixit is identical to the original expression and makes one retry even if fixits are disabled. rdar://85431564 (cherry picked from commit e52de6b)
1 parent 774aa5c commit eb88324

File tree

6 files changed

+78
-14
lines changed

6 files changed

+78
-14
lines changed

lldb/packages/Python/lldbsuite/test/make/Makefile.rules

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,20 @@ ifneq "$(DYLIB_HIDE_SWIFTMODULE)" ""
708708
-emit-library $(DYLIB_SWIFT_FLAGS) -o $@ \
709709
$(patsubst %.swiftmodule.o,,$(DYLIB_OBJECTS))
710710
else
711+
ifneq "$(FRAMEWORK)" ""
712+
mkdir -p $(FRAMEWORK).framework/Versions/A/Headers
713+
mkdir -p $(FRAMEWORK).framework/Versions/A/Modules
714+
mkdir -p $(FRAMEWORK).framework/Versions/A/Resources
715+
ifneq "$(MODULENAME)" ""
716+
mkdir -p $(FRAMEWORK).framework/Versions/A/Modules/$(MODULENAME).swiftmodule
717+
cp -r $(MODULENAME).swiftmodule $(FRAMEWORK).framework/Versions/A/Modules/$(MODULENAME).swiftmodule/$(ARCH).swiftmodule
718+
endif
719+
(cd $(FRAMEWORK).framework/Versions; ln -sf A Current)
720+
(cd $(FRAMEWORK).framework/; ln -sf Versions/A/Headers Headers)
721+
(cd $(FRAMEWORK).framework/; ln -sf Versions/A/Modules Modules)
722+
(cd $(FRAMEWORK).framework/; ln -sf Versions/A/Resources Resources)
723+
(cd $(FRAMEWORK).framework/; ln -sf Versions/A/$(FRAMEWORK) $(FRAMEWORK))
724+
endif
711725
$(SWIFTC) $(patsubst -g,,$(SWIFTFLAGS)) \
712726
-emit-library $(DYLIB_SWIFT_FLAGS) -o $@ $(DYLIB_OBJECTS)
713727
endif

lldb/source/Expression/UserExpression.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,15 +264,22 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
264264

265265
*fixed_expression = user_expression_sp->GetFixedText().str();
266266

267+
// Swift Playgrounds disable fixits, but SwiftASTContext may get
268+
// poisoned (see SwiftASTContextForExpressions::ModulesDidLoad())
269+
// during expression evaluation. When this happens we want to re-run
270+
// the same expression with a freshly initialized SwiftASTContext.
271+
bool is_replay = !options.GetAutoApplyFixIts() && expr == *fixed_expression;
272+
267273
// If there is a fixed expression, try to parse it:
268274
if (!parse_success) {
269275
// Delete the expression that failed to parse before attempting to parse
270276
// the next expression.
271277
user_expression_sp.reset();
272278

273279
execution_results = lldb::eExpressionParseError;
274-
if (!fixed_expression->empty() && options.GetAutoApplyFixIts()) {
275-
const uint64_t max_fix_retries = options.GetRetriesWithFixIts();
280+
if (!fixed_expression->empty() &&
281+
(options.GetAutoApplyFixIts() || is_replay)) {
282+
const uint64_t max_fix_retries = is_replay ? 1 : options.GetRetriesWithFixIts();
276283
for (uint64_t i = 0; i < max_fix_retries; ++i) {
277284
// Try parsing the fixed expression.
278285
lldb::UserExpressionSP fixed_expression_sp(

lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,10 +1221,18 @@ struct SwiftASTContextError : public llvm::ErrorInfo<SwiftASTContextError> {
12211221
/// This indicates an error in the SwiftASTContext.
12221222
struct ModuleImportError : public llvm::ErrorInfo<ModuleImportError> {
12231223
static char ID;
1224-
std::string Message;
1225-
1226-
ModuleImportError(llvm::Twine Message) : Message(Message.str()) {}
1227-
void log(llvm::raw_ostream &OS) const override { OS << "ModuleImport"; }
1224+
std::string msg;
1225+
bool is_explicit;
1226+
1227+
ModuleImportError(llvm::Twine message, bool is_explicit = false)
1228+
: msg(message.str()), is_explicit(is_explicit) {}
1229+
void log(llvm::raw_ostream &OS) const override {
1230+
if (is_explicit)
1231+
OS << "error while processing import statement:";
1232+
else
1233+
OS << "error while importing implicit dependency:";
1234+
OS << msg;
1235+
}
12281236
std::error_code convertToErrorCode() const override {
12291237
return inconvertibleErrorCode();
12301238
}
@@ -1321,8 +1329,7 @@ static llvm::Expected<ParsedExpression> ParseAndImport(
13211329
const char *msg = implicit_import_error.AsCString();
13221330
if (!msg)
13231331
msg = "error status positive, but import still failed";
1324-
return make_error<ModuleImportError>(llvm::Twine("in implicit-import:\n") +
1325-
msg);
1332+
return make_error<ModuleImportError>(msg);
13261333
}
13271334

13281335
swift::ImplicitImportInfo importInfo;
@@ -1471,8 +1478,7 @@ static llvm::Expected<ParsedExpression> ParseAndImport(
14711478
const char *msg = auto_import_error.AsCString();
14721479
if (!msg)
14731480
msg = "error status positive, but import still failed";
1474-
return make_error<ModuleImportError>(
1475-
llvm::Twine("in user-import:\n") + msg);
1481+
return make_error<ModuleImportError>(msg, /*explicit=*/true);
14761482
}
14771483
}
14781484

@@ -1524,13 +1530,19 @@ unsigned SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
15241530
m_sc, *m_exe_scope, m_options, repl, playground);
15251531

15261532
if (!parsed_expr) {
1533+
bool user_import = false;
15271534
bool retry = false;
15281535
handleAllErrors(parsed_expr.takeError(),
15291536
[&](const ModuleImportError &MIE) {
1537+
if (MIE.is_explicit) {
1538+
// A new dylib may have poisoned the context.
1539+
retry = true;
1540+
user_import = true;
1541+
}
15301542
if (swift_ast_ctx->GetClangImporter())
15311543
// Already on backup power.
15321544
diagnostic_manager.PutString(eDiagnosticSeverityError,
1533-
MIE.Message);
1545+
MIE.message());
15341546
else
15351547
// Discard the shared scratch context and retry.
15361548
retry = true;
@@ -1552,7 +1564,8 @@ unsigned SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
15521564
// fresh SwiftASTContext initialized with the flags from the
15531565
// current lldb::Module / Swift dylib to avoid header search
15541566
// mismatches.
1555-
m_sc.target_sp->SetUseScratchTypesystemPerModule(true);
1567+
if (!user_import)
1568+
m_sc.target_sp->SetUseScratchTypesystemPerModule(true);
15561569
return 2;
15571570
}
15581571

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
public func f() -> String { return "Hello from the Dylib!" }

lldb/test/API/lang/swift/playgrounds/Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ SWIFT_SOURCES = PlaygroundStub.swift
66
LD_EXTRAS := -Xlinker -rpath -Xlinker /usr/lib/swift
77
LD_EXTRAS += -L. -lPlaygroundsRuntime
88

9-
PlaygroundStub: libPlaygroundsRuntime.dylib
9+
PlaygroundStub: libPlaygroundsRuntime.dylib Dylib.framework
1010

1111
include Makefile.rules
1212

@@ -15,3 +15,9 @@ libPlaygroundsRuntime.dylib: PlaygroundsRuntime.swift
1515
DYLIB_SWIFT_SOURCES=PlaygroundsRuntime.swift \
1616
DYLIB_NAME=PlaygroundsRuntime
1717

18+
Dylib.framework: Dylib.swift
19+
$(MAKE) -f $(MAKEFILE_RULES) \
20+
FRAMEWORK=Dylib \
21+
DYLIB_SWIFT_SOURCES=Dylib.swift \
22+
DYLIB_NAME=Dylib \
23+
SWIFTFLAGS_EXTRAS="-Xcc -DNEW_OPTION_FROM_DYLIB=1"

lldb/test/API/lang/swift/playgrounds/TestPlaygrounds.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ def do_test(self, force_target):
113113
process, breakpoint)
114114

115115
self.assertEqual(len(threads), 1)
116+
self.expect('settings set target.swift-framework-search-paths "%s"' %
117+
self.getBuildDir())
116118

117119
contents = ""
118120

@@ -147,4 +149,24 @@ def do_test(self, force_target):
147149
ret = self.frame().EvaluateExpression("get_output()")
148150
playground_output = ret.GetSummary()
149151
self.assertTrue(playground_output is not None)
150-
self.assertTrue("=\\'23\\'" in playground_output)
152+
self.assertIn("=\\'23\\'", playground_output)
153+
154+
# Test importing a library that adds new Clang options.
155+
log = self.getBuildArtifact('types.log')
156+
self.expect('log enable lldb types -f ' + log)
157+
contents = "import Dylib\nf()\n"
158+
res = self.frame().EvaluateExpression(contents, options)
159+
ret = self.frame().EvaluateExpression("get_output()")
160+
playground_output = ret.GetSummary()
161+
self.assertTrue(playground_output is not None)
162+
self.assertIn("Hello from the Dylib", playground_output)
163+
164+
# Scan through the types log to make sure the SwiftASTContext was poisoned.
165+
logfile = open(log, "r")
166+
found = 0
167+
for line in logfile:
168+
if 'New Swift image added' in line \
169+
and 'Versions/A/Dylib' in line \
170+
and 'ClangImporter needs to be reinitialized' in line:
171+
found += 1
172+
self.assertEqual(found, 1)

0 commit comments

Comments
 (0)