1292932Sdim//===-- ASTResultSynthesizer.cpp --------------------------------*- C++ -*-===// 2292932Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6292932Sdim// 7292932Sdim//===----------------------------------------------------------------------===// 8292932Sdim 9292932Sdim#include "ASTResultSynthesizer.h" 10292932Sdim 11292932Sdim#include "ClangPersistentVariables.h" 12292932Sdim 13309124Sdim#include "lldb/Symbol/ClangASTContext.h" 14309124Sdim#include "lldb/Symbol/ClangASTImporter.h" 15309124Sdim#include "lldb/Target/Target.h" 16309124Sdim#include "lldb/Utility/LLDBAssert.h" 17321369Sdim#include "lldb/Utility/Log.h" 18292932Sdim#include "stdlib.h" 19292932Sdim#include "clang/AST/ASTContext.h" 20292932Sdim#include "clang/AST/Decl.h" 21292932Sdim#include "clang/AST/DeclCXX.h" 22292932Sdim#include "clang/AST/DeclGroup.h" 23292932Sdim#include "clang/AST/DeclObjC.h" 24292932Sdim#include "clang/AST/Expr.h" 25292932Sdim#include "clang/AST/Stmt.h" 26292932Sdim#include "clang/Parse/Parser.h" 27292932Sdim#include "clang/Sema/SemaDiagnostic.h" 28292932Sdim#include "llvm/Support/Casting.h" 29292932Sdim#include "llvm/Support/raw_ostream.h" 30292932Sdim 31292932Sdimusing namespace llvm; 32292932Sdimusing namespace clang; 33292932Sdimusing namespace lldb_private; 34292932Sdim 35314564SdimASTResultSynthesizer::ASTResultSynthesizer(ASTConsumer *passthrough, 36314564Sdim bool top_level, Target &target) 37353358Sdim : m_ast_context(nullptr), m_passthrough(passthrough), 38353358Sdim m_passthrough_sema(nullptr), m_target(target), m_sema(nullptr), 39353358Sdim m_top_level(top_level) { 40314564Sdim if (!m_passthrough) 41314564Sdim return; 42292932Sdim 43314564Sdim m_passthrough_sema = dyn_cast<SemaConsumer>(passthrough); 44292932Sdim} 45292932Sdim 46314564SdimASTResultSynthesizer::~ASTResultSynthesizer() {} 47292932Sdim 48314564Sdimvoid ASTResultSynthesizer::Initialize(ASTContext &Context) { 49314564Sdim m_ast_context = &Context; 50292932Sdim 51314564Sdim if (m_passthrough) 52314564Sdim m_passthrough->Initialize(Context); 53292932Sdim} 54292932Sdim 55314564Sdimvoid ASTResultSynthesizer::TransformTopLevelDecl(Decl *D) { 56314564Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); 57292932Sdim 58314564Sdim if (NamedDecl *named_decl = dyn_cast<NamedDecl>(D)) { 59314564Sdim if (log && log->GetVerbose()) { 60314564Sdim if (named_decl->getIdentifier()) 61360784Sdim LLDB_LOGF(log, "TransformTopLevelDecl(%s)", 62360784Sdim named_decl->getIdentifier()->getNameStart()); 63314564Sdim else if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D)) 64360784Sdim LLDB_LOGF(log, "TransformTopLevelDecl(%s)", 65360784Sdim method_decl->getSelector().getAsString().c_str()); 66314564Sdim else 67360784Sdim LLDB_LOGF(log, "TransformTopLevelDecl(<complex>)"); 68314564Sdim } 69292932Sdim 70314564Sdim if (m_top_level) { 71314564Sdim RecordPersistentDecl(named_decl); 72292932Sdim } 73314564Sdim } 74292932Sdim 75314564Sdim if (LinkageSpecDecl *linkage_spec_decl = dyn_cast<LinkageSpecDecl>(D)) { 76314564Sdim RecordDecl::decl_iterator decl_iterator; 77292932Sdim 78314564Sdim for (decl_iterator = linkage_spec_decl->decls_begin(); 79314564Sdim decl_iterator != linkage_spec_decl->decls_end(); ++decl_iterator) { 80314564Sdim TransformTopLevelDecl(*decl_iterator); 81292932Sdim } 82314564Sdim } else if (!m_top_level) { 83314564Sdim if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D)) { 84314564Sdim if (m_ast_context && 85314564Sdim !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) { 86314564Sdim RecordPersistentTypes(method_decl); 87314564Sdim SynthesizeObjCMethodResult(method_decl); 88314564Sdim } 89314564Sdim } else if (FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D)) { 90344779Sdim // When completing user input the body of the function may be a nullptr. 91344779Sdim if (m_ast_context && function_decl->hasBody() && 92314564Sdim !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) { 93314564Sdim RecordPersistentTypes(function_decl); 94314564Sdim SynthesizeFunctionResult(function_decl); 95314564Sdim } 96292932Sdim } 97314564Sdim } 98292932Sdim} 99292932Sdim 100314564Sdimbool ASTResultSynthesizer::HandleTopLevelDecl(DeclGroupRef D) { 101314564Sdim DeclGroupRef::iterator decl_iterator; 102292932Sdim 103314564Sdim for (decl_iterator = D.begin(); decl_iterator != D.end(); ++decl_iterator) { 104314564Sdim Decl *decl = *decl_iterator; 105292932Sdim 106314564Sdim TransformTopLevelDecl(decl); 107314564Sdim } 108292932Sdim 109314564Sdim if (m_passthrough) 110314564Sdim return m_passthrough->HandleTopLevelDecl(D); 111314564Sdim return true; 112292932Sdim} 113292932Sdim 114314564Sdimbool ASTResultSynthesizer::SynthesizeFunctionResult(FunctionDecl *FunDecl) { 115314564Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); 116292932Sdim 117314564Sdim if (!m_sema) 118314564Sdim return false; 119292932Sdim 120314564Sdim FunctionDecl *function_decl = FunDecl; 121292932Sdim 122314564Sdim if (!function_decl) 123314564Sdim return false; 124292932Sdim 125314564Sdim if (log && log->GetVerbose()) { 126314564Sdim std::string s; 127314564Sdim raw_string_ostream os(s); 128292932Sdim 129314564Sdim function_decl->print(os); 130292932Sdim 131314564Sdim os.flush(); 132292932Sdim 133360784Sdim LLDB_LOGF(log, "Untransformed function AST:\n%s", s.c_str()); 134314564Sdim } 135292932Sdim 136314564Sdim Stmt *function_body = function_decl->getBody(); 137314564Sdim CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(function_body); 138292932Sdim 139314564Sdim bool ret = SynthesizeBodyResult(compound_stmt, function_decl); 140292932Sdim 141314564Sdim if (log && log->GetVerbose()) { 142314564Sdim std::string s; 143314564Sdim raw_string_ostream os(s); 144292932Sdim 145314564Sdim function_decl->print(os); 146292932Sdim 147314564Sdim os.flush(); 148292932Sdim 149360784Sdim LLDB_LOGF(log, "Transformed function AST:\n%s", s.c_str()); 150314564Sdim } 151292932Sdim 152314564Sdim return ret; 153292932Sdim} 154292932Sdim 155314564Sdimbool ASTResultSynthesizer::SynthesizeObjCMethodResult( 156314564Sdim ObjCMethodDecl *MethodDecl) { 157314564Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); 158292932Sdim 159314564Sdim if (!m_sema) 160314564Sdim return false; 161292932Sdim 162314564Sdim if (!MethodDecl) 163314564Sdim return false; 164292932Sdim 165314564Sdim if (log && log->GetVerbose()) { 166314564Sdim std::string s; 167314564Sdim raw_string_ostream os(s); 168292932Sdim 169314564Sdim MethodDecl->print(os); 170292932Sdim 171314564Sdim os.flush(); 172292932Sdim 173360784Sdim LLDB_LOGF(log, "Untransformed method AST:\n%s", s.c_str()); 174314564Sdim } 175292932Sdim 176314564Sdim Stmt *method_body = MethodDecl->getBody(); 177292932Sdim 178314564Sdim if (!method_body) 179314564Sdim return false; 180292932Sdim 181314564Sdim CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(method_body); 182292932Sdim 183314564Sdim bool ret = SynthesizeBodyResult(compound_stmt, MethodDecl); 184292932Sdim 185314564Sdim if (log && log->GetVerbose()) { 186314564Sdim std::string s; 187314564Sdim raw_string_ostream os(s); 188292932Sdim 189314564Sdim MethodDecl->print(os); 190292932Sdim 191314564Sdim os.flush(); 192292932Sdim 193360784Sdim LLDB_LOGF(log, "Transformed method AST:\n%s", s.c_str()); 194314564Sdim } 195292932Sdim 196314564Sdim return ret; 197292932Sdim} 198292932Sdim 199314564Sdimbool ASTResultSynthesizer::SynthesizeBodyResult(CompoundStmt *Body, 200314564Sdim DeclContext *DC) { 201314564Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); 202292932Sdim 203314564Sdim ASTContext &Ctx(*m_ast_context); 204292932Sdim 205314564Sdim if (!Body) 206314564Sdim return false; 207292932Sdim 208314564Sdim if (Body->body_empty()) 209314564Sdim return false; 210292932Sdim 211314564Sdim Stmt **last_stmt_ptr = Body->body_end() - 1; 212314564Sdim Stmt *last_stmt = *last_stmt_ptr; 213292932Sdim 214314564Sdim while (dyn_cast<NullStmt>(last_stmt)) { 215314564Sdim if (last_stmt_ptr != Body->body_begin()) { 216314564Sdim last_stmt_ptr--; 217314564Sdim last_stmt = *last_stmt_ptr; 218314564Sdim } else { 219314564Sdim return false; 220292932Sdim } 221314564Sdim } 222292932Sdim 223314564Sdim Expr *last_expr = dyn_cast<Expr>(last_stmt); 224292932Sdim 225314564Sdim if (!last_expr) 226314564Sdim // No auxiliary variable necessary; expression returns void 227314564Sdim return true; 228292932Sdim 229314564Sdim // In C++11, last_expr can be a LValueToRvalue implicit cast. Strip that off 230341825Sdim // if that's the case. 231292932Sdim 232314564Sdim do { 233314564Sdim ImplicitCastExpr *implicit_cast = dyn_cast<ImplicitCastExpr>(last_expr); 234292932Sdim 235314564Sdim if (!implicit_cast) 236314564Sdim break; 237292932Sdim 238314564Sdim if (implicit_cast->getCastKind() != CK_LValueToRValue) 239314564Sdim break; 240292932Sdim 241314564Sdim last_expr = implicit_cast->getSubExpr(); 242353358Sdim } while (false); 243292932Sdim 244314564Sdim // is_lvalue is used to record whether the expression returns an assignable 245341825Sdim // Lvalue or an Rvalue. This is relevant because they are handled 246341825Sdim // differently. 247314564Sdim // 248314564Sdim // For Lvalues 249314564Sdim // 250314564Sdim // - In AST result synthesis (here!) the expression E is transformed into an 251314564Sdim // initialization 252314564Sdim // T *$__lldb_expr_result_ptr = &E. 253314564Sdim // 254314564Sdim // - In structure allocation, a pointer-sized slot is allocated in the 255314564Sdim // struct that is to be 256314564Sdim // passed into the expression. 257314564Sdim // 258314564Sdim // - In IR transformations, reads and writes to $__lldb_expr_result_ptr are 259314564Sdim // redirected at 260314564Sdim // an entry in the struct ($__lldb_arg) passed into the expression. 261314564Sdim // (Other persistent 262314564Sdim // variables are treated similarly, having been materialized as 263314564Sdim // references, but in those 264314564Sdim // cases the value of the reference itself is never modified.) 265314564Sdim // 266314564Sdim // - During materialization, $0 (the result persistent variable) is ignored. 267314564Sdim // 268314564Sdim // - During dematerialization, $0 is marked up as a load address with value 269314564Sdim // equal to the 270314564Sdim // contents of the structure entry. 271314564Sdim // 272314564Sdim // For Rvalues 273314564Sdim // 274314564Sdim // - In AST result synthesis the expression E is transformed into an 275314564Sdim // initialization 276314564Sdim // static T $__lldb_expr_result = E. 277314564Sdim // 278314564Sdim // - In structure allocation, a pointer-sized slot is allocated in the 279314564Sdim // struct that is to be 280314564Sdim // passed into the expression. 281314564Sdim // 282314564Sdim // - In IR transformations, an instruction is inserted at the beginning of 283314564Sdim // the function to 284314564Sdim // dereference the pointer resident in the slot. Reads and writes to 285314564Sdim // $__lldb_expr_result 286314564Sdim // are redirected at that dereferenced version. Guard variables for the 287314564Sdim // static variable 288314564Sdim // are excised. 289314564Sdim // 290314564Sdim // - During materialization, $0 (the result persistent variable) is 291314564Sdim // populated with the location 292314564Sdim // of a newly-allocated area of memory. 293314564Sdim // 294314564Sdim // - During dematerialization, $0 is ignored. 295292932Sdim 296341825Sdim bool is_lvalue = last_expr->getValueKind() == VK_LValue && 297341825Sdim last_expr->getObjectKind() == OK_Ordinary; 298292932Sdim 299314564Sdim QualType expr_qual_type = last_expr->getType(); 300314564Sdim const clang::Type *expr_type = expr_qual_type.getTypePtr(); 301292932Sdim 302314564Sdim if (!expr_type) 303314564Sdim return false; 304292932Sdim 305314564Sdim if (expr_type->isVoidType()) 306314564Sdim return true; 307292932Sdim 308314564Sdim if (log) { 309314564Sdim std::string s = expr_qual_type.getAsString(); 310292932Sdim 311360784Sdim LLDB_LOGF(log, "Last statement is an %s with type: %s", 312360784Sdim (is_lvalue ? "lvalue" : "rvalue"), s.c_str()); 313314564Sdim } 314292932Sdim 315353358Sdim clang::VarDecl *result_decl = nullptr; 316292932Sdim 317314564Sdim if (is_lvalue) { 318314564Sdim IdentifierInfo *result_ptr_id; 319292932Sdim 320314564Sdim if (expr_type->isFunctionType()) 321314564Sdim result_ptr_id = 322314564Sdim &Ctx.Idents.get("$__lldb_expr_result"); // functions actually should 323314564Sdim // be treated like function 324314564Sdim // pointers 325314564Sdim else 326314564Sdim result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result_ptr"); 327292932Sdim 328314564Sdim m_sema->RequireCompleteType(SourceLocation(), expr_qual_type, 329314564Sdim clang::diag::err_incomplete_type); 330292932Sdim 331314564Sdim QualType ptr_qual_type; 332292932Sdim 333353358Sdim if (expr_qual_type->getAs<ObjCObjectType>() != nullptr) 334314564Sdim ptr_qual_type = Ctx.getObjCObjectPointerType(expr_qual_type); 335314564Sdim else 336314564Sdim ptr_qual_type = Ctx.getPointerType(expr_qual_type); 337292932Sdim 338314564Sdim result_decl = 339314564Sdim VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(), 340353358Sdim result_ptr_id, ptr_qual_type, nullptr, SC_Static); 341292932Sdim 342314564Sdim if (!result_decl) 343314564Sdim return false; 344292932Sdim 345314564Sdim ExprResult address_of_expr = 346314564Sdim m_sema->CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, last_expr); 347314564Sdim if (address_of_expr.get()) 348314564Sdim m_sema->AddInitializerToDecl(result_decl, address_of_expr.get(), true); 349292932Sdim else 350314564Sdim return false; 351314564Sdim } else { 352314564Sdim IdentifierInfo &result_id = Ctx.Idents.get("$__lldb_expr_result"); 353292932Sdim 354353358Sdim result_decl = 355353358Sdim VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(), &result_id, 356353358Sdim expr_qual_type, nullptr, SC_Static); 357292932Sdim 358314564Sdim if (!result_decl) 359314564Sdim return false; 360292932Sdim 361314564Sdim m_sema->AddInitializerToDecl(result_decl, last_expr, true); 362314564Sdim } 363292932Sdim 364314564Sdim DC->addDecl(result_decl); 365292932Sdim 366314564Sdim /////////////////////////////// 367314564Sdim // call AddInitializerToDecl 368314564Sdim // 369292932Sdim 370314564Sdim // m_sema->AddInitializerToDecl(result_decl, last_expr); 371292932Sdim 372314564Sdim ///////////////////////////////// 373314564Sdim // call ConvertDeclToDeclGroup 374314564Sdim // 375292932Sdim 376314564Sdim Sema::DeclGroupPtrTy result_decl_group_ptr; 377292932Sdim 378314564Sdim result_decl_group_ptr = m_sema->ConvertDeclToDeclGroup(result_decl); 379292932Sdim 380314564Sdim //////////////////////// 381314564Sdim // call ActOnDeclStmt 382314564Sdim // 383292932Sdim 384314564Sdim StmtResult result_initialization_stmt_result(m_sema->ActOnDeclStmt( 385314564Sdim result_decl_group_ptr, SourceLocation(), SourceLocation())); 386292932Sdim 387314564Sdim //////////////////////////////////////////////// 388314564Sdim // replace the old statement with the new one 389314564Sdim // 390292932Sdim 391360784Sdim *last_stmt_ptr = static_cast<Stmt *>(result_initialization_stmt_result.get()); 392292932Sdim 393314564Sdim return true; 394292932Sdim} 395292932Sdim 396314564Sdimvoid ASTResultSynthesizer::HandleTranslationUnit(ASTContext &Ctx) { 397314564Sdim if (m_passthrough) 398314564Sdim m_passthrough->HandleTranslationUnit(Ctx); 399292932Sdim} 400292932Sdim 401314564Sdimvoid ASTResultSynthesizer::RecordPersistentTypes(DeclContext *FunDeclCtx) { 402314564Sdim typedef DeclContext::specific_decl_iterator<TypeDecl> TypeDeclIterator; 403292932Sdim 404314564Sdim for (TypeDeclIterator i = TypeDeclIterator(FunDeclCtx->decls_begin()), 405314564Sdim e = TypeDeclIterator(FunDeclCtx->decls_end()); 406314564Sdim i != e; ++i) { 407314564Sdim MaybeRecordPersistentType(*i); 408314564Sdim } 409292932Sdim} 410292932Sdim 411314564Sdimvoid ASTResultSynthesizer::MaybeRecordPersistentType(TypeDecl *D) { 412314564Sdim if (!D->getIdentifier()) 413314564Sdim return; 414292932Sdim 415314564Sdim StringRef name = D->getName(); 416292932Sdim 417314564Sdim if (name.size() == 0 || name[0] != '$') 418314564Sdim return; 419292932Sdim 420314564Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); 421292932Sdim 422314564Sdim ConstString name_cs(name.str().c_str()); 423292932Sdim 424360784Sdim LLDB_LOGF(log, "Recording persistent type %s\n", name_cs.GetCString()); 425292932Sdim 426314564Sdim m_decls.push_back(D); 427309124Sdim} 428292932Sdim 429314564Sdimvoid ASTResultSynthesizer::RecordPersistentDecl(NamedDecl *D) { 430314564Sdim lldbassert(m_top_level); 431309124Sdim 432314564Sdim if (!D->getIdentifier()) 433314564Sdim return; 434309124Sdim 435314564Sdim StringRef name = D->getName(); 436309124Sdim 437314564Sdim if (name.size() == 0) 438314564Sdim return; 439309124Sdim 440314564Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); 441309124Sdim 442314564Sdim ConstString name_cs(name.str().c_str()); 443309124Sdim 444360784Sdim LLDB_LOGF(log, "Recording persistent decl %s\n", name_cs.GetCString()); 445309124Sdim 446314564Sdim m_decls.push_back(D); 447292932Sdim} 448292932Sdim 449314564Sdimvoid ASTResultSynthesizer::CommitPersistentDecls() { 450360784Sdim auto *state = 451360784Sdim m_target.GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC); 452360784Sdim if (!state) 453360784Sdim return; 454360784Sdim 455360784Sdim auto *persistent_vars = llvm::cast<ClangPersistentVariables>(state); 456360784Sdim ClangASTContext *scratch_ctx = ClangASTContext::GetScratch(m_target); 457360784Sdim 458314564Sdim for (clang::NamedDecl *decl : m_decls) { 459314564Sdim StringRef name = decl->getName(); 460314564Sdim ConstString name_cs(name.str().c_str()); 461309124Sdim 462314564Sdim Decl *D_scratch = m_target.GetClangASTImporter()->DeportDecl( 463360784Sdim &scratch_ctx->getASTContext(), decl); 464309124Sdim 465314564Sdim if (!D_scratch) { 466314564Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); 467309124Sdim 468314564Sdim if (log) { 469314564Sdim std::string s; 470314564Sdim llvm::raw_string_ostream ss(s); 471314564Sdim decl->dump(ss); 472314564Sdim ss.flush(); 473309124Sdim 474360784Sdim LLDB_LOGF(log, "Couldn't commit persistent decl: %s\n", s.c_str()); 475314564Sdim } 476309124Sdim 477314564Sdim continue; 478314564Sdim } 479309124Sdim 480314564Sdim if (NamedDecl *NamedDecl_scratch = dyn_cast<NamedDecl>(D_scratch)) 481360784Sdim persistent_vars->RegisterPersistentDecl(name_cs, NamedDecl_scratch, 482360784Sdim scratch_ctx); 483314564Sdim } 484309124Sdim} 485309124Sdim 486314564Sdimvoid ASTResultSynthesizer::HandleTagDeclDefinition(TagDecl *D) { 487314564Sdim if (m_passthrough) 488314564Sdim m_passthrough->HandleTagDeclDefinition(D); 489292932Sdim} 490292932Sdim 491314564Sdimvoid ASTResultSynthesizer::CompleteTentativeDefinition(VarDecl *D) { 492314564Sdim if (m_passthrough) 493314564Sdim m_passthrough->CompleteTentativeDefinition(D); 494292932Sdim} 495292932Sdim 496314564Sdimvoid ASTResultSynthesizer::HandleVTable(CXXRecordDecl *RD) { 497314564Sdim if (m_passthrough) 498314564Sdim m_passthrough->HandleVTable(RD); 499292932Sdim} 500292932Sdim 501314564Sdimvoid ASTResultSynthesizer::PrintStats() { 502314564Sdim if (m_passthrough) 503314564Sdim m_passthrough->PrintStats(); 504292932Sdim} 505292932Sdim 506314564Sdimvoid ASTResultSynthesizer::InitializeSema(Sema &S) { 507314564Sdim m_sema = &S; 508292932Sdim 509314564Sdim if (m_passthrough_sema) 510314564Sdim m_passthrough_sema->InitializeSema(S); 511292932Sdim} 512292932Sdim 513314564Sdimvoid ASTResultSynthesizer::ForgetSema() { 514353358Sdim m_sema = nullptr; 515292932Sdim 516314564Sdim if (m_passthrough_sema) 517314564Sdim m_passthrough_sema->ForgetSema(); 518292932Sdim} 519