Skip to content

Commit 44cecca

Browse files
authored
Merge pull request #1302 from vedantk/gardening
[gardening] Extend TestSwiftDynamicSelf.py, modernize WrapExpression
2 parents 1a9bad5 + 1304f50 commit 44cecca

File tree

3 files changed

+113
-65
lines changed

3 files changed

+113
-65
lines changed

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

Lines changed: 68 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -90,27 +90,41 @@ void SwiftASTManipulator::WrapExpression(
9090
__builtin_logger_initialize()
9191
)";
9292

93-
// The debug function declarations need only be declared once per session - on the first REPL call.
94-
// This code assumes that the first call is the first REPL call; don't call playground once then playground || repl again
93+
// The debug function declarations need only be declared once per session -
94+
// on the first REPL call. This code assumes that the first call is the
95+
// first REPL call; don't call playground once then playground || repl
96+
// again
9597
bool first_expression = options.GetPreparePlaygroundStubFunctions();
9698

97-
const char *playground_prefix = first_expression ? playground_logger_declarations : "";
99+
const char *playground_prefix =
100+
first_expression ? playground_logger_declarations : "";
98101

99102
if (pound_file && pound_line) {
100103
wrapped_stream.Printf("%s#sourceLocation(file: \"%s\", line: %u)\n%s\n",
101104
playground_prefix, pound_file, pound_line,
102105
orig_text);
103106
} else {
104-
// In 2017+, xcode playgrounds send orig_text that starts with a module loading prefix (not the above prefix), then a sourceLocation specifier that indicates the page name, and then the page body text.
105-
// The first_body_line mechanism in this function cannot be used to compensate for the playground_prefix added here, since it incorrectly continues to apply even after sourceLocation directives are read frmo the orig_text.
106-
// To make sure playgrounds work correctly whether or not they supply their own sourceLocation, create a dummy sourceLocation here with a fake filename that starts counting the first line of orig_text as line 1.
107+
// In 2017+, xcode playgrounds send orig_text that starts with a module
108+
// loading prefix (not the above prefix), then a sourceLocation specifier
109+
// that indicates the page name, and then the page body text. The
110+
// first_body_line mechanism in this function cannot be used to
111+
// compensate for the playground_prefix added here, since it incorrectly
112+
// continues to apply even after sourceLocation directives are read from
113+
// the orig_text. To make sure playgrounds work correctly whether or not
114+
// they supply their own sourceLocation, create a dummy sourceLocation
115+
// here with a fake filename that starts counting the first line of
116+
// orig_text as line 1.
107117
wrapped_stream.Printf("%s#sourceLocation(file: \"%s\", line: %u)\n%s\n",
108118
playground_prefix, "Playground.swift", 1,
109119
orig_text);
110120
}
111121
first_body_line = 1;
112122
return;
113-
} else if (repl) { // repl but not playground.
123+
}
124+
125+
assert(!playground && "Playground mode not expected");
126+
127+
if (repl) {
114128
if (pound_file && pound_line) {
115129
wrapped_stream.Printf("#sourceLocation(file: \"%s\", line: %u)\n%s\n",
116130
llvm::sys::path::filename(pound_file).str().c_str(),
@@ -122,87 +136,85 @@ __builtin_logger_initialize()
122136
return;
123137
}
124138

125-
std::string expr_source_path;
139+
assert(!playground && !repl && "Playground/REPL mode not expected");
126140

127141
if (pound_file && pound_line) {
128142
fixed_text.Printf("#sourceLocation(file: \"%s\", line: %u)\n%s\n",
129143
pound_file, pound_line, orig_text);
130144
text = fixed_text.GetString().data();
131145
} else if (generate_debug_info) {
146+
std::string expr_source_path;
132147
if (SwiftASTManipulator::SaveExpressionTextToTempFile(orig_text, options,
133-
expr_source_path)) {
148+
expr_source_path)) {
134149
fixed_text.Printf("#sourceLocation(file: \"%s\", line: 1)\n%s\n",
135150
expr_source_path.c_str(), orig_text);
136151
text = fixed_text.GetString().data();
137152
}
138153
}
139154

140155
// Note: All the wrapper functions we make are marked with the
141-
// @LLDBDebuggerFunction macro so that the compiler
142-
// can do whatever special treatment it need to do on them. If you add new
143-
// variants be sure to mark them this way.
144-
// Also, any function that might end up being in an extension of swift class
145-
// needs to be marked final, since otherwise
146-
// the compiler might try to dispatch them dynamically, which it can't do
147-
// correctly for these functions.
148-
149-
llvm::SmallString<32> buffer;
150-
llvm::raw_svector_ostream os(buffer);
156+
// @LLDBDebuggerFunction macro so that the compiler can do whatever special
157+
// treatment it need to do on them. If you add new variants be sure to mark
158+
// them this way. Also, any function that might end up being in an extension
159+
// of swift class needs to be marked final, since otherwise the compiler
160+
// might try to dispatch them dynamically, which it can't do correctly for
161+
// these functions.
162+
163+
std::string availability = "";
151164
if (!os_version.empty())
152-
os << "@available(" << os_version << ", *)";
153-
std::string availability = os.str();
165+
availability = (llvm::Twine("@available(") + os_version + ", *)").str();
154166

155167
StreamString wrapped_expr_text;
156-
wrapped_expr_text.Printf("do\n"
157-
"{\n"
158-
"%s%s%s\n" // Don't indent the code so error columns
159-
// match up with errors from compiler
160-
"}\n"
161-
"catch (let __lldb_tmp_error)\n"
162-
"{\n"
163-
" var %s = __lldb_tmp_error\n"
164-
"}\n",
168+
169+
// Avoid indenting user code: this makes column information from compiler
170+
// errors match up with what the user typed.
171+
wrapped_expr_text.Printf(R"(
172+
do {
173+
%s%s%s
174+
} catch (let __lldb_tmp_error) {
175+
var %s = __lldb_tmp_error
176+
}
177+
)",
165178
GetUserCodeStartMarker(), text,
166179
GetUserCodeEndMarker(), GetErrorName());
167180

168-
if (needs_object_ptr | static_method) {
181+
if (needs_object_ptr || static_method) {
169182
const char *func_decorator = "";
170183
if (static_method) {
171184
if (is_class)
172185
func_decorator = "final class";
173186
else
174187
func_decorator = "static";
175-
} else if (is_class &&
176-
!(weak_self)) {
188+
} else if (is_class && !weak_self) {
177189
func_decorator = "final";
178190
} else {
179191
func_decorator = "mutating";
180192
}
181193

182194
const char *optional_extension =
183-
(weak_self)
184-
? "Swift.Optional where Wrapped == "
185-
: "";
186-
187-
wrapped_stream.Printf(
188-
"extension %s$__lldb_context {\n"
189-
" @LLDBDebuggerFunction %s\n"
190-
" %s func $__lldb_wrapped_expr_%u(_ $__lldb_arg : "
191-
"UnsafeMutablePointer<Any>) {\n"
192-
"%s" // This is the expression text (with newlines).
193-
" }\n"
194-
"}\n"
195-
"%s\n"
196-
"func $__lldb_expr(_ $__lldb_arg : UnsafeMutablePointer<Any>) {\n"
197-
" do {\n"
198-
" $__lldb_injected_self.$__lldb_wrapped_expr_%u(\n"
199-
" $__lldb_arg\n"
200-
" )\n"
201-
" }\n"
202-
"}\n",
203-
optional_extension, availability.c_str(), func_decorator,
204-
current_counter, wrapped_expr_text.GetData(), availability.c_str(),
205-
current_counter);
195+
weak_self ? "Swift.Optional where Wrapped == " : "";
196+
197+
// The expression text is inserted into the body of $__lldb_wrapped_expr_%u.
198+
wrapped_stream.Printf(R"(
199+
extension %s$__lldb_context {
200+
@LLDBDebuggerFunction %s
201+
%s func $__lldb_wrapped_expr_%u(_ $__lldb_arg : UnsafeMutablePointer<Any>) {
202+
%s
203+
}
204+
}
205+
%s
206+
func $__lldb_expr(_ $__lldb_arg : UnsafeMutablePointer<Any>) {
207+
do {
208+
$__lldb_injected_self.$__lldb_wrapped_expr_%u(
209+
$__lldb_arg
210+
)
211+
}
212+
}
213+
)",
214+
optional_extension, availability.c_str(),
215+
func_decorator, current_counter,
216+
wrapped_expr_text.GetData(), availability.c_str(),
217+
current_counter);
206218

207219
first_body_line = 5;
208220
} else {

lldb/test/API/lang/swift/dynamic_self/TestSwiftDynamicSelf.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,47 @@ class TestSwiftDynamicSelf(lldbtest.TestBase):
99

1010
mydir = lldbtest.TestBase.compute_mydir(__file__)
1111

12-
@swiftTest
13-
def test_dynamic_self(self):
14-
self.build()
15-
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
16-
self, 'break here', lldb.SBFileSpec('main.swift'))
12+
def get_self_from_Base_method(self, frame):
13+
'''When stopped in a method in Base, get the 'self' with type Base.'''
14+
var_self = frame.FindVariable("self")
15+
self.assertEqual(var_self.GetNumChildren(), 2)
16+
return var_self
1717

18-
frame = thread.frames[0]
18+
def get_self_as_Base_from_Child_method(self, frame):
19+
'''When stopped in a method in Child, get the 'self' with type Base.'''
1920
var_self = frame.FindVariable("self")
20-
self.assertEqual(var_self.GetNumChildren(), 0)
2121
dyn_self = var_self.GetDynamicValue(True)
2222
self.assertEqual(dyn_self.GetNumChildren(), 1)
2323
var_self_base = dyn_self.GetChildAtIndex(0)
24+
self.assertEqual(var_self_base.GetNumChildren(), 2)
25+
return var_self_base
26+
27+
def check_members(self, var_self_base, c_val, v_val):
2428
member_c = var_self_base.GetChildMemberWithName("c")
2529
member_v = var_self_base.GetChildMemberWithName("v")
26-
lldbutil.check_variable(self, member_c, False, value="100")
27-
lldbutil.check_variable(self, member_v, False, value="210")
30+
lldbutil.check_variable(self, member_c, False, value=c_val)
31+
lldbutil.check_variable(self, member_v, False, value=v_val)
32+
33+
@swiftTest
34+
def test_dynamic_self(self):
35+
self.build()
36+
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
37+
self, 'break here', lldb.SBFileSpec('main.swift'))
38+
39+
# In Base.init.
40+
frame = thread.frames[0]
41+
self.check_members(self.get_self_from_Base_method(frame), "100", "200")
42+
43+
lldbutil.continue_to_breakpoint(process, bkpt) # Stop in Child.init.
44+
frame = thread.frames[0]
45+
# When stopped in Child.init(), the first child of 'self' is 'a.Base'.
46+
self.assertEqual(frame.FindVariable("self").GetNumChildren(), 1)
47+
self.check_members(self.get_self_as_Base_from_Child_method(frame),
48+
"100", "210")
49+
50+
lldbutil.continue_to_breakpoint(process, bkpt) # Stop in Child.show.
51+
frame = thread.frames[0]
52+
# When stopped in Child.show(), 'self' doesn't have a child.
53+
self.assertEqual(frame.FindVariable("self").GetNumChildren(), 0)
54+
self.check_members(self.get_self_as_Base_from_Child_method(frame),
55+
"100", "220")

lldb/test/API/lang/swift/dynamic_self/main.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
class Base {
2+
init() {
3+
print("Base: c = \(c), v = \(v)") // break here
4+
}
25
func show() -> Self {
36
return self
47
}
@@ -9,6 +12,11 @@ class Base {
912
func use<T>(_ t: T) {}
1013

1114
class Child : Base {
15+
override init() {
16+
super.init()
17+
v += 10
18+
print("Child: c = \(c), v = \(v)") // break here
19+
}
1220
override func show() -> Self {
1321
v += 10
1422
use((self.c, self.v)) // break here

0 commit comments

Comments
 (0)