//===- ObjectTransformLayerTest.cpp - Unit tests for ObjectTransformLayer -===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "gtest/gtest.h" using namespace llvm::orc; namespace { // Stand-in for RuntimeDyld::MemoryManager typedef int MockMemoryManager; // Stand-in for RuntimeDyld::SymbolResolver typedef int MockSymbolResolver; // stand-in for object::ObjectFile typedef int MockObjectFile; // stand-in for llvm::MemoryBuffer set typedef int MockMemoryBufferSet; // Mock transform that operates on unique pointers to object files, and // allocates new object files rather than mutating the given ones. struct AllocatingTransform { std::unique_ptr<MockObjectFile> operator()(std::unique_ptr<MockObjectFile> Obj) const { return llvm::make_unique<MockObjectFile>(*Obj + 1); } }; // Mock base layer for verifying behavior of transform layer. // Each method "T foo(args)" is accompanied by two auxiliary methods: // - "void expectFoo(args)", to be called before calling foo on the transform // layer; saves values of args, which mock layer foo then verifies against. // - "void verifyFoo(T)", to be called after foo, which verifies that the // transform layer called the base layer and forwarded any return value. class MockBaseLayer { public: typedef int ObjSetHandleT; MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); } template <typename ObjSetT, typename MemoryManagerPtrT, typename SymbolResolverPtrT> ObjSetHandleT addObjectSet(ObjSetT &Objects, MemoryManagerPtrT MemMgr, SymbolResolverPtrT Resolver) { EXPECT_EQ(MockManager, *MemMgr) << "MM should pass through"; EXPECT_EQ(MockResolver, *Resolver) << "Resolver should pass through"; size_t I = 0; for (auto &ObjPtr : Objects) { EXPECT_EQ(MockObjects[I++] + 1, *ObjPtr) << "Transform should be applied"; } EXPECT_EQ(MockObjects.size(), I) << "Number of objects should match"; LastCalled = "addObjectSet"; MockObjSetHandle = 111; return MockObjSetHandle; } template <typename ObjSetT> void expectAddObjectSet(ObjSetT &Objects, MockMemoryManager *MemMgr, MockSymbolResolver *Resolver) { MockManager = *MemMgr; MockResolver = *Resolver; for (auto &ObjPtr : Objects) { MockObjects.push_back(*ObjPtr); } } void verifyAddObjectSet(ObjSetHandleT Returned) { EXPECT_EQ("addObjectSet", LastCalled); EXPECT_EQ(MockObjSetHandle, Returned) << "Return should pass through"; resetExpectations(); } void removeObjectSet(ObjSetHandleT H) { EXPECT_EQ(MockObjSetHandle, H); LastCalled = "removeObjectSet"; } void expectRemoveObjectSet(ObjSetHandleT H) { MockObjSetHandle = H; } void verifyRemoveObjectSet() { EXPECT_EQ("removeObjectSet", LastCalled); resetExpectations(); } JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) { EXPECT_EQ(MockName, Name) << "Name should pass through"; EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through"; LastCalled = "findSymbol"; MockSymbol = JITSymbol(122, llvm::JITSymbolFlags::None); return MockSymbol; } void expectFindSymbol(const std::string &Name, bool ExportedSymbolsOnly) { MockName = Name; MockBool = ExportedSymbolsOnly; } void verifyFindSymbol(llvm::orc::JITSymbol Returned) { EXPECT_EQ("findSymbol", LastCalled); EXPECT_EQ(MockSymbol.getAddress(), Returned.getAddress()) << "Return should pass through"; resetExpectations(); } JITSymbol findSymbolIn(ObjSetHandleT H, const std::string &Name, bool ExportedSymbolsOnly) { EXPECT_EQ(MockObjSetHandle, H) << "Handle should pass through"; EXPECT_EQ(MockName, Name) << "Name should pass through"; EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through"; LastCalled = "findSymbolIn"; MockSymbol = JITSymbol(122, llvm::JITSymbolFlags::None); return MockSymbol; } void expectFindSymbolIn(ObjSetHandleT H, const std::string &Name, bool ExportedSymbolsOnly) { MockObjSetHandle = H; MockName = Name; MockBool = ExportedSymbolsOnly; } void verifyFindSymbolIn(llvm::orc::JITSymbol Returned) { EXPECT_EQ("findSymbolIn", LastCalled); EXPECT_EQ(MockSymbol.getAddress(), Returned.getAddress()) << "Return should pass through"; resetExpectations(); } void emitAndFinalize(ObjSetHandleT H) { EXPECT_EQ(MockObjSetHandle, H) << "Handle should pass through"; LastCalled = "emitAndFinalize"; } void expectEmitAndFinalize(ObjSetHandleT H) { MockObjSetHandle = H; } void verifyEmitAndFinalize() { EXPECT_EQ("emitAndFinalize", LastCalled); resetExpectations(); } void mapSectionAddress(ObjSetHandleT H, const void *LocalAddress, TargetAddress TargetAddr) { EXPECT_EQ(MockObjSetHandle, H); EXPECT_EQ(MockLocalAddress, LocalAddress); EXPECT_EQ(MockTargetAddress, TargetAddr); LastCalled = "mapSectionAddress"; } void expectMapSectionAddress(ObjSetHandleT H, const void *LocalAddress, TargetAddress TargetAddr) { MockObjSetHandle = H; MockLocalAddress = LocalAddress; MockTargetAddress = TargetAddr; } void verifyMapSectionAddress() { EXPECT_EQ("mapSectionAddress", LastCalled); resetExpectations(); } private: // Backing fields for remembering parameter/return values std::string LastCalled; MockMemoryManager MockManager; MockSymbolResolver MockResolver; std::vector<MockObjectFile> MockObjects; ObjSetHandleT MockObjSetHandle; std::string MockName; bool MockBool; JITSymbol MockSymbol; const void *MockLocalAddress; TargetAddress MockTargetAddress; MockMemoryBufferSet MockBufferSet; // Clear remembered parameters between calls void resetExpectations() { LastCalled = "nothing"; MockManager = 0; MockResolver = 0; MockObjects.clear(); MockObjSetHandle = 0; MockName = "bogus"; MockSymbol = JITSymbol(nullptr); MockLocalAddress = nullptr; MockTargetAddress = 0; MockBufferSet = 0; } }; // Test each operation on ObjectTransformLayer. TEST(ObjectTransformLayerTest, Main) { MockBaseLayer M; // Create one object transform layer using a transform (as a functor) // that allocates new objects, and deals in unique pointers. ObjectTransformLayer<MockBaseLayer, AllocatingTransform> T1(M); // Create a second object transform layer using a transform (as a lambda) // that mutates objects in place, and deals in naked pointers ObjectTransformLayer<MockBaseLayer, std::function<MockObjectFile *(MockObjectFile *)>> T2(M, [](MockObjectFile *Obj) { ++(*Obj); return Obj; }); // Instantiate some mock objects to use below MockObjectFile MockObject1 = 211; MockObjectFile MockObject2 = 222; MockMemoryManager MockManager = 233; MockSymbolResolver MockResolver = 244; // Test addObjectSet with T1 (allocating, unique pointers) std::vector<std::unique_ptr<MockObjectFile>> Objs1; Objs1.push_back(llvm::make_unique<MockObjectFile>(MockObject1)); Objs1.push_back(llvm::make_unique<MockObjectFile>(MockObject2)); auto MM = llvm::make_unique<MockMemoryManager>(MockManager); auto SR = llvm::make_unique<MockSymbolResolver>(MockResolver); M.expectAddObjectSet(Objs1, MM.get(), SR.get()); auto H = T1.addObjectSet(Objs1, std::move(MM), std::move(SR)); M.verifyAddObjectSet(H); // Test addObjectSet with T2 (mutating, naked pointers) llvm::SmallVector<MockObjectFile *, 2> Objs2; Objs2.push_back(&MockObject1); Objs2.push_back(&MockObject2); M.expectAddObjectSet(Objs2, &MockManager, &MockResolver); H = T2.addObjectSet(Objs2, &MockManager, &MockResolver); M.verifyAddObjectSet(H); EXPECT_EQ(212, MockObject1) << "Expected mutation"; EXPECT_EQ(223, MockObject2) << "Expected mutation"; // Test removeObjectSet M.expectRemoveObjectSet(H); T1.removeObjectSet(H); M.verifyRemoveObjectSet(); // Test findSymbol std::string Name = "foo"; bool ExportedOnly = true; M.expectFindSymbol(Name, ExportedOnly); JITSymbol Symbol = T2.findSymbol(Name, ExportedOnly); M.verifyFindSymbol(Symbol); // Test findSymbolIn Name = "bar"; ExportedOnly = false; M.expectFindSymbolIn(H, Name, ExportedOnly); Symbol = T1.findSymbolIn(H, Name, ExportedOnly); M.verifyFindSymbolIn(Symbol); // Test emitAndFinalize M.expectEmitAndFinalize(H); T2.emitAndFinalize(H); M.verifyEmitAndFinalize(); // Test mapSectionAddress char Buffer[24]; TargetAddress MockAddress = 255; M.expectMapSectionAddress(H, Buffer, MockAddress); T1.mapSectionAddress(H, Buffer, MockAddress); M.verifyMapSectionAddress(); // Verify transform getter (non-const) MockObjectFile Mutatee = 277; MockObjectFile *Out = T2.getTransform()(&Mutatee); EXPECT_EQ(&Mutatee, Out) << "Expected in-place transform"; EXPECT_EQ(278, Mutatee) << "Expected incrementing transform"; // Verify transform getter (const) auto OwnedObj = llvm::make_unique<MockObjectFile>(288); const auto &T1C = T1; OwnedObj = T1C.getTransform()(std::move(OwnedObj)); EXPECT_EQ(289, *OwnedObj) << "Expected incrementing transform"; } }