Skip to content

[lldb] Restore Playground functionality #10946

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

Merged
merged 2 commits into from
Jul 2, 2025
Merged
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: 2 additions & 1 deletion lldb/source/Plugins/ExpressionParser/Swift/SwiftREPL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,8 @@ Status SwiftREPL::DoInitialization() {
return Status::FromError(type_system_or_err.takeError());
std::static_pointer_cast<TypeSystemSwiftTypeRefForExpressions>(
*type_system_or_err)
->SetCompilerOptions(m_compiler_options.c_str());
->SetCompilerOptions(/*repl=*/true, /*playgrounds=*/false,
m_compiler_options.c_str());

std::string format_str = "${ansi.negative}Swift " +
swift::version::getCompilerVersion() +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,9 @@ bool SwiftUserExpression::Parse(DiagnosticManager &diagnostic_manager,
"unknown error");
// Notify SwiftASTContext that this is a Playground.
if (m_options.GetPlaygroundTransformEnabled())
m_swift_scratch_ctx->SetCompilerOptions("");
m_swift_scratch_ctx->SetCompilerOptions(
m_options.GetREPLEnabled(), m_options.GetPlaygroundTransformEnabled(),
"");

// For playgrounds, the target triple should be used for expression
// evaluation, not the current module. This requires disabling precise
Expand Down
43 changes: 24 additions & 19 deletions lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2689,16 +2689,14 @@ static lldb::ModuleSP GetUnitTestModule(lldb_private::ModuleList &modules) {
return ModuleSP();
}

lldb::TypeSystemSP
SwiftASTContext::CreateInstance(const SymbolContext &sc,
TypeSystemSwiftTypeRef &typeref_typesystem,
const char *extra_options) {
bool is_repl = extra_options;
lldb::TypeSystemSP SwiftASTContext::CreateInstance(
const SymbolContext &sc, TypeSystemSwiftTypeRef &typeref_typesystem,
bool repl, bool playground, const char *extra_options) {
bool for_expressions =
llvm::isa<TypeSystemSwiftTypeRefForExpressions>(&typeref_typesystem);
// REPL requires an expression type system.
assert(!is_repl || for_expressions);
if (is_repl && !for_expressions)
assert(!repl || for_expressions);
if (repl && !for_expressions)
return {};

if (!ModuleList::GetGlobalModuleListProperties()
Expand All @@ -2707,26 +2705,24 @@ SwiftASTContext::CreateInstance(const SymbolContext &sc,

CompileUnit *cu = sc.comp_unit;
const char *key = TypeSystemSwiftTypeRef::DeriveKeyFor(sc);
bool swift_context = cu && cu->GetLanguage() == eLanguageTypeSwift;
std::string m_description;
{
StreamString ss;
ss << "SwiftASTContext";
if (for_expressions)
ss << "ForExpressions";
ss << "(module: " << '"' << key << "\", " << "cu: " << '"';
if (cu)
if (cu && swift_context)
ss << cu->GetPrimaryFile().GetFilename();
else
ss << "null";
ss << "*";
ss << '"' << ')';
m_description = ss.GetString();
}

LLDB_SCOPED_TIMERF("%s::CreateInstance", m_description.c_str());

if (is_repl)
LOG_PRINTF(GetLog(LLDBLog::Types), "REPL detected");

// This function can either create an expression/scratch/repl context,
// or a SwiftAST fallback context for a TypeSystemSwiftTyperef.
// - SwiftASTContexForExpressions: target=non-null, module=null.
Expand Down Expand Up @@ -2770,8 +2766,9 @@ SwiftASTContext::CreateInstance(const SymbolContext &sc,
if (ShouldEnableEmbeddedSwift(cu))
lang_opts.enableFeature(swift::Feature::Embedded);
}
auto defer_log = llvm::make_scope_exit(
[swift_ast_sp, is_repl] { swift_ast_sp->LogConfiguration(is_repl); });
auto defer_log = llvm::make_scope_exit([swift_ast_sp, repl, playground] {
swift_ast_sp->LogConfiguration(repl, playground);
});

LOG_PRINTF(GetLog(LLDBLog::Types), "(Target)");
auto logError = [&](const char *message) {
Expand All @@ -2797,7 +2794,12 @@ SwiftASTContext::CreateInstance(const SymbolContext &sc,
ModuleList module_module;
if (!target_sp)
module_module.Append(module_sp);
ModuleList &modules = target_sp ? target_sp->GetImages() : module_module;
// Leave modules empty if not in a Swift context to avoid a fragile
// and expensive scan through all images. Unless this is a Playground, which
// has a non-Swift executable, and user code in a framework.
ModuleList &modules = (target_sp && (swift_context || playground))
? target_sp->GetImages()
: module_module;
const size_t num_images = modules.GetSize();

// Set the SDK path prior to doing search paths. Otherwise when we
Expand Down Expand Up @@ -2869,7 +2871,7 @@ SwiftASTContext::CreateInstance(const SymbolContext &sc,

ArchSpec preferred_arch;
llvm::Triple preferred_triple;
if (is_repl) {
if (repl) {
LOG_PRINTF(GetLog(LLDBLog::Types), "REPL: prefer target triple.");
preferred_arch = target_arch;
preferred_triple = target_triple;
Expand Down Expand Up @@ -3133,7 +3135,8 @@ SwiftASTContext::CreateInstance(const SymbolContext &sc,
}
}
};
scan_module(module_sp, 0);
if (swift_context || playground)
scan_module(module_sp, 0);
for (size_t mi = 0; mi != num_images; ++mi) {
auto image_sp = modules.GetModuleAtIndex(mi);
if (!visited_modules.count(image_sp.get()))
Expand Down Expand Up @@ -5461,7 +5464,7 @@ void SwiftASTContext::ClearModuleDependentCaches() {
m_negative_type_cache.Clear();
}

void SwiftASTContext::LogConfiguration(bool is_repl) {
void SwiftASTContext::LogConfiguration(bool repl, bool playground) {
// It makes no sense to call VALID_OR_RETURN here. We specifically
// want the logs in the error case!
HEALTH_LOG_PRINTF("(SwiftASTContext*)%p:", static_cast<void *>(this));
Expand All @@ -5471,8 +5474,10 @@ void SwiftASTContext::LogConfiguration(bool is_repl) {
HEALTH_LOG_PRINTF(" (no AST context)");
return;
}
if (is_repl)
if (repl)
HEALTH_LOG_PRINTF(" REPL : true");
if (playground)
HEALTH_LOG_PRINTF(" Playground : true");
HEALTH_LOG_PRINTF(" Swift/C++ interop : %s",
ast_context->LangOpts.EnableCXXInterop ? "on" : "off");
HEALTH_LOG_PRINTF(" Swift/Objective-C interop : %s",
Expand Down
6 changes: 3 additions & 3 deletions lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ class SwiftASTContext : public TypeSystemSwift {
/// context.
static lldb::TypeSystemSP
CreateInstance(const SymbolContext &sc,
TypeSystemSwiftTypeRef &typeref_typesystem,
const char *extra_options = nullptr);
TypeSystemSwiftTypeRef &typeref_typesystem, bool repl = false,
bool playground = false, const char *extra_options = nullptr);

static void EnumerateSupportedLanguages(
std::set<lldb::LanguageType> &languages_for_types,
Expand Down Expand Up @@ -539,7 +539,7 @@ class SwiftASTContext : public TypeSystemSwift {
swift::TBDGenOptions &GetTBDGenOptions();

void ClearModuleDependentCaches() override;
void LogConfiguration(bool is_repl = false);
void LogConfiguration(bool repl = false, bool playground = false);
bool HasTarget();
bool HasExplicitModules() const { return m_has_explicit_modules; }
bool CheckProcessChanged();
Expand Down
4 changes: 2 additions & 2 deletions lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ static lldb::TypeSystemSP CreateTypeSystemInstance(lldb::LanguageType language,
} else if (target) {
assert(!module);
return std::shared_ptr<TypeSystemSwiftTypeRefForExpressions>(
new TypeSystemSwiftTypeRefForExpressions(language, *target,
extra_options));
new TypeSystemSwiftTypeRefForExpressions(language, *target, false,
false, extra_options));
}
llvm_unreachable("Neither type nor module given to CreateTypeSystemInstance");
}
Expand Down
28 changes: 16 additions & 12 deletions lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1963,24 +1963,26 @@ TypeSystemSwiftTypeRef::TypeSystemSwiftTypeRef(Module &module) {
}

TypeSystemSwiftTypeRefForExpressions::TypeSystemSwiftTypeRefForExpressions(
lldb::LanguageType language, Target &target, const char *extra_options)
lldb::LanguageType language, Target &target, bool repl, bool playground,
const char *extra_options)
: m_target_wp(target.shared_from_this()),
m_persistent_state_up(new SwiftPersistentExpressionState) {
m_description = "TypeSystemSwiftTypeRefForExpressions";
LLDB_LOGF(GetLog(LLDBLog::Types),
"%s::TypeSystemSwiftTypeRefForExpressions()",
m_description.c_str());
// Is this a REPL or Playground?
if (extra_options) {
assert(!repl && !playground && !extra_options && "use SetCompilerOptions()");
if (repl || playground || extra_options) {
SymbolContext global_sc(target.shared_from_this(),
target.GetExecutableModule());
const char *key = DeriveKeyFor(global_sc);
m_swift_ast_context_map.insert(
{key,
{SwiftASTContext::CreateInstance(
global_sc,
*const_cast<TypeSystemSwiftTypeRefForExpressions *>(this),
extra_options),
*const_cast<TypeSystemSwiftTypeRefForExpressions *>(this), repl,
playground, extra_options),
0}});
}
}
Expand Down Expand Up @@ -2072,15 +2074,14 @@ ConstString TypeSystemSwiftTypeRef::GetSwiftModuleFor(const SymbolContext &sc) {
}

const char *TypeSystemSwiftTypeRef::DeriveKeyFor(const SymbolContext &sc) {
if (sc.function)
if (sc.comp_unit && sc.comp_unit->GetLanguage() == eLanguageTypeSwift)
if (ConstString name = GetSwiftModuleFor(sc))
return name.GetCString();

if (sc.module_sp) {
if (sc.module_sp->GetFileSpec())
return sc.module_sp->GetFileSpec().GetFilename().GetCString();
return sc.module_sp->GetObjectName().GetCString();
}
// Otherwise create a catch-all context per unique triple.
if (sc.module_sp)
return ConstString(sc.module_sp->GetArchitecture().GetTriple().str()).AsCString();

return nullptr;
}

Expand Down Expand Up @@ -2184,8 +2185,8 @@ SwiftASTContextSP TypeSystemSwiftTypeRefForExpressions::GetSwiftASTContext(

// Create a new SwiftASTContextForExpressions.
ts = SwiftASTContext::CreateInstance(
sc, *const_cast<TypeSystemSwiftTypeRefForExpressions *>(this),
m_compiler_options);
sc, *const_cast<TypeSystemSwiftTypeRefForExpressions *>(this), m_repl,
m_playground, m_compiler_options);
m_swift_ast_context_map.insert({key, {ts, retry_count}});
}

Expand Down Expand Up @@ -2563,6 +2564,9 @@ template <> bool Equivalent<CompilerType>(CompilerType l, CompilerType r) {
ConstString rhs = r.GetMangledTypeName();
if (lhs == ConstString("$sSiD") && rhs == ConstString("$sSuD"))
return true;
if (lhs.GetStringRef() == "$sSPySo0023unnamedstruct_hEEEdhdEaVGSgD" &&
rhs.GetStringRef() == "$ss13OpaquePointerVSgD")
return true;
// Ignore missing sugar.
swift::Demangle::Demangler dem;
auto l_node = GetDemangledType(dem, lhs.GetStringRef());
Expand Down
10 changes: 8 additions & 2 deletions lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,8 @@ class TypeSystemSwiftTypeRefForExpressions : public TypeSystemSwiftTypeRef {
/// \}

TypeSystemSwiftTypeRefForExpressions(lldb::LanguageType language,
Target &target,
Target &target, bool repl,
bool playground,
const char *extra_options);

static TypeSystemSwiftTypeRefForExpressionsSP GetForTarget(Target &target);
Expand All @@ -638,7 +639,10 @@ class TypeSystemSwiftTypeRefForExpressions : public TypeSystemSwiftTypeRef {
GetSwiftASTContextOrNull(const SymbolContext &sc) const override;
/// This API needs to be called for a REPL or Playground before the first call
/// to GetSwiftASTContext is being made.
void SetCompilerOptions(const char *compiler_options) {
void SetCompilerOptions(bool repl, bool playground,
const char *compiler_options) {
m_repl = repl;
m_playground = playground;
m_compiler_options = compiler_options;
}
lldb::TargetWP GetTargetWP() const override { return m_target_wp; }
Expand Down Expand Up @@ -668,6 +672,8 @@ class TypeSystemSwiftTypeRefForExpressions : public TypeSystemSwiftTypeRef {
protected:
lldb::TargetWP m_target_wp;
unsigned m_generation = 0;
bool m_repl = false;
bool m_playground = false;
const char *m_compiler_options = nullptr;

/// This exists to implement the PerformCompileUnitImports
Expand Down
1 change: 1 addition & 0 deletions lldb/test/API/lang/swift/playgrounds/AuxSources.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// This file intentionally left blank.
1 change: 1 addition & 0 deletions lldb/test/API/lang/swift/playgrounds/Import.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import AuxSources
import Dylib
f()
let comment = "and back again"
13 changes: 11 additions & 2 deletions lldb/test/API/lang/swift/playgrounds/Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
EXE = PlaygroundStub
SWIFT_SOURCES = PlaygroundStub.swift
# The real playground stub has no debug info and is written in C.
SWIFTFLAGS_EXTRAS = -gnone

# The deployment target we set is pre-ABI stability. The Swift driver will not
# point the RPATH at the system library. Do it manually.
LD_EXTRAS := -Xlinker -rpath -Xlinker /usr/lib/swift
LD_EXTRAS += -L. -lPlaygroundsRuntime
LD_EXTRAS += -L. -lPlaygroundsRuntime -F. -framework AuxSources

PlaygroundStub: libPlaygroundsRuntime.dylib Dylib.framework
PlaygroundStub: libPlaygroundsRuntime.dylib Dylib.framework AuxSources.framework

include Makefile.rules

Expand All @@ -21,3 +23,10 @@ Dylib.framework: Dylib.swift
DYLIB_SWIFT_SOURCES=Dylib.swift \
DYLIB_NAME=Dylib \
SWIFTFLAGS_EXTRAS="-Xcc -DNEW_OPTION_FROM_DYLIB=1"

AuxSources.framework: AuxSources.swift
"$(MAKE)" -f $(MAKEFILE_RULES) \
FRAMEWORK=AuxSources \
DYLIB_SWIFT_SOURCES=AuxSources.swift \
DYLIB_NAME=AuxSources \
SWIFTFLAGS_EXTRAS="-Xcc -DHAVE_AUXSOURCES=1"
7 changes: 5 additions & 2 deletions lldb/test/API/lang/swift/playgrounds/PlaygroundStub.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
// -----------------------------------------------------------------------------

@_silgen_name ("playground_logger_initialize") func builtin_initialize();
@_silgen_name ("GetOutput") func get_output() -> String;

builtin_initialize();

print(""); // Set breakpoint here
func break_here() {
print("This is the frame where the playground expression will be executed.")
}

break_here()
14 changes: 11 additions & 3 deletions lldb/test/API/lang/swift/playgrounds/TestSwiftPlaygrounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ def launch(self, force_target):
['libPlaygroundsRuntime.dylib'])

# Set the breakpoints
breakpoint = target.BreakpointCreateBySourceRegex(
'Set breakpoint here', lldb.SBFileSpec("PlaygroundStub.swift"))
breakpoint = target.BreakpointCreateByName('break_here')
self.assertTrue(breakpoint.GetNumLocations() > 0, VALID_BREAKPOINT)

process = target.LaunchSimple(None, None, os.getcwd())
Expand All @@ -147,7 +146,11 @@ def execute_code(self, input_file, expect_error=False):
options.SetAutoApplyFixIts(False)

res = self.frame().EvaluateExpression(contents, options)
ret = self.frame().EvaluateExpression("get_output()")

options = lldb.SBExpressionOptions()
options.SetLanguage(lldb.eLanguageTypeSwift)
self.frame().EvaluateExpression("import PlaygroundsRuntime", options)
ret = self.frame().EvaluateExpression("get_output()", options)
is_error = res.GetError().Fail() and not (
res.GetError().GetType() == 1 and
res.GetError().GetError() == 0x1001)
Expand Down Expand Up @@ -196,4 +199,9 @@ def do_import_test(self):

# Scan through the types log to make sure the SwiftASTContext was poisoned.
self.filecheck('platform shell cat ""%s"' % log, __file__)
# CHECK: RegisterSectionModules("AuxSources")
# CHECK: Playground : true
# If we wanted this to work, SwiftASTContext would need to find the AuxSources image and switch the symbol context to there.
# CHECK-NOT: -DHAVE_AUXSOURCES
# CHECK: Module import remark{{.*}} loaded module 'AuxSources'; source: 'AuxSources', loaded: 'AuxSources'
# CHECK: New Swift image added{{.*}}Versions/A/Dylib{{.*}}ClangImporter needs to be reinitialized
3 changes: 3 additions & 0 deletions lldb/test/API/lang/swift/po/objc/Base.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import Foundation;
@interface Base : NSObject
@end
15 changes: 15 additions & 0 deletions lldb/test/API/lang/swift/po/objc/Foo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation
import Base

private class Bar : Base {
}

extension Bar {
override var debugDescription : String { "Hello from Swift" }
}

@objc public class Foo : NSObject {
@objc public func getBase() -> Base? {
return Bar()
}
}
9 changes: 9 additions & 0 deletions lldb/test/API/lang/swift/po/objc/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
OBJC_SOURCES := main.m
CFLAGS_EXTRAS = $(MANDATORY_MODULE_BUILD_CFLAGS) -I. -I$(BUILDDIR)

SWIFT_SOURCES := Foo.swift
SWIFTFLAGS_EXTRAS = -Xcc -I$(SRCDIR) -emit-objc-header-path Foo.h -parse-as-library

all: Foo.swift.o $(EXE)

include Makefile.rules
Loading