File: | build/source/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp |
Warning: | line 426, column 11 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-- ClangExpressionSourceCode.cpp -------------------------------------===// | |||
2 | // | |||
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||
4 | // See https://llvm.org/LICENSE.txt for license information. | |||
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||
6 | // | |||
7 | //===----------------------------------------------------------------------===// | |||
8 | ||||
9 | #include "ClangExpressionSourceCode.h" | |||
10 | ||||
11 | #include "ClangExpressionUtil.h" | |||
12 | ||||
13 | #include "clang/Basic/CharInfo.h" | |||
14 | #include "clang/Basic/FileManager.h" | |||
15 | #include "clang/Basic/SourceManager.h" | |||
16 | #include "clang/Lex/Lexer.h" | |||
17 | #include "llvm/ADT/ScopeExit.h" | |||
18 | #include "llvm/ADT/StringRef.h" | |||
19 | ||||
20 | #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" | |||
21 | #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" | |||
22 | #include "lldb/Symbol/Block.h" | |||
23 | #include "lldb/Symbol/CompileUnit.h" | |||
24 | #include "lldb/Symbol/DebugMacros.h" | |||
25 | #include "lldb/Symbol/TypeSystem.h" | |||
26 | #include "lldb/Symbol/VariableList.h" | |||
27 | #include "lldb/Target/ExecutionContext.h" | |||
28 | #include "lldb/Target/Language.h" | |||
29 | #include "lldb/Target/Platform.h" | |||
30 | #include "lldb/Target/StackFrame.h" | |||
31 | #include "lldb/Target/Target.h" | |||
32 | #include "lldb/Utility/StreamString.h" | |||
33 | #include "lldb/lldb-forward.h" | |||
34 | ||||
35 | using namespace lldb_private; | |||
36 | ||||
37 | #define PREFIX_NAME"<lldb wrapper prefix>" "<lldb wrapper prefix>" | |||
38 | #define SUFFIX_NAME"<lldb wrapper suffix>" "<lldb wrapper suffix>" | |||
39 | ||||
40 | const llvm::StringRef ClangExpressionSourceCode::g_prefix_file_name = PREFIX_NAME"<lldb wrapper prefix>"; | |||
41 | ||||
42 | const char *ClangExpressionSourceCode::g_expression_prefix = | |||
43 | "#line 1 \"" PREFIX_NAME"<lldb wrapper prefix>" R"(" | |||
44 | #ifndef offsetof | |||
45 | #define offsetof(t, d) __builtin_offsetof(t, d) | |||
46 | #endif | |||
47 | #ifndef NULL | |||
48 | #define NULL (__null) | |||
49 | #endif | |||
50 | #ifndef Nil | |||
51 | #define Nil (__null) | |||
52 | #endif | |||
53 | #ifndef nil | |||
54 | #define nil (__null) | |||
55 | #endif | |||
56 | #ifndef YES | |||
57 | #define YES ((BOOL)1) | |||
58 | #endif | |||
59 | #ifndef NO | |||
60 | #define NO ((BOOL)0) | |||
61 | #endif | |||
62 | typedef __INT8_TYPE__ int8_t; | |||
63 | typedef __UINT8_TYPE__ uint8_t; | |||
64 | typedef __INT16_TYPE__ int16_t; | |||
65 | typedef __UINT16_TYPE__ uint16_t; | |||
66 | typedef __INT32_TYPE__ int32_t; | |||
67 | typedef __UINT32_TYPE__ uint32_t; | |||
68 | typedef __INT64_TYPE__ int64_t; | |||
69 | typedef __UINT64_TYPE__ uint64_t; | |||
70 | typedef __INTPTR_TYPE__ intptr_t; | |||
71 | typedef __UINTPTR_TYPE__ uintptr_t; | |||
72 | typedef __SIZE_TYPE__ size_t; | |||
73 | typedef __PTRDIFF_TYPE__ ptrdiff_t; | |||
74 | typedef unsigned short unichar; | |||
75 | extern "C" | |||
76 | { | |||
77 | int printf(const char * __restrict, ...); | |||
78 | } | |||
79 | )"; | |||
80 | ||||
81 | const char *ClangExpressionSourceCode::g_expression_suffix = | |||
82 | "\n;\n#line 1 \"" SUFFIX_NAME"<lldb wrapper suffix>" "\"\n"; | |||
83 | ||||
84 | namespace { | |||
85 | ||||
86 | class AddMacroState { | |||
87 | enum State { | |||
88 | CURRENT_FILE_NOT_YET_PUSHED, | |||
89 | CURRENT_FILE_PUSHED, | |||
90 | CURRENT_FILE_POPPED | |||
91 | }; | |||
92 | ||||
93 | public: | |||
94 | AddMacroState(const FileSpec ¤t_file, const uint32_t current_file_line) | |||
95 | : m_current_file(current_file), m_current_file_line(current_file_line) {} | |||
96 | ||||
97 | void StartFile(const FileSpec &file) { | |||
98 | m_file_stack.push_back(file); | |||
99 | if (file == m_current_file) | |||
100 | m_state = CURRENT_FILE_PUSHED; | |||
101 | } | |||
102 | ||||
103 | void EndFile() { | |||
104 | if (m_file_stack.size() == 0) | |||
105 | return; | |||
106 | ||||
107 | FileSpec old_top = m_file_stack.back(); | |||
108 | m_file_stack.pop_back(); | |||
109 | if (old_top == m_current_file) | |||
110 | m_state = CURRENT_FILE_POPPED; | |||
111 | } | |||
112 | ||||
113 | // An entry is valid if it occurs before the current line in the current | |||
114 | // file. | |||
115 | bool IsValidEntry(uint32_t line) { | |||
116 | switch (m_state) { | |||
117 | case CURRENT_FILE_NOT_YET_PUSHED: | |||
118 | return true; | |||
119 | case CURRENT_FILE_PUSHED: | |||
120 | // If we are in file included in the current file, the entry should be | |||
121 | // added. | |||
122 | if (m_file_stack.back() != m_current_file) | |||
123 | return true; | |||
124 | ||||
125 | return line < m_current_file_line; | |||
126 | default: | |||
127 | return false; | |||
128 | } | |||
129 | } | |||
130 | ||||
131 | private: | |||
132 | std::vector<FileSpec> m_file_stack; | |||
133 | State m_state = CURRENT_FILE_NOT_YET_PUSHED; | |||
134 | FileSpec m_current_file; | |||
135 | uint32_t m_current_file_line; | |||
136 | }; | |||
137 | ||||
138 | } // anonymous namespace | |||
139 | ||||
140 | static void AddMacros(const DebugMacros *dm, CompileUnit *comp_unit, | |||
141 | AddMacroState &state, StreamString &stream) { | |||
142 | if (dm == nullptr) | |||
143 | return; | |||
144 | ||||
145 | // The macros directives below can potentially redefine builtin macros of the | |||
146 | // Clang instance which parses the user expression. The Clang diagnostics | |||
147 | // caused by this are not useful for the user as the source code here is | |||
148 | // generated by LLDB. | |||
149 | stream << "#pragma clang diagnostic push\n"; | |||
150 | stream << "#pragma clang diagnostic ignored \"-Wmacro-redefined\"\n"; | |||
151 | stream << "#pragma clang diagnostic ignored \"-Wbuiltin-macro-redefined\"\n"; | |||
152 | auto pop_warning = llvm::make_scope_exit([&stream](){ | |||
153 | stream << "#pragma clang diagnostic pop\n"; | |||
154 | }); | |||
155 | ||||
156 | for (size_t i = 0; i < dm->GetNumMacroEntries(); i++) { | |||
157 | const DebugMacroEntry &entry = dm->GetMacroEntryAtIndex(i); | |||
158 | uint32_t line; | |||
159 | ||||
160 | switch (entry.GetType()) { | |||
161 | case DebugMacroEntry::DEFINE: | |||
162 | if (state.IsValidEntry(entry.GetLineNumber())) | |||
163 | stream.Printf("#define %s\n", entry.GetMacroString().AsCString()); | |||
164 | else | |||
165 | return; | |||
166 | break; | |||
167 | case DebugMacroEntry::UNDEF: | |||
168 | if (state.IsValidEntry(entry.GetLineNumber())) | |||
169 | stream.Printf("#undef %s\n", entry.GetMacroString().AsCString()); | |||
170 | else | |||
171 | return; | |||
172 | break; | |||
173 | case DebugMacroEntry::START_FILE: | |||
174 | line = entry.GetLineNumber(); | |||
175 | if (state.IsValidEntry(line)) | |||
176 | state.StartFile(entry.GetFileSpec(comp_unit)); | |||
177 | else | |||
178 | return; | |||
179 | break; | |||
180 | case DebugMacroEntry::END_FILE: | |||
181 | state.EndFile(); | |||
182 | break; | |||
183 | case DebugMacroEntry::INDIRECT: | |||
184 | AddMacros(entry.GetIndirectDebugMacros(), comp_unit, state, stream); | |||
185 | break; | |||
186 | default: | |||
187 | // This is an unknown/invalid entry. Ignore. | |||
188 | break; | |||
189 | } | |||
190 | } | |||
191 | } | |||
192 | ||||
193 | lldb_private::ClangExpressionSourceCode::ClangExpressionSourceCode( | |||
194 | llvm::StringRef filename, llvm::StringRef name, llvm::StringRef prefix, | |||
195 | llvm::StringRef body, Wrapping wrap, WrapKind wrap_kind) | |||
196 | : ExpressionSourceCode(name, prefix, body, wrap), m_wrap_kind(wrap_kind) { | |||
197 | // Use #line markers to pretend that we have a single-line source file | |||
198 | // containing only the user expression. This will hide our wrapper code | |||
199 | // from the user when we render diagnostics with Clang. | |||
200 | m_start_marker = "#line 1 \"" + filename.str() + "\"\n"; | |||
201 | m_end_marker = g_expression_suffix; | |||
202 | } | |||
203 | ||||
204 | namespace { | |||
205 | /// Allows checking if a token is contained in a given expression. | |||
206 | class TokenVerifier { | |||
207 | /// The tokens we found in the expression. | |||
208 | llvm::StringSet<> m_tokens; | |||
209 | ||||
210 | public: | |||
211 | TokenVerifier(std::string body); | |||
212 | /// Returns true iff the given expression body contained a token with the | |||
213 | /// given content. | |||
214 | bool hasToken(llvm::StringRef token) const { | |||
215 | return m_tokens.contains(token); | |||
216 | } | |||
217 | }; | |||
218 | ||||
219 | // If we're evaluating from inside a lambda that captures a 'this' pointer, | |||
220 | // add a "using" declaration to 'stream' for each capture used in the | |||
221 | // expression (tokenized by 'verifier'). | |||
222 | // | |||
223 | // If no 'this' capture exists, generate no using declarations. Instead | |||
224 | // capture lookups will get resolved by the same mechanism as class member | |||
225 | // variable lookup. That's because Clang generates an unnamed structure | |||
226 | // representing the lambda closure whose members are the captured variables. | |||
227 | void AddLambdaCaptureDecls(StreamString &stream, StackFrame *frame, | |||
228 | TokenVerifier const &verifier) { | |||
229 | assert(frame)(static_cast <bool> (frame) ? void (0) : __assert_fail ( "frame", "lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp" , 229, __extension__ __PRETTY_FUNCTION__)); | |||
230 | ||||
231 | if (auto thisValSP = ClangExpressionUtil::GetLambdaValueObject(frame)) { | |||
232 | uint32_t numChildren = thisValSP->GetNumChildren(); | |||
233 | for (uint32_t i = 0; i < numChildren; ++i) { | |||
234 | auto childVal = thisValSP->GetChildAtIndex(i, true); | |||
235 | ConstString childName(childVal ? childVal->GetName() : ConstString("")); | |||
236 | ||||
237 | if (!childName.IsEmpty() && verifier.hasToken(childName.GetStringRef()) && | |||
238 | childName != "this") { | |||
239 | stream.Printf("using $__lldb_local_vars::%s;\n", | |||
240 | childName.GetCString()); | |||
241 | } | |||
242 | } | |||
243 | } | |||
244 | } | |||
245 | ||||
246 | } // namespace | |||
247 | ||||
248 | TokenVerifier::TokenVerifier(std::string body) { | |||
249 | using namespace clang; | |||
250 | ||||
251 | // We only care about tokens and not their original source locations. If we | |||
252 | // move the whole expression to only be in one line we can simplify the | |||
253 | // following code that extracts the token contents. | |||
254 | std::replace(body.begin(), body.end(), '\n', ' '); | |||
255 | std::replace(body.begin(), body.end(), '\r', ' '); | |||
256 | ||||
257 | FileSystemOptions file_opts; | |||
258 | FileManager file_mgr(file_opts, | |||
259 | FileSystem::Instance().GetVirtualFileSystem()); | |||
260 | ||||
261 | // Let's build the actual source code Clang needs and setup some utility | |||
262 | // objects. | |||
263 | llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs()); | |||
264 | llvm::IntrusiveRefCntPtr<DiagnosticOptions> diags_opts( | |||
265 | new DiagnosticOptions()); | |||
266 | DiagnosticsEngine diags(diag_ids, diags_opts); | |||
267 | clang::SourceManager SM(diags, file_mgr); | |||
268 | auto buf = llvm::MemoryBuffer::getMemBuffer(body); | |||
269 | ||||
270 | FileID FID = SM.createFileID(buf->getMemBufferRef()); | |||
271 | ||||
272 | // Let's just enable the latest ObjC and C++ which should get most tokens | |||
273 | // right. | |||
274 | LangOptions Opts; | |||
275 | Opts.ObjC = true; | |||
276 | Opts.DollarIdents = true; | |||
277 | Opts.CPlusPlus20 = true; | |||
278 | Opts.LineComment = true; | |||
279 | ||||
280 | Lexer lex(FID, buf->getMemBufferRef(), SM, Opts); | |||
281 | ||||
282 | Token token; | |||
283 | bool exit = false; | |||
284 | while (!exit) { | |||
285 | // Returns true if this is the last token we get from the lexer. | |||
286 | exit = lex.LexFromRawLexer(token); | |||
287 | ||||
288 | // Extract the column number which we need to extract the token content. | |||
289 | // Our expression is just one line, so we don't need to handle any line | |||
290 | // numbers here. | |||
291 | bool invalid = false; | |||
292 | unsigned start = SM.getSpellingColumnNumber(token.getLocation(), &invalid); | |||
293 | if (invalid) | |||
294 | continue; | |||
295 | // Column numbers start at 1, but indexes in our string start at 0. | |||
296 | --start; | |||
297 | ||||
298 | // Annotations don't have a length, so let's skip them. | |||
299 | if (token.isAnnotation()) | |||
300 | continue; | |||
301 | ||||
302 | // Extract the token string from our source code and store it. | |||
303 | std::string token_str = body.substr(start, token.getLength()); | |||
304 | if (token_str.empty()) | |||
305 | continue; | |||
306 | m_tokens.insert(token_str); | |||
307 | } | |||
308 | } | |||
309 | ||||
310 | void ClangExpressionSourceCode::AddLocalVariableDecls(StreamString &stream, | |||
311 | const std::string &expr, | |||
312 | StackFrame *frame) const { | |||
313 | assert(frame)(static_cast <bool> (frame) ? void (0) : __assert_fail ( "frame", "lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp" , 313, __extension__ __PRETTY_FUNCTION__)); | |||
314 | TokenVerifier tokens(expr); | |||
315 | ||||
316 | lldb::VariableListSP var_list_sp = frame->GetInScopeVariableList(false, true); | |||
317 | ||||
318 | for (size_t i = 0; i < var_list_sp->GetSize(); i++) { | |||
319 | lldb::VariableSP var_sp = var_list_sp->GetVariableAtIndex(i); | |||
320 | ||||
321 | ConstString var_name = var_sp->GetName(); | |||
322 | ||||
323 | if (var_name == "this" && m_wrap_kind == WrapKind::CppMemberFunction) { | |||
324 | AddLambdaCaptureDecls(stream, frame, tokens); | |||
325 | ||||
326 | continue; | |||
327 | } | |||
328 | ||||
329 | // We can check for .block_descriptor w/o checking for langauge since this | |||
330 | // is not a valid identifier in either C or C++. | |||
331 | if (!var_name || var_name == ".block_descriptor") | |||
332 | continue; | |||
333 | ||||
334 | if (!expr.empty() && !tokens.hasToken(var_name.GetStringRef())) | |||
335 | continue; | |||
336 | ||||
337 | const bool is_objc = m_wrap_kind == WrapKind::ObjCInstanceMethod || | |||
338 | m_wrap_kind == WrapKind::ObjCStaticMethod; | |||
339 | if ((var_name == "self" || var_name == "_cmd") && is_objc) | |||
340 | continue; | |||
341 | ||||
342 | stream.Printf("using $__lldb_local_vars::%s;\n", var_name.AsCString()); | |||
343 | } | |||
344 | } | |||
345 | ||||
346 | bool ClangExpressionSourceCode::GetText( | |||
347 | std::string &text, ExecutionContext &exe_ctx, bool add_locals, | |||
348 | bool force_add_all_locals, llvm::ArrayRef<std::string> modules) const { | |||
349 | const char *target_specific_defines = "typedef signed char BOOL;\n"; | |||
350 | std::string module_macros; | |||
351 | llvm::raw_string_ostream module_macros_stream(module_macros); | |||
352 | ||||
353 | Target *target = exe_ctx.GetTargetPtr(); | |||
| ||||
354 | if (target) { | |||
355 | if (target->GetArchitecture().GetMachine() == llvm::Triple::aarch64 || | |||
356 | target->GetArchitecture().GetMachine() == llvm::Triple::aarch64_32) { | |||
357 | target_specific_defines = "typedef bool BOOL;\n"; | |||
358 | } | |||
359 | if (target->GetArchitecture().GetMachine() == llvm::Triple::x86_64) { | |||
360 | if (lldb::PlatformSP platform_sp = target->GetPlatform()) { | |||
361 | if (platform_sp->GetPluginName() == "ios-simulator") { | |||
362 | target_specific_defines = "typedef bool BOOL;\n"; | |||
363 | } | |||
364 | } | |||
365 | } | |||
366 | ||||
367 | auto *persistent_vars = llvm::cast<ClangPersistentVariables>( | |||
368 | target->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC)); | |||
369 | std::shared_ptr<ClangModulesDeclVendor> decl_vendor = | |||
370 | persistent_vars->GetClangModulesDeclVendor(); | |||
371 | if (decl_vendor) { | |||
372 | const ClangModulesDeclVendor::ModuleVector &hand_imported_modules = | |||
373 | persistent_vars->GetHandLoadedClangModules(); | |||
374 | ClangModulesDeclVendor::ModuleVector modules_for_macros; | |||
375 | ||||
376 | for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) { | |||
377 | modules_for_macros.push_back(module); | |||
378 | } | |||
379 | ||||
380 | if (target->GetEnableAutoImportClangModules()) { | |||
381 | if (StackFrame *frame = exe_ctx.GetFramePtr()) { | |||
382 | if (Block *block = frame->GetFrameBlock()) { | |||
383 | SymbolContext sc; | |||
384 | ||||
385 | block->CalculateSymbolContext(&sc); | |||
386 | ||||
387 | if (sc.comp_unit) { | |||
388 | StreamString error_stream; | |||
389 | ||||
390 | decl_vendor->AddModulesForCompileUnit( | |||
391 | *sc.comp_unit, modules_for_macros, error_stream); | |||
392 | } | |||
393 | } | |||
394 | } | |||
395 | } | |||
396 | ||||
397 | decl_vendor->ForEachMacro( | |||
398 | modules_for_macros, | |||
399 | [&module_macros_stream](llvm::StringRef token, | |||
400 | llvm::StringRef expansion) -> bool { | |||
401 | // Check if the macro hasn't already been defined in the | |||
402 | // g_expression_prefix (which defines a few builtin macros). | |||
403 | module_macros_stream << "#ifndef " << token << "\n"; | |||
404 | module_macros_stream << expansion << "\n"; | |||
405 | module_macros_stream << "#endif\n"; | |||
406 | return false; | |||
407 | }); | |||
408 | } | |||
409 | } | |||
410 | ||||
411 | StreamString debug_macros_stream; | |||
412 | StreamString lldb_local_var_decls; | |||
413 | if (StackFrame *frame = exe_ctx.GetFramePtr()) { | |||
414 | const SymbolContext &sc = frame->GetSymbolContext( | |||
415 | lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry); | |||
416 | ||||
417 | if (sc.comp_unit && sc.line_entry.IsValid()) { | |||
418 | DebugMacros *dm = sc.comp_unit->GetDebugMacros(); | |||
419 | if (dm) { | |||
420 | AddMacroState state(sc.line_entry.file, sc.line_entry.line); | |||
421 | AddMacros(dm, sc.comp_unit, state, debug_macros_stream); | |||
422 | } | |||
423 | } | |||
424 | ||||
425 | if (add_locals) | |||
426 | if (target->GetInjectLocalVariables(&exe_ctx)) { | |||
| ||||
427 | AddLocalVariableDecls(lldb_local_var_decls, | |||
428 | force_add_all_locals ? "" : m_body, frame); | |||
429 | } | |||
430 | } | |||
431 | ||||
432 | if (m_wrap) { | |||
433 | // Generate a list of @import statements that will import the specified | |||
434 | // module into our expression. | |||
435 | std::string module_imports; | |||
436 | for (const std::string &module : modules) { | |||
437 | module_imports.append("@import "); | |||
438 | module_imports.append(module); | |||
439 | module_imports.append(";\n"); | |||
440 | } | |||
441 | ||||
442 | StreamString wrap_stream; | |||
443 | ||||
444 | wrap_stream.Printf("%s\n%s\n%s\n%s\n%s\n", g_expression_prefix, | |||
445 | module_macros.c_str(), debug_macros_stream.GetData(), | |||
446 | target_specific_defines, m_prefix.c_str()); | |||
447 | ||||
448 | // First construct a tagged form of the user expression so we can find it | |||
449 | // later: | |||
450 | std::string tagged_body; | |||
451 | tagged_body.append(m_start_marker); | |||
452 | tagged_body.append(m_body); | |||
453 | tagged_body.append(m_end_marker); | |||
454 | ||||
455 | switch (m_wrap_kind) { | |||
456 | case WrapKind::Function: | |||
457 | wrap_stream.Printf("%s" | |||
458 | "void \n" | |||
459 | "%s(void *$__lldb_arg) \n" | |||
460 | "{ \n" | |||
461 | " %s; \n" | |||
462 | "%s" | |||
463 | "} \n", | |||
464 | module_imports.c_str(), m_name.c_str(), | |||
465 | lldb_local_var_decls.GetData(), tagged_body.c_str()); | |||
466 | break; | |||
467 | case WrapKind::CppMemberFunction: | |||
468 | wrap_stream.Printf("%s" | |||
469 | "void \n" | |||
470 | "$__lldb_class::%s(void *$__lldb_arg) \n" | |||
471 | "{ \n" | |||
472 | " %s; \n" | |||
473 | "%s" | |||
474 | "} \n", | |||
475 | module_imports.c_str(), m_name.c_str(), | |||
476 | lldb_local_var_decls.GetData(), tagged_body.c_str()); | |||
477 | break; | |||
478 | case WrapKind::ObjCInstanceMethod: | |||
479 | wrap_stream.Printf( | |||
480 | "%s" | |||
481 | "@interface $__lldb_objc_class ($__lldb_category) \n" | |||
482 | "-(void)%s:(void *)$__lldb_arg; \n" | |||
483 | "@end \n" | |||
484 | "@implementation $__lldb_objc_class ($__lldb_category) \n" | |||
485 | "-(void)%s:(void *)$__lldb_arg \n" | |||
486 | "{ \n" | |||
487 | " %s; \n" | |||
488 | "%s" | |||
489 | "} \n" | |||
490 | "@end \n", | |||
491 | module_imports.c_str(), m_name.c_str(), m_name.c_str(), | |||
492 | lldb_local_var_decls.GetData(), tagged_body.c_str()); | |||
493 | break; | |||
494 | ||||
495 | case WrapKind::ObjCStaticMethod: | |||
496 | wrap_stream.Printf( | |||
497 | "%s" | |||
498 | "@interface $__lldb_objc_class ($__lldb_category) \n" | |||
499 | "+(void)%s:(void *)$__lldb_arg; \n" | |||
500 | "@end \n" | |||
501 | "@implementation $__lldb_objc_class ($__lldb_category) \n" | |||
502 | "+(void)%s:(void *)$__lldb_arg \n" | |||
503 | "{ \n" | |||
504 | " %s; \n" | |||
505 | "%s" | |||
506 | "} \n" | |||
507 | "@end \n", | |||
508 | module_imports.c_str(), m_name.c_str(), m_name.c_str(), | |||
509 | lldb_local_var_decls.GetData(), tagged_body.c_str()); | |||
510 | break; | |||
511 | } | |||
512 | ||||
513 | text = std::string(wrap_stream.GetString()); | |||
514 | } else { | |||
515 | text.append(m_body); | |||
516 | } | |||
517 | ||||
518 | return true; | |||
519 | } | |||
520 | ||||
521 | bool ClangExpressionSourceCode::GetOriginalBodyBounds( | |||
522 | std::string transformed_text, size_t &start_loc, size_t &end_loc) { | |||
523 | start_loc = transformed_text.find(m_start_marker); | |||
524 | if (start_loc == std::string::npos) | |||
525 | return false; | |||
526 | start_loc += m_start_marker.size(); | |||
527 | end_loc = transformed_text.find(m_end_marker); | |||
528 | return end_loc != std::string::npos; | |||
529 | } |