From 16894348ccff87053158193311077b20d9ca8855 Mon Sep 17 00:00:00 2001 From: Gnimuc Date: Mon, 13 Jan 2025 18:49:11 +0900 Subject: [PATCH 1/2] Improve the test coverage of the C API --- lib/Interpreter/CXCppInterOp.cpp | 12 +-- lib/Interpreter/CppInterOp.cpp | 19 ++-- .../CppInterOp/FunctionReflectionTest.cpp | 93 +++++++++++++++++++ unittests/CppInterOp/InterpreterTest.cpp | 18 +++- unittests/CppInterOp/ScopeReflectionTest.cpp | 4 +- unittests/CppInterOp/TypeReflectionTest.cpp | 4 +- 6 files changed, 132 insertions(+), 18 deletions(-) diff --git a/lib/Interpreter/CXCppInterOp.cpp b/lib/Interpreter/CXCppInterOp.cpp index 35025b0ed..2b5569915 100644 --- a/lib/Interpreter/CXCppInterOp.cpp +++ b/lib/Interpreter/CXCppInterOp.cpp @@ -326,17 +326,15 @@ void clang_Interpreter_addIncludePath(CXInterpreter I, const char* dir) { getInterpreter(I)->AddIncludePath(dir); } +namespace Cpp { +int Declare(compat::Interpreter& interp, const char* code, bool silent); +} // namespace Cpp + enum CXErrorCode clang_Interpreter_declare(CXInterpreter I, const char* code, bool silent) { auto* interp = getInterpreter(I); - auto& diag = interp->getSema().getDiagnostics(); - - const bool is_silent_old = diag.getSuppressAllDiagnostics(); - - diag.setSuppressAllDiagnostics(silent); - const auto result = interp->declare(code); - diag.setSuppressAllDiagnostics(is_silent_old); + const auto result = Cpp::Declare(*interp, code, silent); if (result) return CXError_Failure; diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp index e4ef404a7..56e994989 100755 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -836,12 +836,17 @@ namespace Cpp { return false; } - TCppFunction_t GetDefaultConstructor(TCppScope_t scope) { + TCppFunction_t GetDefaultConstructor(compat::Interpreter& interp, + TCppScope_t scope) { if (!HasDefaultConstructor(scope)) return nullptr; auto *CXXRD = (clang::CXXRecordDecl*)scope; - return getSema().LookupDefaultConstructor(CXXRD); + return interp.getCI()->getSema().LookupDefaultConstructor(CXXRD); + } + + TCppFunction_t GetDefaultConstructor(TCppScope_t scope) { + return GetDefaultConstructor(getInterp(), scope); } TCppFunction_t GetDestructor(TCppScope_t scope) { @@ -3039,9 +3044,7 @@ namespace Cpp { }; } // namespace - int Declare(const char* code, bool silent) { - auto& I = getInterp(); - + int Declare(compat::Interpreter& I, const char* code, bool silent) { if (silent) { clangSilent diagSuppr(I.getSema().getDiagnostics()); return I.declare(code); @@ -3050,6 +3053,10 @@ namespace Cpp { return I.declare(code); } + int Declare(const char* code, bool silent) { + return Declare(getInterp(), code, silent); + } + int Process(const char *code) { return getInterp().process(code); } @@ -3533,7 +3540,7 @@ namespace Cpp { if (!HasDefaultConstructor(Class)) return nullptr; - auto* const Ctor = GetDefaultConstructor(Class); + auto* const Ctor = GetDefaultConstructor(interp, Class); if (JitCall JC = MakeFunctionCallable(&interp, Ctor)) { if (arena) { JC.Invoke(&arena, {}, (void*)~0); // Tell Invoke to use placement new. diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index 8862fbd72..8f0a26587 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -6,6 +6,8 @@ #include "clang/Interpreter/CppInterOp.h" #include "clang/Sema/Sema.h" +#include "clang-c/CXCppInterOp.h" + #include "gtest/gtest.h" #include @@ -105,6 +107,20 @@ TEST(FunctionReflectionTest, GetClassMethods) { std::vector methods5; Cpp::GetClassMethods(nullptr, methods5); EXPECT_EQ(methods5.size(), 0); + + // C API + auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + auto C_API_SHIM = [&](Cpp::TCppFunction_t method) { + auto Str = clang_getFunctionSignature( + make_scope(static_cast(method), I)); + auto Res = std::string(get_c_string(Str)); + dispose_string(Str); + return Res; + }; + EXPECT_EQ(C_API_SHIM(methods0[0]), "int A::f1(int a, int b)"); + // Clean up resources + clang_Interpreter_takeInterpreterAsPtr(I); + clang_Interpreter_dispose(I); } TEST(FunctionReflectionTest, ConstructorInGetClassMethods) { @@ -161,6 +177,15 @@ TEST(FunctionReflectionTest, HasDefaultConstructor) { EXPECT_TRUE(Cpp::HasDefaultConstructor(Decls[0])); EXPECT_TRUE(Cpp::HasDefaultConstructor(Decls[1])); EXPECT_FALSE(Cpp::HasDefaultConstructor(Decls[3])); + + // C API + auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + EXPECT_TRUE(clang_hasDefaultConstructor(make_scope(Decls[0], I))); + EXPECT_TRUE(clang_hasDefaultConstructor(make_scope(Decls[1], I))); + EXPECT_FALSE(clang_hasDefaultConstructor(make_scope(Decls[3], I))); + // Clean up resources + clang_Interpreter_takeInterpreterAsPtr(I); + clang_Interpreter_dispose(I); } TEST(FunctionReflectionTest, GetDestructor) { @@ -189,6 +214,14 @@ TEST(FunctionReflectionTest, GetDestructor) { EXPECT_TRUE(DeletedDtor); EXPECT_TRUE(Cpp::IsFunctionDeleted(DeletedDtor)); EXPECT_FALSE(Cpp::GetDestructor(Decls[3])); + + // C API + auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + EXPECT_TRUE(clang_getDestructor(make_scope(Decls[0], I)).data[0]); + EXPECT_TRUE(clang_getDestructor(make_scope(Decls[1], I)).data[0]); + // Clean up resources + clang_Interpreter_takeInterpreterAsPtr(I); + clang_Interpreter_dispose(I); } TEST(FunctionReflectionTest, GetFunctionsUsingName) { @@ -524,6 +557,17 @@ TEST(FunctionReflectionTest, IsTemplatedFunction) { EXPECT_FALSE(Cpp::IsTemplatedFunction(Decls[3])); EXPECT_FALSE(Cpp::IsTemplatedFunction(SubDeclsC1[1])); EXPECT_TRUE(Cpp::IsTemplatedFunction(SubDeclsC1[2])); + + // C API + auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + EXPECT_FALSE(clang_isTemplatedFunction(make_scope(Decls[0], I))); + EXPECT_TRUE(clang_isTemplatedFunction(make_scope(Decls[1], I))); + EXPECT_FALSE(clang_isTemplatedFunction(make_scope(Decls[3], I))); + EXPECT_FALSE(clang_isTemplatedFunction(make_scope(SubDeclsC1[1], I))); + EXPECT_TRUE(clang_isTemplatedFunction(make_scope(SubDeclsC1[2], I))); + // Clean up resources + clang_Interpreter_takeInterpreterAsPtr(I); + clang_Interpreter_dispose(I); } TEST(FunctionReflectionTest, ExistsFunctionTemplate) { @@ -544,6 +588,14 @@ TEST(FunctionReflectionTest, ExistsFunctionTemplate) { EXPECT_TRUE(Cpp::ExistsFunctionTemplate("f", 0)); EXPECT_TRUE(Cpp::ExistsFunctionTemplate("f", Decls[1])); EXPECT_FALSE(Cpp::ExistsFunctionTemplate("f", Decls[2])); + + // C API + auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + EXPECT_TRUE(clang_existsFunctionTemplate("f", make_scope(Decls[1], I))); + EXPECT_FALSE(clang_existsFunctionTemplate("f", make_scope(Decls[2], I))); + // Clean up resources + clang_Interpreter_takeInterpreterAsPtr(I); + clang_Interpreter_dispose(I); } TEST(FunctionReflectionTest, InstantiateTemplateFunctionFromString) { @@ -1119,6 +1171,17 @@ TEST(FunctionReflectionTest, JitCallAdvanced) { EXPECT_TRUE(object) << "Failed to call the ctor."; // Building a wrapper with a typedef decl must be possible. Cpp::Destruct(object, Decls[1]); + + // C API + auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + auto S = clang_getDefaultConstructor(make_scope(Decls[0], I)); + void* object_c = nullptr; + clang_invoke(S, &object_c, nullptr, 0, nullptr); + EXPECT_TRUE(object_c) << "Failed to call the ctor."; + clang_destruct(object_c, make_scope(Decls[1], I), true); + // Clean up resources + clang_Interpreter_takeInterpreterAsPtr(I); + clang_Interpreter_dispose(I); } @@ -1473,6 +1536,23 @@ TEST(FunctionReflectionTest, Construct) { Cpp::Deallocate(scope, where); output = testing::internal::GetCapturedStdout(); EXPECT_EQ(output, "Constructor Executed"); + output.clear(); + + // C API + testing::internal::CaptureStdout(); + auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + auto scope_c = make_scope(static_cast(scope), I); + auto object_c = clang_construct(scope_c, nullptr); + EXPECT_TRUE(object_c != nullptr); + output = testing::internal::GetCapturedStdout(); + EXPECT_EQ(output, "Constructor Executed"); + output.clear(); + auto* dummy = clang_allocate(8); + EXPECT_TRUE(dummy); + clang_deallocate(dummy); + // Clean up resources + clang_Interpreter_takeInterpreterAsPtr(I); + clang_Interpreter_dispose(I); } TEST(FunctionReflectionTest, Destruct) { @@ -1516,4 +1596,17 @@ TEST(FunctionReflectionTest, Destruct) { Cpp::Deallocate(scope, object); output = testing::internal::GetCapturedStdout(); EXPECT_EQ(output, "Destructor Executed"); + + // C API + testing::internal::CaptureStdout(); + auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + auto scope_c = make_scope(static_cast(scope), I); + auto object_c = clang_construct(scope_c, nullptr); + clang_destruct(object_c, scope_c, true); + output = testing::internal::GetCapturedStdout(); + EXPECT_EQ(output, "Destructor Executed"); + output.clear(); + // Clean up resources + clang_Interpreter_takeInterpreterAsPtr(I); + clang_Interpreter_dispose(I); } diff --git a/unittests/CppInterOp/InterpreterTest.cpp b/unittests/CppInterOp/InterpreterTest.cpp index 19ce70e13..459b71bc3 100644 --- a/unittests/CppInterOp/InterpreterTest.cpp +++ b/unittests/CppInterOp/InterpreterTest.cpp @@ -13,6 +13,7 @@ #include "clang/Basic/Version.h" +#include #include "clang-c/CXCppInterOp.h" #include "llvm/ADT/SmallString.h" @@ -85,16 +86,26 @@ TEST(InterpreterTest, Process) { #endif if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; - Cpp::CreateInterpreter(); + auto* I = Cpp::CreateInterpreter(); EXPECT_TRUE(Cpp::Process("") == 0); EXPECT_TRUE(Cpp::Process("int a = 12;") == 0); EXPECT_FALSE(Cpp::Process("error_here;") == 0); // Linker/JIT error. EXPECT_FALSE(Cpp::Process("int f(); int res = f();") == 0); + + // C API + auto* CXI = clang_createInterpreterFromRawPtr(I); + clang_Interpreter_declare(CXI, "#include ", false); + clang_Interpreter_process(CXI, "int c = 42;"); + auto* CXV = clang_createValue(); + auto Res = clang_Interpreter_evaluate(CXI, "c", CXV); + EXPECT_EQ(Res, CXError_Success); + clang_Value_dispose(CXV); + clang_Interpreter_dispose(CXI); } TEST(InterpreterTest, CreateInterpreter) { - auto I = Cpp::CreateInterpreter(); + auto* I = Cpp::CreateInterpreter(); EXPECT_TRUE(I); // Check if the default standard is c++14 @@ -118,9 +129,10 @@ TEST(InterpreterTest, CreateInterpreter) { #ifndef CPPINTEROP_USE_CLING // C API - auto CXI = clang_createInterpreterFromRawPtr(I); + auto* CXI = clang_createInterpreterFromRawPtr(I); auto CLI = clang_Interpreter_getClangInterpreter(CXI); EXPECT_TRUE(CLI); + auto I2 = clang_Interpreter_takeInterpreterAsPtr(CXI); EXPECT_EQ(I, I2); clang_Interpreter_dispose(CXI); diff --git a/unittests/CppInterOp/ScopeReflectionTest.cpp b/unittests/CppInterOp/ScopeReflectionTest.cpp index b3edf5b00..cda468bc0 100644 --- a/unittests/CppInterOp/ScopeReflectionTest.cpp +++ b/unittests/CppInterOp/ScopeReflectionTest.cpp @@ -15,6 +15,8 @@ #include "llvm/Support/Valgrind.h" +#include "clang-c/CXCppInterOp.h" + #include "gtest/gtest.h" #include @@ -816,7 +818,7 @@ TEST(ScopeReflectionTest, InstantiateNNTPClassTemplate) { /*type_size*/ args1.size())); // C API - auto I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); CXTemplateArgInfo Args1[] = {{IntTy, "5"}}; auto C_API_SHIM = [&](auto Decl) { return clang_instantiateTemplate(make_scope(Decl, I), Args1, 1).data[0]; diff --git a/unittests/CppInterOp/TypeReflectionTest.cpp b/unittests/CppInterOp/TypeReflectionTest.cpp index 4f47c0229..5c5e12118 100644 --- a/unittests/CppInterOp/TypeReflectionTest.cpp +++ b/unittests/CppInterOp/TypeReflectionTest.cpp @@ -5,6 +5,8 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Sema/Sema.h" +#include "clang-c/CXCppInterOp.h" + #include "gtest/gtest.h" #include @@ -354,7 +356,7 @@ TEST(TypeReflectionTest, GetComplexType) { EXPECT_EQ(get_complex_type_as_string("double"), "_Complex double"); // C API - auto I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); auto C_API_SHIM = [&](const std::string& element_type) { auto ElementQT = Cpp::GetType(element_type); CXQualType EQT = {CXType_Unexposed, {ElementQT, I}}; From 35cbc9c4a5ec70cb28e3c0196422adb0d4099f6d Mon Sep 17 00:00:00 2001 From: Gnimuc Date: Mon, 24 Mar 2025 19:08:10 +0900 Subject: [PATCH 2/2] Export runtime symbols --- lib/Interpreter/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Interpreter/CMakeLists.txt b/lib/Interpreter/CMakeLists.txt index 7568f7f3f..2d63940e2 100644 --- a/lib/Interpreter/CMakeLists.txt +++ b/lib/Interpreter/CMakeLists.txt @@ -130,6 +130,8 @@ if(EMSCRIPTEN) PRIVATE "SHELL: -s WASM_BIGINT" PRIVATE "SHELL: -s SIDE_MODULE=1" PRIVATE "SHELL: ${SYMBOLS_LIST}" + PUBLIC "SHELL: -Wl,--export=__clang_Interpreter_SetValueNoAlloc" + PUBLIC "SHELL: -Wl,--export=__clang_Interpreter_SetValueWithAlloc" ) if (CPPINTEROP_ENABLE_TESTING) # When compiling Emscripten tests the shared library it links to is expected to be in the same folder as the compiled Javascript