LLDB  mainline
ASTResultSynthesizer.cpp
Go to the documentation of this file.
1 //===-- ASTResultSynthesizer.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 "ASTResultSynthesizer.h"
10 
11 #include "ClangASTImporter.h"
13 
15 #include "lldb/Target/Target.h"
17 #include "lldb/Utility/Log.h"
18 #include "stdlib.h"
19 #include "clang/AST/ASTContext.h"
20 #include "clang/AST/Decl.h"
21 #include "clang/AST/DeclCXX.h"
22 #include "clang/AST/DeclGroup.h"
23 #include "clang/AST/DeclObjC.h"
24 #include "clang/AST/Expr.h"
25 #include "clang/AST/Stmt.h"
26 #include "clang/Parse/Parser.h"
27 #include "clang/Sema/SemaDiagnostic.h"
28 #include "llvm/Support/Casting.h"
29 #include "llvm/Support/raw_ostream.h"
30 
31 using namespace llvm;
32 using namespace clang;
33 using namespace lldb_private;
34 
35 ASTResultSynthesizer::ASTResultSynthesizer(ASTConsumer *passthrough,
36  bool top_level, Target &target)
37  : m_ast_context(nullptr), m_passthrough(passthrough),
38  m_passthrough_sema(nullptr), m_target(target), m_sema(nullptr),
39  m_top_level(top_level) {
40  if (!m_passthrough)
41  return;
42 
43  m_passthrough_sema = dyn_cast<SemaConsumer>(passthrough);
44 }
45 
47 
48 void ASTResultSynthesizer::Initialize(ASTContext &Context) {
49  m_ast_context = &Context;
50 
51  if (m_passthrough)
52  m_passthrough->Initialize(Context);
53 }
54 
57 
58  if (NamedDecl *named_decl = dyn_cast<NamedDecl>(D)) {
59  if (log && log->GetVerbose()) {
60  if (named_decl->getIdentifier())
61  LLDB_LOGF(log, "TransformTopLevelDecl(%s)",
62  named_decl->getIdentifier()->getNameStart());
63  else if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D))
64  LLDB_LOGF(log, "TransformTopLevelDecl(%s)",
65  method_decl->getSelector().getAsString().c_str());
66  else
67  LLDB_LOGF(log, "TransformTopLevelDecl(<complex>)");
68  }
69 
70  if (m_top_level) {
71  RecordPersistentDecl(named_decl);
72  }
73  }
74 
75  if (LinkageSpecDecl *linkage_spec_decl = dyn_cast<LinkageSpecDecl>(D)) {
76  RecordDecl::decl_iterator decl_iterator;
77 
78  for (decl_iterator = linkage_spec_decl->decls_begin();
79  decl_iterator != linkage_spec_decl->decls_end(); ++decl_iterator) {
80  TransformTopLevelDecl(*decl_iterator);
81  }
82  } else if (!m_top_level) {
83  if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D)) {
84  if (m_ast_context &&
85  !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) {
86  RecordPersistentTypes(method_decl);
87  SynthesizeObjCMethodResult(method_decl);
88  }
89  } else if (FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D)) {
90  // When completing user input the body of the function may be a nullptr.
91  if (m_ast_context && function_decl->hasBody() &&
92  !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) {
93  RecordPersistentTypes(function_decl);
94  SynthesizeFunctionResult(function_decl);
95  }
96  }
97  }
98 }
99 
101  DeclGroupRef::iterator decl_iterator;
102 
103  for (decl_iterator = D.begin(); decl_iterator != D.end(); ++decl_iterator) {
104  Decl *decl = *decl_iterator;
105 
106  TransformTopLevelDecl(decl);
107  }
108 
109  if (m_passthrough)
110  return m_passthrough->HandleTopLevelDecl(D);
111  return true;
112 }
113 
116 
117  if (!m_sema)
118  return false;
119 
120  FunctionDecl *function_decl = FunDecl;
121 
122  if (!function_decl)
123  return false;
124 
125  if (log && log->GetVerbose()) {
126  std::string s;
127  raw_string_ostream os(s);
128 
129  function_decl->print(os);
130 
131  os.flush();
132 
133  LLDB_LOGF(log, "Untransformed function AST:\n%s", s.c_str());
134  }
135 
136  Stmt *function_body = function_decl->getBody();
137  CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(function_body);
138 
139  bool ret = SynthesizeBodyResult(compound_stmt, function_decl);
140 
141  if (log && log->GetVerbose()) {
142  std::string s;
143  raw_string_ostream os(s);
144 
145  function_decl->print(os);
146 
147  os.flush();
148 
149  LLDB_LOGF(log, "Transformed function AST:\n%s", s.c_str());
150  }
151 
152  return ret;
153 }
154 
156  ObjCMethodDecl *MethodDecl) {
158 
159  if (!m_sema)
160  return false;
161 
162  if (!MethodDecl)
163  return false;
164 
165  if (log && log->GetVerbose()) {
166  std::string s;
167  raw_string_ostream os(s);
168 
169  MethodDecl->print(os);
170 
171  os.flush();
172 
173  LLDB_LOGF(log, "Untransformed method AST:\n%s", s.c_str());
174  }
175 
176  Stmt *method_body = MethodDecl->getBody();
177 
178  if (!method_body)
179  return false;
180 
181  CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(method_body);
182 
183  bool ret = SynthesizeBodyResult(compound_stmt, MethodDecl);
184 
185  if (log && log->GetVerbose()) {
186  std::string s;
187  raw_string_ostream os(s);
188 
189  MethodDecl->print(os);
190 
191  os.flush();
192 
193  LLDB_LOGF(log, "Transformed method AST:\n%s", s.c_str());
194  }
195 
196  return ret;
197 }
198 
200  DeclContext *DC) {
202 
203  ASTContext &Ctx(*m_ast_context);
204 
205  if (!Body)
206  return false;
207 
208  if (Body->body_empty())
209  return false;
210 
211  Stmt **last_stmt_ptr = Body->body_end() - 1;
212  Stmt *last_stmt = *last_stmt_ptr;
213 
214  while (dyn_cast<NullStmt>(last_stmt)) {
215  if (last_stmt_ptr != Body->body_begin()) {
216  last_stmt_ptr--;
217  last_stmt = *last_stmt_ptr;
218  } else {
219  return false;
220  }
221  }
222 
223  Expr *last_expr = dyn_cast<Expr>(last_stmt);
224 
225  if (!last_expr)
226  // No auxiliary variable necessary; expression returns void
227  return true;
228 
229  // In C++11, last_expr can be a LValueToRvalue implicit cast. Strip that off
230  // if that's the case.
231 
232  do {
233  ImplicitCastExpr *implicit_cast = dyn_cast<ImplicitCastExpr>(last_expr);
234 
235  if (!implicit_cast)
236  break;
237 
238  if (implicit_cast->getCastKind() != CK_LValueToRValue)
239  break;
240 
241  last_expr = implicit_cast->getSubExpr();
242  } while (false);
243 
244  // is_lvalue is used to record whether the expression returns an assignable
245  // Lvalue or an Rvalue. This is relevant because they are handled
246  // differently.
247  //
248  // For Lvalues
249  //
250  // - In AST result synthesis (here!) the expression E is transformed into an
251  // initialization T *$__lldb_expr_result_ptr = &E.
252  //
253  // - In structure allocation, a pointer-sized slot is allocated in the
254  // struct that is to be passed into the expression.
255  //
256  // - In IR transformations, reads and writes to $__lldb_expr_result_ptr are
257  // redirected at an entry in the struct ($__lldb_arg) passed into the
258  // expression. (Other persistent variables are treated similarly, having
259  // been materialized as references, but in those cases the value of the
260  // reference itself is never modified.)
261  //
262  // - During materialization, $0 (the result persistent variable) is ignored.
263  //
264  // - During dematerialization, $0 is marked up as a load address with value
265  // equal to the contents of the structure entry.
266  //
267  // For Rvalues
268  //
269  // - In AST result synthesis the expression E is transformed into an
270  // initialization static T $__lldb_expr_result = E.
271  //
272  // - In structure allocation, a pointer-sized slot is allocated in the
273  // struct that is to be passed into the expression.
274  //
275  // - In IR transformations, an instruction is inserted at the beginning of
276  // the function to dereference the pointer resident in the slot. Reads and
277  // writes to $__lldb_expr_result are redirected at that dereferenced
278  // version. Guard variables for the static variable are excised.
279  //
280  // - During materialization, $0 (the result persistent variable) is
281  // populated with the location of a newly-allocated area of memory.
282  //
283  // - During dematerialization, $0 is ignored.
284 
285  bool is_lvalue = last_expr->getValueKind() == VK_LValue &&
286  last_expr->getObjectKind() == OK_Ordinary;
287 
288  QualType expr_qual_type = last_expr->getType();
289  const clang::Type *expr_type = expr_qual_type.getTypePtr();
290 
291  if (!expr_type)
292  return false;
293 
294  if (expr_type->isVoidType())
295  return true;
296 
297  if (log) {
298  std::string s = expr_qual_type.getAsString();
299 
300  LLDB_LOGF(log, "Last statement is an %s with type: %s",
301  (is_lvalue ? "lvalue" : "rvalue"), s.c_str());
302  }
303 
304  clang::VarDecl *result_decl = nullptr;
305 
306  if (is_lvalue) {
307  IdentifierInfo *result_ptr_id;
308 
309  if (expr_type->isFunctionType())
310  result_ptr_id =
311  &Ctx.Idents.get("$__lldb_expr_result"); // functions actually should
312  // be treated like function
313  // pointers
314  else
315  result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result_ptr");
316 
317  m_sema->RequireCompleteType(last_expr->getSourceRange().getBegin(),
318  expr_qual_type,
319  clang::diag::err_incomplete_type);
320 
321  QualType ptr_qual_type;
322 
323  if (expr_qual_type->getAs<ObjCObjectType>() != nullptr)
324  ptr_qual_type = Ctx.getObjCObjectPointerType(expr_qual_type);
325  else
326  ptr_qual_type = Ctx.getPointerType(expr_qual_type);
327 
328  result_decl =
329  VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(),
330  result_ptr_id, ptr_qual_type, nullptr, SC_Static);
331 
332  if (!result_decl)
333  return false;
334 
335  ExprResult address_of_expr =
336  m_sema->CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, last_expr);
337  if (address_of_expr.get())
338  m_sema->AddInitializerToDecl(result_decl, address_of_expr.get(), true);
339  else
340  return false;
341  } else {
342  IdentifierInfo &result_id = Ctx.Idents.get("$__lldb_expr_result");
343 
344  result_decl =
345  VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(), &result_id,
346  expr_qual_type, nullptr, SC_Static);
347 
348  if (!result_decl)
349  return false;
350 
351  m_sema->AddInitializerToDecl(result_decl, last_expr, true);
352  }
353 
354  DC->addDecl(result_decl);
355 
356  ///////////////////////////////
357  // call AddInitializerToDecl
358  //
359 
360  // m_sema->AddInitializerToDecl(result_decl, last_expr);
361 
362  /////////////////////////////////
363  // call ConvertDeclToDeclGroup
364  //
365 
366  Sema::DeclGroupPtrTy result_decl_group_ptr;
367 
368  result_decl_group_ptr = m_sema->ConvertDeclToDeclGroup(result_decl);
369 
370  ////////////////////////
371  // call ActOnDeclStmt
372  //
373 
374  StmtResult result_initialization_stmt_result(m_sema->ActOnDeclStmt(
375  result_decl_group_ptr, SourceLocation(), SourceLocation()));
376 
377  ////////////////////////////////////////////////
378  // replace the old statement with the new one
379  //
380 
381  *last_stmt_ptr = static_cast<Stmt *>(result_initialization_stmt_result.get());
382 
383  return true;
384 }
385 
387  if (m_passthrough)
388  m_passthrough->HandleTranslationUnit(Ctx);
389 }
390 
391 void ASTResultSynthesizer::RecordPersistentTypes(DeclContext *FunDeclCtx) {
392  typedef DeclContext::specific_decl_iterator<TypeDecl> TypeDeclIterator;
393 
394  for (TypeDeclIterator i = TypeDeclIterator(FunDeclCtx->decls_begin()),
395  e = TypeDeclIterator(FunDeclCtx->decls_end());
396  i != e; ++i) {
398  }
399 }
400 
402  if (!D->getIdentifier())
403  return;
404 
405  StringRef name = D->getName();
406 
407  if (name.size() == 0 || name[0] != '$')
408  return;
409 
411 
412  ConstString name_cs(name.str().c_str());
413 
414  LLDB_LOGF(log, "Recording persistent type %s\n", name_cs.GetCString());
415 
416  m_decls.push_back(D);
417 }
418 
421 
422  if (!D->getIdentifier())
423  return;
424 
425  StringRef name = D->getName();
426 
427  if (name.size() == 0)
428  return;
429 
431 
432  ConstString name_cs(name.str().c_str());
433 
434  LLDB_LOGF(log, "Recording persistent decl %s\n", name_cs.GetCString());
435 
436  m_decls.push_back(D);
437 }
438 
440  auto *state =
442  if (!state)
443  return;
444 
445  auto *persistent_vars = llvm::cast<ClangPersistentVariables>(state);
447 
448  for (clang::NamedDecl *decl : m_decls) {
449  StringRef name = decl->getName();
450  ConstString name_cs(name.str().c_str());
451 
452  Decl *D_scratch = persistent_vars->GetClangASTImporter()->DeportDecl(
453  &scratch_ctx->getASTContext(), decl);
454 
455  if (!D_scratch) {
457 
458  if (log) {
459  std::string s;
460  llvm::raw_string_ostream ss(s);
461  decl->dump(ss);
462  ss.flush();
463 
464  LLDB_LOGF(log, "Couldn't commit persistent decl: %s\n", s.c_str());
465  }
466 
467  continue;
468  }
469 
470  if (NamedDecl *NamedDecl_scratch = dyn_cast<NamedDecl>(D_scratch))
471  persistent_vars->RegisterPersistentDecl(name_cs, NamedDecl_scratch,
472  scratch_ctx);
473  }
474 }
475 
477  if (m_passthrough)
478  m_passthrough->HandleTagDeclDefinition(D);
479 }
480 
482  if (m_passthrough)
483  m_passthrough->CompleteTentativeDefinition(D);
484 }
485 
486 void ASTResultSynthesizer::HandleVTable(CXXRecordDecl *RD) {
487  if (m_passthrough)
488  m_passthrough->HandleVTable(RD);
489 }
490 
492  if (m_passthrough)
493  m_passthrough->PrintStats();
494 }
495 
497  m_sema = &S;
498 
499  if (m_passthrough_sema)
500  m_passthrough_sema->InitializeSema(S);
501 }
502 
504  m_sema = nullptr;
505 
506  if (m_passthrough_sema)
507  m_passthrough_sema->ForgetSema();
508 }
bool SynthesizeObjCMethodResult(clang::ObjCMethodDecl *MethodDecl)
Process an Objective-C method and produce the result variable and initialization. ...
~ASTResultSynthesizer() override
Destructor.
clang::ASTContext * m_ast_context
The AST context to use for identifiers and types.
A class that represents a running process on the host machine.
Definition: Debugger.h:49
#define lldbassert(x)
Definition: LLDBAssert.h:15
A TypeSystem implementation based on Clang.
void MaybeRecordPersistentType(clang::TypeDecl *D)
Given a TypeDecl, if it declares a type whose name starts with a dollar sign, register it as a pointe...
std::vector< clang::NamedDecl * > m_decls
Persistent declarations to register assuming the expression succeeds.
void ForgetSema() override
Reset the Sema to NULL now that transformations are done.
bool SynthesizeBodyResult(clang::CompoundStmt *Body, clang::DeclContext *DC)
Process a function body and produce the result variable and initialization.
clang::SemaConsumer * m_passthrough_sema
The SemaConsumer down the chain, for passthrough.
void RecordPersistentDecl(clang::NamedDecl *D)
Given a NamedDecl, register it as a pointer type in the target&#39;s scratch AST context.
Target & m_target
The target, which contains the persistent variable store and the.
void CommitPersistentDecls()
The parse has succeeded, so record its persistent decls.
void HandleVTable(clang::CXXRecordDecl *RD) override
Passthrough stub.
Log * GetLogIfAllCategoriesSet(uint32_t mask)
Definition: Logging.cpp:58
void InitializeSema(clang::Sema &S) override
Set the Sema object to use when performing transforms, and pass it on.
void HandleTranslationUnit(clang::ASTContext &Ctx) override
Passthrough stub.
bool GetVerbose() const
Definition: Log.cpp:275
void TransformTopLevelDecl(clang::Decl *D)
Hunt the given Decl for FunctionDecls named $__lldb_expr, recursing as necessary through LinkageSpecD...
void CompleteTentativeDefinition(clang::VarDecl *D) override
Passthrough stub.
void Initialize(clang::ASTContext &Context) override
Link this consumer with a particular AST context.
void PrintStats() override
Passthrough stub.
#define LLDB_LOGF(log,...)
Definition: Log.h:249
bool HandleTopLevelDecl(clang::DeclGroupRef D) override
Examine a list of Decls to find the function $__lldb_expr and transform its code. ...
A uniqued constant string class.
Definition: ConstString.h:40
Non-standardized C, such as K&R.
clang::Sema * m_sema
The Sema to use.
clang::ASTConsumer * m_passthrough
The ASTConsumer down the chain, for passthrough.
#define LIBLLDB_LOG_EXPRESSIONS
Definition: Logging.h:22
void HandleTagDeclDefinition(clang::TagDecl *D) override
Passthrough stub.
PersistentExpressionState * GetPersistentExpressionStateForLanguage(lldb::LanguageType language)
Definition: Target.cpp:2178
static TypeSystemClang * GetScratch(Target &target, bool create_on_demand=true)
bool SynthesizeFunctionResult(clang::FunctionDecl *FunDecl)
Process a function and produce the result variable and initialization.
void RecordPersistentTypes(clang::DeclContext *FunDeclCtx)
Given a DeclContext for a function or method, find all types declared in the context and record any p...