|  | //===- unittests/Interpreter/InterpreterTest.cpp --- Interpreter tests ----===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // Unit tests for Clang's Interpreter library. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/Interpreter/Interpreter.h" | 
|  |  | 
|  | #include "clang/AST/Decl.h" | 
|  | #include "clang/AST/DeclGroup.h" | 
|  | #include "clang/AST/Mangle.h" | 
|  | #include "clang/Frontend/CompilerInstance.h" | 
|  | #include "clang/Frontend/TextDiagnosticPrinter.h" | 
|  | #include "clang/Interpreter/Value.h" | 
|  | #include "clang/Sema/Lookup.h" | 
|  | #include "clang/Sema/Sema.h" | 
|  |  | 
|  | #include "llvm/ExecutionEngine/Orc/LLJIT.h" | 
|  | #include "llvm/Support/ManagedStatic.h" | 
|  | #include "llvm/Support/TargetSelect.h" | 
|  |  | 
|  | #include "gmock/gmock.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace clang; | 
|  |  | 
|  | #if defined(_AIX) | 
|  | #define CLANG_INTERPRETER_NO_SUPPORT_EXEC | 
|  | #endif | 
|  |  | 
|  | #if LLVM_ADDRESS_SANITIZER_BUILD || LLVM_HWADDRESS_SANITIZER_BUILD | 
|  | #include <sanitizer/lsan_interface.h> | 
|  | #else | 
|  | extern "C" void __lsan_ignore_object(const void *p) {} | 
|  | #endif | 
|  |  | 
|  | int Global = 42; | 
|  | // JIT reports symbol not found on Windows without the visibility attribute. | 
|  | REPL_EXTERNAL_VISIBILITY int getGlobal() { return Global; } | 
|  | REPL_EXTERNAL_VISIBILITY void setGlobal(int val) { Global = val; } | 
|  |  | 
|  | namespace { | 
|  | using Args = std::vector<const char *>; | 
|  | static std::unique_ptr<Interpreter> | 
|  | createInterpreter(const Args &ExtraArgs = {}, | 
|  | DiagnosticConsumer *Client = nullptr) { | 
|  | Args ClangArgs = {"-Xclang", "-emit-llvm-only"}; | 
|  | ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end()); | 
|  | auto CB = clang::IncrementalCompilerBuilder(); | 
|  | CB.SetCompilerArgs(ClangArgs); | 
|  | auto CI = cantFail(CB.CreateCpp()); | 
|  | if (Client) | 
|  | CI->getDiagnostics().setClient(Client, /*ShouldOwnClient=*/false); | 
|  | return cantFail(clang::Interpreter::create(std::move(CI))); | 
|  | } | 
|  |  | 
|  | static size_t DeclsSize(TranslationUnitDecl *PTUDecl) { | 
|  | return std::distance(PTUDecl->decls().begin(), PTUDecl->decls().end()); | 
|  | } | 
|  |  | 
|  | TEST(InterpreterTest, Sanity) { | 
|  | std::unique_ptr<Interpreter> Interp = createInterpreter(); | 
|  |  | 
|  | using PTU = PartialTranslationUnit; | 
|  |  | 
|  | PTU &R1(cantFail(Interp->Parse("void g(); void g() {}"))); | 
|  | EXPECT_EQ(2U, DeclsSize(R1.TUPart)); | 
|  |  | 
|  | PTU &R2(cantFail(Interp->Parse("int i;"))); | 
|  | EXPECT_EQ(1U, DeclsSize(R2.TUPart)); | 
|  | } | 
|  |  | 
|  | static std::string DeclToString(Decl *D) { | 
|  | return llvm::cast<NamedDecl>(D)->getQualifiedNameAsString(); | 
|  | } | 
|  |  | 
|  | TEST(InterpreterTest, IncrementalInputTopLevelDecls) { | 
|  | std::unique_ptr<Interpreter> Interp = createInterpreter(); | 
|  | auto R1 = Interp->Parse("int var1 = 42; int f() { return var1; }"); | 
|  | // gtest doesn't expand into explicit bool conversions. | 
|  | EXPECT_TRUE(!!R1); | 
|  | auto R1DeclRange = R1->TUPart->decls(); | 
|  | EXPECT_EQ(2U, DeclsSize(R1->TUPart)); | 
|  | EXPECT_EQ("var1", DeclToString(*R1DeclRange.begin())); | 
|  | EXPECT_EQ("f", DeclToString(*(++R1DeclRange.begin()))); | 
|  |  | 
|  | auto R2 = Interp->Parse("int var2 = f();"); | 
|  | EXPECT_TRUE(!!R2); | 
|  | auto R2DeclRange = R2->TUPart->decls(); | 
|  | EXPECT_EQ(1U, DeclsSize(R2->TUPart)); | 
|  | EXPECT_EQ("var2", DeclToString(*R2DeclRange.begin())); | 
|  | } | 
|  |  | 
|  | TEST(InterpreterTest, Errors) { | 
|  | Args ExtraArgs = {"-Xclang", "-diagnostic-log-file", "-Xclang", "-"}; | 
|  |  | 
|  | // Create the diagnostic engine with unowned consumer. | 
|  | std::string DiagnosticOutput; | 
|  | llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); | 
|  | auto DiagPrinter = std::make_unique<TextDiagnosticPrinter>( | 
|  | DiagnosticsOS, new DiagnosticOptions()); | 
|  |  | 
|  | auto Interp = createInterpreter(ExtraArgs, DiagPrinter.get()); | 
|  | auto Err = Interp->Parse("intentional_error v1 = 42; ").takeError(); | 
|  | using ::testing::HasSubstr; | 
|  | EXPECT_THAT(DiagnosticsOS.str(), | 
|  | HasSubstr("error: unknown type name 'intentional_error'")); | 
|  | EXPECT_EQ("Parsing failed.", llvm::toString(std::move(Err))); | 
|  |  | 
|  | auto RecoverErr = Interp->Parse("int var1 = 42;"); | 
|  | EXPECT_TRUE(!!RecoverErr); | 
|  | } | 
|  |  | 
|  | // Here we test whether the user can mix declarations and statements. The | 
|  | // interpreter should be smart enough to recognize the declarations from the | 
|  | // statements and wrap the latter into a declaration, producing valid code. | 
|  | TEST(InterpreterTest, DeclsAndStatements) { | 
|  | Args ExtraArgs = {"-Xclang", "-diagnostic-log-file", "-Xclang", "-"}; | 
|  |  | 
|  | // Create the diagnostic engine with unowned consumer. | 
|  | std::string DiagnosticOutput; | 
|  | llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); | 
|  | auto DiagPrinter = std::make_unique<TextDiagnosticPrinter>( | 
|  | DiagnosticsOS, new DiagnosticOptions()); | 
|  |  | 
|  | auto Interp = createInterpreter(ExtraArgs, DiagPrinter.get()); | 
|  | auto R1 = Interp->Parse( | 
|  | "int var1 = 42; extern \"C\" int printf(const char*, ...);"); | 
|  | // gtest doesn't expand into explicit bool conversions. | 
|  | EXPECT_TRUE(!!R1); | 
|  |  | 
|  | auto *PTU1 = R1->TUPart; | 
|  | EXPECT_EQ(2U, DeclsSize(PTU1)); | 
|  |  | 
|  | auto R2 = Interp->Parse("var1++; printf(\"var1 value %d\\n\", var1);"); | 
|  | EXPECT_TRUE(!!R2); | 
|  | } | 
|  |  | 
|  | TEST(InterpreterTest, UndoCommand) { | 
|  | Args ExtraArgs = {"-Xclang", "-diagnostic-log-file", "-Xclang", "-"}; | 
|  |  | 
|  | // Create the diagnostic engine with unowned consumer. | 
|  | std::string DiagnosticOutput; | 
|  | llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); | 
|  | auto DiagPrinter = std::make_unique<TextDiagnosticPrinter>( | 
|  | DiagnosticsOS, new DiagnosticOptions()); | 
|  |  | 
|  | auto Interp = createInterpreter(ExtraArgs, DiagPrinter.get()); | 
|  |  | 
|  | // Fail to undo. | 
|  | auto Err1 = Interp->Undo(); | 
|  | EXPECT_EQ("Operation failed. Too many undos", | 
|  | llvm::toString(std::move(Err1))); | 
|  | auto Err2 = Interp->Parse("int foo = 42;"); | 
|  | EXPECT_TRUE(!!Err2); | 
|  | auto Err3 = Interp->Undo(2); | 
|  | EXPECT_EQ("Operation failed. Too many undos", | 
|  | llvm::toString(std::move(Err3))); | 
|  |  | 
|  | // Succeed to undo. | 
|  | auto Err4 = Interp->Parse("int x = 42;"); | 
|  | EXPECT_TRUE(!!Err4); | 
|  | auto Err5 = Interp->Undo(); | 
|  | EXPECT_FALSE(Err5); | 
|  | auto Err6 = Interp->Parse("int x = 24;"); | 
|  | EXPECT_TRUE(!!Err6); | 
|  | auto Err7 = Interp->Parse("#define X 42"); | 
|  | EXPECT_TRUE(!!Err7); | 
|  | auto Err8 = Interp->Undo(); | 
|  | EXPECT_FALSE(Err8); | 
|  | auto Err9 = Interp->Parse("#define X 24"); | 
|  | EXPECT_TRUE(!!Err9); | 
|  |  | 
|  | // Undo input contains errors. | 
|  | auto Err10 = Interp->Parse("int y = ;"); | 
|  | EXPECT_FALSE(!!Err10); | 
|  | EXPECT_EQ("Parsing failed.", llvm::toString(Err10.takeError())); | 
|  | auto Err11 = Interp->Parse("int y = 42;"); | 
|  | EXPECT_TRUE(!!Err11); | 
|  | auto Err12 = Interp->Undo(); | 
|  | EXPECT_FALSE(Err12); | 
|  | } | 
|  |  | 
|  | static std::string MangleName(NamedDecl *ND) { | 
|  | ASTContext &C = ND->getASTContext(); | 
|  | std::unique_ptr<MangleContext> MangleC(C.createMangleContext()); | 
|  | std::string mangledName; | 
|  | llvm::raw_string_ostream RawStr(mangledName); | 
|  | MangleC->mangleName(ND, RawStr); | 
|  | return RawStr.str(); | 
|  | } | 
|  |  | 
|  | static bool HostSupportsJit() { | 
|  | auto J = llvm::orc::LLJITBuilder().create(); | 
|  | if (J) | 
|  | return true; | 
|  | LLVMConsumeError(llvm::wrap(J.takeError())); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | struct LLVMInitRAII { | 
|  | LLVMInitRAII() { | 
|  | llvm::InitializeNativeTarget(); | 
|  | llvm::InitializeNativeTargetAsmPrinter(); | 
|  | } | 
|  | ~LLVMInitRAII() { llvm::llvm_shutdown(); } | 
|  | } LLVMInit; | 
|  |  | 
|  | #ifdef CLANG_INTERPRETER_NO_SUPPORT_EXEC | 
|  | TEST(IncrementalProcessing, DISABLED_FindMangledNameSymbol) { | 
|  | #else | 
|  | TEST(IncrementalProcessing, FindMangledNameSymbol) { | 
|  | #endif | 
|  |  | 
|  | std::unique_ptr<Interpreter> Interp = createInterpreter(); | 
|  |  | 
|  | auto &PTU(cantFail(Interp->Parse("int f(const char*) {return 0;}"))); | 
|  | EXPECT_EQ(1U, DeclsSize(PTU.TUPart)); | 
|  | auto R1DeclRange = PTU.TUPart->decls(); | 
|  |  | 
|  | // We cannot execute on the platform. | 
|  | if (!HostSupportsJit()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | NamedDecl *FD = cast<FunctionDecl>(*R1DeclRange.begin()); | 
|  | // Lower the PTU | 
|  | if (llvm::Error Err = Interp->Execute(PTU)) { | 
|  | // We cannot execute on the platform. | 
|  | consumeError(std::move(Err)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::string MangledName = MangleName(FD); | 
|  | auto Addr = Interp->getSymbolAddress(MangledName); | 
|  | EXPECT_FALSE(!Addr); | 
|  | EXPECT_NE(0U, Addr->getValue()); | 
|  | GlobalDecl GD(FD); | 
|  | EXPECT_EQ(*Addr, cantFail(Interp->getSymbolAddress(GD))); | 
|  | cantFail( | 
|  | Interp->ParseAndExecute("extern \"C\" int printf(const char*,...);")); | 
|  | Addr = Interp->getSymbolAddress("printf"); | 
|  | EXPECT_FALSE(!Addr); | 
|  |  | 
|  | // FIXME: Re-enable when we investigate the way we handle dllimports on Win. | 
|  | #ifndef _WIN32 | 
|  | EXPECT_EQ((uintptr_t)&printf, Addr->getValue()); | 
|  | #endif // _WIN32 | 
|  | } | 
|  |  | 
|  | static Value AllocateObject(TypeDecl *TD, Interpreter &Interp) { | 
|  | std::string Name = TD->getQualifiedNameAsString(); | 
|  | Value Addr; | 
|  | cantFail(Interp.ParseAndExecute("new " + Name + "()", &Addr)); | 
|  | return Addr; | 
|  | } | 
|  |  | 
|  | static NamedDecl *LookupSingleName(Interpreter &Interp, const char *Name) { | 
|  | Sema &SemaRef = Interp.getCompilerInstance()->getSema(); | 
|  | ASTContext &C = SemaRef.getASTContext(); | 
|  | DeclarationName DeclName = &C.Idents.get(Name); | 
|  | LookupResult R(SemaRef, DeclName, SourceLocation(), Sema::LookupOrdinaryName); | 
|  | SemaRef.LookupName(R, SemaRef.TUScope); | 
|  | assert(!R.empty()); | 
|  | return R.getFoundDecl(); | 
|  | } | 
|  |  | 
|  | #ifdef CLANG_INTERPRETER_NO_SUPPORT_EXEC | 
|  | TEST(IncrementalProcessing, DISABLED_InstantiateTemplate) { | 
|  | #else | 
|  | TEST(IncrementalProcessing, InstantiateTemplate) { | 
|  | #endif | 
|  | // FIXME: We cannot yet handle delayed template parsing. If we run with | 
|  | // -fdelayed-template-parsing we try adding the newly created decl to the | 
|  | // active PTU which causes an assert. | 
|  | std::vector<const char *> Args = {"-fno-delayed-template-parsing"}; | 
|  | std::unique_ptr<Interpreter> Interp = createInterpreter(Args); | 
|  |  | 
|  | llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);" | 
|  | "class A {};" | 
|  | "struct B {" | 
|  | "  template<typename T>" | 
|  | "  static int callme(T) { return 42; }" | 
|  | "};")); | 
|  | auto &PTU = llvm::cantFail(Interp->Parse("auto _t = &B::callme<A*>;")); | 
|  | auto PTUDeclRange = PTU.TUPart->decls(); | 
|  | EXPECT_EQ(1, std::distance(PTUDeclRange.begin(), PTUDeclRange.end())); | 
|  |  | 
|  | // We cannot execute on the platform. | 
|  | if (!HostSupportsJit()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Lower the PTU | 
|  | if (llvm::Error Err = Interp->Execute(PTU)) { | 
|  | // We cannot execute on the platform. | 
|  | consumeError(std::move(Err)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | TypeDecl *TD = cast<TypeDecl>(LookupSingleName(*Interp, "A")); | 
|  | Value NewA = AllocateObject(TD, *Interp); | 
|  |  | 
|  | // Find back the template specialization | 
|  | VarDecl *VD = static_cast<VarDecl *>(*PTUDeclRange.begin()); | 
|  | UnaryOperator *UO = llvm::cast<UnaryOperator>(VD->getInit()); | 
|  | NamedDecl *TmpltSpec = llvm::cast<DeclRefExpr>(UO->getSubExpr())->getDecl(); | 
|  |  | 
|  | std::string MangledName = MangleName(TmpltSpec); | 
|  | typedef int (*TemplateSpecFn)(void *); | 
|  | auto fn = | 
|  | cantFail(Interp->getSymbolAddress(MangledName)).toPtr<TemplateSpecFn>(); | 
|  | EXPECT_EQ(42, fn(NewA.getPtr())); | 
|  | // FIXME: release the memory. | 
|  | __lsan_ignore_object(NewA.getPtr()); | 
|  | } | 
|  |  | 
|  | #ifdef CLANG_INTERPRETER_NO_SUPPORT_EXEC | 
|  | TEST(InterpreterTest, DISABLED_Value) { | 
|  | #else | 
|  | TEST(InterpreterTest, Value) { | 
|  | #endif | 
|  | // We cannot execute on the platform. | 
|  | if (!HostSupportsJit()) | 
|  | return; | 
|  |  | 
|  | std::unique_ptr<Interpreter> Interp = createInterpreter(); | 
|  |  | 
|  | Value V1; | 
|  | llvm::cantFail(Interp->ParseAndExecute("int x = 42;")); | 
|  | llvm::cantFail(Interp->ParseAndExecute("x", &V1)); | 
|  | EXPECT_TRUE(V1.isValid()); | 
|  | EXPECT_TRUE(V1.hasValue()); | 
|  | EXPECT_EQ(V1.getInt(), 42); | 
|  | EXPECT_EQ(V1.convertTo<int>(), 42); | 
|  | EXPECT_TRUE(V1.getType()->isIntegerType()); | 
|  | EXPECT_EQ(V1.getKind(), Value::K_Int); | 
|  | EXPECT_FALSE(V1.isManuallyAlloc()); | 
|  |  | 
|  | Value V2; | 
|  | llvm::cantFail(Interp->ParseAndExecute("double y = 3.14;")); | 
|  | llvm::cantFail(Interp->ParseAndExecute("y", &V2)); | 
|  | EXPECT_TRUE(V2.isValid()); | 
|  | EXPECT_TRUE(V2.hasValue()); | 
|  | EXPECT_EQ(V2.getDouble(), 3.14); | 
|  | EXPECT_EQ(V2.convertTo<double>(), 3.14); | 
|  | EXPECT_TRUE(V2.getType()->isFloatingType()); | 
|  | EXPECT_EQ(V2.getKind(), Value::K_Double); | 
|  | EXPECT_FALSE(V2.isManuallyAlloc()); | 
|  |  | 
|  | Value V3; | 
|  | llvm::cantFail(Interp->ParseAndExecute( | 
|  | "struct S { int* p; S() { p = new int(42); } ~S() { delete p; }};")); | 
|  | llvm::cantFail(Interp->ParseAndExecute("S{}", &V3)); | 
|  | EXPECT_TRUE(V3.isValid()); | 
|  | EXPECT_TRUE(V3.hasValue()); | 
|  | EXPECT_TRUE(V3.getType()->isRecordType()); | 
|  | EXPECT_EQ(V3.getKind(), Value::K_PtrOrObj); | 
|  | EXPECT_TRUE(V3.isManuallyAlloc()); | 
|  |  | 
|  | Value V4; | 
|  | llvm::cantFail(Interp->ParseAndExecute("int getGlobal();")); | 
|  | llvm::cantFail(Interp->ParseAndExecute("void setGlobal(int);")); | 
|  | llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V4)); | 
|  | EXPECT_EQ(V4.getInt(), 42); | 
|  | EXPECT_TRUE(V4.getType()->isIntegerType()); | 
|  |  | 
|  | Value V5; | 
|  | // Change the global from the compiled code. | 
|  | setGlobal(43); | 
|  | llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V5)); | 
|  | EXPECT_EQ(V5.getInt(), 43); | 
|  | EXPECT_TRUE(V5.getType()->isIntegerType()); | 
|  |  | 
|  | // Change the global from the interpreted code. | 
|  | llvm::cantFail(Interp->ParseAndExecute("setGlobal(44);")); | 
|  | EXPECT_EQ(getGlobal(), 44); | 
|  |  | 
|  | Value V6; | 
|  | llvm::cantFail(Interp->ParseAndExecute("void foo() {}")); | 
|  | llvm::cantFail(Interp->ParseAndExecute("foo()", &V6)); | 
|  | EXPECT_TRUE(V6.isValid()); | 
|  | EXPECT_FALSE(V6.hasValue()); | 
|  | EXPECT_TRUE(V6.getType()->isVoidType()); | 
|  | EXPECT_EQ(V6.getKind(), Value::K_Void); | 
|  | EXPECT_FALSE(V2.isManuallyAlloc()); | 
|  |  | 
|  | Value V7; | 
|  | llvm::cantFail(Interp->ParseAndExecute("foo", &V7)); | 
|  | EXPECT_TRUE(V7.isValid()); | 
|  | EXPECT_TRUE(V7.hasValue()); | 
|  | EXPECT_TRUE(V7.getType()->isFunctionProtoType()); | 
|  | EXPECT_EQ(V7.getKind(), Value::K_PtrOrObj); | 
|  | EXPECT_FALSE(V7.isManuallyAlloc()); | 
|  |  | 
|  | Value V8; | 
|  | llvm::cantFail(Interp->ParseAndExecute("struct SS{ void f() {} };")); | 
|  | llvm::cantFail(Interp->ParseAndExecute("&SS::f", &V8)); | 
|  | EXPECT_TRUE(V8.isValid()); | 
|  | EXPECT_TRUE(V8.hasValue()); | 
|  | EXPECT_TRUE(V8.getType()->isMemberFunctionPointerType()); | 
|  | EXPECT_EQ(V8.getKind(), Value::K_PtrOrObj); | 
|  | EXPECT_TRUE(V8.isManuallyAlloc()); | 
|  |  | 
|  | Value V9; | 
|  | llvm::cantFail(Interp->ParseAndExecute("struct A { virtual int f(); };")); | 
|  | llvm::cantFail( | 
|  | Interp->ParseAndExecute("struct B : A { int f() { return 42; }};")); | 
|  | llvm::cantFail(Interp->ParseAndExecute("int (B::*ptr)() = &B::f;")); | 
|  | llvm::cantFail(Interp->ParseAndExecute("ptr", &V9)); | 
|  | EXPECT_TRUE(V9.isValid()); | 
|  | EXPECT_TRUE(V9.hasValue()); | 
|  | EXPECT_TRUE(V9.getType()->isMemberFunctionPointerType()); | 
|  | EXPECT_EQ(V9.getKind(), Value::K_PtrOrObj); | 
|  | EXPECT_TRUE(V9.isManuallyAlloc()); | 
|  | } | 
|  | } // end anonymous namespace |