diff --git a/core/metacling/src/TClingCallFunc.cxx b/core/metacling/src/TClingCallFunc.cxx index 7d4b93c8810e0..ff80a07017fac 100644 --- a/core/metacling/src/TClingCallFunc.cxx +++ b/core/metacling/src/TClingCallFunc.cxx @@ -1065,197 +1065,6 @@ static std::string PrepareStructorWrapper(const Decl *D, const char *wrapper_pre return wrapper_name; } -tcling_callfunc_ctor_Wrapper_t TClingCallFunc::make_ctor_wrapper(const TClingClassInfo *info, - ROOT::TMetaUtils::EIOCtorCategory kind, const std::string &type_name) -{ - // Make a code string that follows this pattern: - // - // void - // unique_wrapper_ddd(void** ret, void* arena, unsigned long nary) - // { - // if (!arena) { - // if (!nary) { - // *ret = new ClassName; - // } - // else { - // *ret = new ClassName[nary]; - // } - // } - // else { - // if (!nary) { - // *ret = new (arena) ClassName; - // } - // else { - // *ret = new (arena) ClassName[nary]; - // } - // } - // } - // - // When I/O constructor used: - // - // void - // unique_wrapper_ddd(void** ret, void* arena, unsigned long nary) - // { - // if (!arena) { - // if (!nary) { - // *ret = new ClassName((TRootIOCtor*)nullptr); - // } - // else { - // char *buf = malloc(nary * sizeof(ClassName)); - // for (int k=0;k gCtorWrapperStore; - - R__LOCKGUARD_CLING(gInterpreterMutex); - - auto D = info->GetDecl(); - auto I = gCtorWrapperStore.find(D); - if (I != gCtorWrapperStore.end()) - return (tcling_callfunc_ctor_Wrapper_t)I->second; - - // - // Make the wrapper name. - // - string class_name; - string wrapper_name = PrepareStructorWrapper(D, "__ctor", class_name); - - string constr_arg; - if (kind == ROOT::TMetaUtils::EIOCtorCategory::kIOPtrType) - constr_arg = string("((") + type_name + "*)nullptr)"; - else if (kind == ROOT::TMetaUtils::EIOCtorCategory::kIORefType) - constr_arg = string("(*((") + type_name + "*)arena))"; - - // - // Write the wrapper code. - // - int indent_level = 0; - ostringstream buf; - buf << "__attribute__((used)) "; - buf << "extern \"C\" void "; - buf << wrapper_name; - buf << "(void** ret, void* arena, unsigned long nary)\n"; - buf << "{\n"; - - // if (!arena) { - // if (!nary) { - // *ret = new ClassName; - // } - // else { - // *ret = new ClassName[nary]; - // } - // } - indent(buf, ++indent_level); - buf << "if (!arena) {\n"; - indent(buf, ++indent_level); - buf << "if (!nary) {\n"; - indent(buf, ++indent_level); - buf << "*ret = new " << class_name << constr_arg << ";\n"; - indent(buf, --indent_level); - buf << "}\n"; - indent(buf, indent_level); - buf << "else {\n"; - indent(buf, ++indent_level); - if (constr_arg.empty()) { - buf << "*ret = new " << class_name << "[nary];\n"; - } else { - buf << "char *buf = (char *) malloc(nary * sizeof(" << class_name << "));\n"; - indent(buf, indent_level); - buf << "for (int k=0;kHasDefaultConstructor()) { - // // FIXME: We might have a ROOT ioctor, we might - // // have to check for that here. - // ::Error("TClingCallFunc::ExecDefaultConstructor", - // "Class has no default constructor: %s", - // info->Name()); - // return 0; - //} - void *obj = nullptr; - (*wrapper)(&obj, address, nary); - return obj; - } - ::Error("TClingCallFunc::ExecDefaultConstructor", "Called with no wrapper, not implemented!"); + // this function's clients assume nary = 0 for operator new and nary > 0 for array new. JitCall expects + // the number of objects you want to construct; nary = 1 constructs a single object + // This handles this difference in semantics + if (nary == 0) + nary = 1; + + clang::Decl *D = const_cast(info->GetDecl()); + + if (Cpp::IsClass(D) || Cpp::IsConstructor(D)) + return Cpp::Construct(D, address, nary); + + ::Error("TClingCallFunc::ExecDefaultConstructor", "ClassInfo missing a valid Scope/Constructor"); return nullptr; } diff --git a/core/metacling/src/TClingCallFunc.h b/core/metacling/src/TClingCallFunc.h index 62510764ae505..3a54a286d4ff7 100644 --- a/core/metacling/src/TClingCallFunc.h +++ b/core/metacling/src/TClingCallFunc.h @@ -33,6 +33,7 @@ #include "TInterpreter.h" #include "cling/Interpreter/Value.h" +#include #include "clang/AST/ASTContext.h" #include "llvm/ADT/SmallVector.h" @@ -53,9 +54,8 @@ class TClingClassInfo; class TClingMethodInfo; class TInterpreterValue; -typedef void (*tcling_callfunc_Wrapper_t)(void*, int, void**, void*); -typedef void (*tcling_callfunc_ctor_Wrapper_t)(void**, void*, unsigned long); -typedef void (*tcling_callfunc_dtor_Wrapper_t)(void*, unsigned long, int); +typedef void (*tcling_callfunc_Wrapper_t)(void *, int, void **, void *); +typedef void (*tcling_callfunc_dtor_Wrapper_t)(void *, unsigned long, int); class TClingCallFunc { @@ -109,9 +109,6 @@ class TClingCallFunc { tcling_callfunc_Wrapper_t make_wrapper(); - tcling_callfunc_ctor_Wrapper_t - make_ctor_wrapper(const TClingClassInfo *, ROOT::TMetaUtils::EIOCtorCategory, const std::string &); - tcling_callfunc_dtor_Wrapper_t make_dtor_wrapper(const TClingClassInfo* info); diff --git a/interpreter/CppInterOp/.clang-format b/interpreter/CppInterOp/.clang-format index 23d6f8ef56464..3cb372d93c544 100644 --- a/interpreter/CppInterOp/.clang-format +++ b/interpreter/CppInterOp/.clang-format @@ -1,7 +1,7 @@ BasedOnStyle: LLVM Language: Cpp -Standard: Cpp11 +Standard: c++17 PointerAlignment: Left IncludeCategories: diff --git a/interpreter/CppInterOp/Emscripten-build-instructions.md b/interpreter/CppInterOp/Emscripten-build-instructions.md index 3bc3b444d2b3e..e41912bcc1c58 100644 --- a/interpreter/CppInterOp/Emscripten-build-instructions.md +++ b/interpreter/CppInterOp/Emscripten-build-instructions.md @@ -173,6 +173,7 @@ $env:CMAKE_SYSTEM_PREFIX_PATH=$env:PREFIX ``` on Windows. Now to build and test your Emscripten build of CppInterOp using node on Linux and osx execute the following +(BUILD_SHARED_LIBS=ON is only needed if building xeus-cpp, as CppInterOp can be built as an Emscripten static library) ```bash mkdir build @@ -190,6 +191,7 @@ emmake make -j $(nproc --all) check-cppinterop ``` To build and test your Emscripten build of CppInterOp on using node Windows execute the following +(BUILD_SHARED_LIBS=ON is only needed if building xeus-cpp, as CppInterOp can be built as an Emscripten static library) ```powershell emcmake cmake -DCMAKE_BUILD_TYPE=Release ` diff --git a/interpreter/CppInterOp/cmake/modules/GoogleTest.cmake b/interpreter/CppInterOp/cmake/modules/GoogleTest.cmake index 78e924443a843..4df2ff0474017 100644 --- a/interpreter/CppInterOp/cmake/modules/GoogleTest.cmake +++ b/interpreter/CppInterOp/cmake/modules/GoogleTest.cmake @@ -22,10 +22,18 @@ include(ExternalProject) if (EMSCRIPTEN) if (CMAKE_C_COMPILER MATCHES ".bat") set(config_cmd emcmake.bat cmake) - set(build_cmd emmake.bat make) + if(CMAKE_GENERATOR STREQUAL "Ninja") + set(build_cmd emmake.bat ninja) + else() + set(build_cmd emmake.bat make) + endif() else() set(config_cmd emcmake cmake) - set(build_cmd emmake make) + if(CMAKE_GENERATOR STREQUAL "Ninja") + set(build_cmd emmake ninja) + else() + set(build_cmd emmake make) + endif() endif() else() set(config_cmd ${CMAKE_COMMAND}) diff --git a/interpreter/CppInterOp/cppinterop-version.tag b/interpreter/CppInterOp/cppinterop-version.tag index 9e113fd24e0f8..b4773ac5239e8 100644 --- a/interpreter/CppInterOp/cppinterop-version.tag +++ b/interpreter/CppInterOp/cppinterop-version.tag @@ -1 +1 @@ -130dd30548b59d8f618339158ab917c083184968 +9c9423069529caea5aee79b61580fe9a7b1eb73b diff --git a/interpreter/CppInterOp/docs/DebuggingCppInterOp.rst b/interpreter/CppInterOp/docs/DebuggingCppInterOp.rst index 067b7812844c0..6ef04acd5326f 100644 --- a/interpreter/CppInterOp/docs/DebuggingCppInterOp.rst +++ b/interpreter/CppInterOp/docs/DebuggingCppInterOp.rst @@ -48,7 +48,7 @@ Here's a comprehensive example that demonstrates common CppInterOp usage pattern .. code-block:: cpp - #include "clang/Interpreter/CppInterOp.h" + #include #include void run_code(std::string code) { diff --git a/interpreter/CppInterOp/docs/Emscripten-build-instructions.rst b/interpreter/CppInterOp/docs/Emscripten-build-instructions.rst index 94403e8366e4d..57362738221ba 100644 --- a/interpreter/CppInterOp/docs/Emscripten-build-instructions.rst +++ b/interpreter/CppInterOp/docs/Emscripten-build-instructions.rst @@ -197,6 +197,7 @@ and $env:CMAKE_SYSTEM_PREFIX_PATH=$env:PREFIX on Windows. Now to build and test your Emscripten build of CppInterOp on Linux and osx execute the following +(BUILD_SHARED_LIBS=ON is only needed if building xeus-cpp, as CppInterOp can be built as an Emscripten static library) .. code:: bash @@ -214,6 +215,7 @@ on Windows. Now to build and test your Emscripten build of CppInterOp on Linux a emmake make -j $(nproc --all) check-cppinterop To build and test your Emscripten build of CppInterOp on Windows execute the following +(BUILD_SHARED_LIBS=ON is only needed if building xeus-cpp, as CppInterOp can be built as an Emscripten static library) .. code:: powershell diff --git a/interpreter/CppInterOp/include/CppInterOp/CppInterOp.h b/interpreter/CppInterOp/include/CppInterOp/CppInterOp.h index 2f3d330dbb749..7b428e4b28061 100644 --- a/interpreter/CppInterOp/include/CppInterOp/CppInterOp.h +++ b/interpreter/CppInterOp/include/CppInterOp/CppInterOp.h @@ -224,6 +224,8 @@ class JitCall { ///\param[in] nary - Use array new if we have to construct an array of /// objects (nary > 1). ///\param[in] args - a pointer to a argument list and argument size. + ///\param[in] is_arena - a pointer that indicates if placement new is to be + /// used // FIXME: Change the type of withFree from int to bool in the wrapper code. void InvokeConstructor(void* result, unsigned long nary = 1, ArgList args = {}, void* is_arena = nullptr) const { @@ -849,7 +851,7 @@ CPPINTEROP_API void Deallocate(TCppScope_t scope, TCppObject_t address, /// Creates one or more objects of class \c scope by calling its default /// constructor. -/// \param[in] scope Class to construct +/// \param[in] scope Class to construct, or handle to Constructor /// \param[in] arena If set, this API uses placement new to construct at this /// address. /// \param[in] is used to indicate the number of objects to construct. diff --git a/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt b/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt index 8835511b4f85f..142d4d5d92945 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt +++ b/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt @@ -114,6 +114,7 @@ endif() if(EMSCRIPTEN) + if(BUILD_SHARED_LIBS) # FIXME: When dynamically linking the Emscripten shared library to the # unit tests main_module you get errors due to undefined symbols. The reading of the file # below into a SYMBOLS_LIST variable is a temporary workaround that exports the undefined @@ -131,8 +132,13 @@ if(EMSCRIPTEN) PRIVATE "SHELL: -s SIDE_MODULE=1" PRIVATE "SHELL: ${SYMBOLS_LIST}" ) +else() + target_link_options(clangCppInterOp + PRIVATE "SHELL: -s WASM_BIGINT" + ) +endif() 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 + # When compiling Emscripten tests the CppInterOp library it links to is expected to be in the same folder as the compiled Javascript add_custom_command(TARGET clangCppInterOp POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_BINARY_DIR}/unittests/CppInterOp/ ) diff --git a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp index bcbc123599805..cd776dbcef49e 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp +++ b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp @@ -616,8 +616,6 @@ static Decl* GetScopeFromType(QualType QT) { Type = Type->getUnqualifiedDesugaredType(); if (auto* ET = llvm::dyn_cast(Type)) return ET->getDecl(); - if (auto* FnType = llvm::dyn_cast(Type)) - Type = const_cast(FnType->getReturnType().getTypePtr()); return Type->getAsCXXRecordDecl(); } return 0; @@ -1327,6 +1325,8 @@ void GetDatamembers(TCppScope_t scope, std::vector& datamembers) { if (auto* CXXRD = llvm::dyn_cast_or_null(D)) { getSema().ForceDeclarationOfImplicitMembers(CXXRD); + if (CXXRD->hasDefinition()) + CXXRD = CXXRD->getDefinition(); llvm::SmallVector stack_begin; llvm::SmallVector stack_end; @@ -1447,7 +1447,7 @@ intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D, } offset += C.toCharUnitsFromBits(C.getFieldOffset(FD)).getQuantity(); } - if (BaseCXXRD && BaseCXXRD != FieldParentRecordDecl) { + if (BaseCXXRD && BaseCXXRD != FieldParentRecordDecl->getCanonicalDecl()) { // FieldDecl FD belongs to some class C, but the base class BaseCXXRD is // not C. That means BaseCXXRD derives from C. Offset needs to be // calculated for Derived class @@ -1476,7 +1476,7 @@ intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D, } if (auto* RD = llvm::dyn_cast(FieldParentRecordDecl)) { // add in the offsets for the (multi level) base classes - while (BaseCXXRD != RD) { + while (BaseCXXRD != RD->getCanonicalDecl()) { CXXRecordDecl* Parent = direction.at(RD); offset += C.getASTRecordLayout(Parent).getBaseClassOffset(RD).getQuantity(); @@ -1689,6 +1689,10 @@ std::string GetTypeAsString(TCppType_t var) { PrintingPolicy Policy((LangOptions())); Policy.Bool = true; // Print bool instead of _Bool. Policy.SuppressTagKeyword = true; // Do not print `class std::string`. +#if CLANG_VERSION_MAJOR > 16 + Policy.SuppressElaboration = true; +#endif + Policy.FullyQualifiedName = true; return compat::FixTypeName(QT.getAsString(Policy)); } @@ -1833,6 +1837,7 @@ void get_type_as_string(QualType QT, std::string& type_name, ASTContext& C, #if CLANG_VERSION_MAJOR > 16 Policy.SuppressElaboration = true; #endif + Policy.SuppressTagKeyword = !QT->isEnumeralType(); Policy.FullyQualifiedName = true; QT.getAsStringInternal(type_name, Policy); } @@ -2103,6 +2108,30 @@ void make_narg_call(const FunctionDecl* FD, const std::string& return_type, std::string template_args = complete_name.substr(idx); name = name_without_template_args + (template_args.empty() ? "" : " " + template_args); + + // If a template has consecutive parameter packs, then it is impossible to + // use the explicit name in the wrapper, since the type deduction is what + // determines the split of the packs. Instead, we'll revert to the + // non-templated function name and hope that the type casts in the wrapper + // will suffice. + if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) { + const FunctionTemplateDecl* FTDecl = + llvm::dyn_cast(FD->getPrimaryTemplate()); + if (FTDecl) { + auto* templateParms = FTDecl->getTemplateParameters(); + int numPacks = 0; + for (size_t iParam = 0, nParams = templateParms->size(); + iParam < nParams; ++iParam) { + if (templateParms->getParam(iParam)->isTemplateParameterPack()) + numPacks += 1; + else + numPacks = 0; + } + if (numPacks > 1) { + name = name_without_template_args; + } + } + } } if (op_flag || N <= 1) callbuf << name; @@ -2707,7 +2736,7 @@ int get_wrapper_code(compat::Interpreter& I, const FunctionDecl* FD, // { std::ostringstream buf; - buf << "__cf"; + buf << "__jc"; // const NamedDecl* ND = dyn_cast(FD); // std::string mn; // fInterp->maybeMangleDeclName(ND, mn); @@ -3422,6 +3451,8 @@ static Decl* InstantiateTemplate(TemplateDecl* TemplateD, // This will instantiate tape type and return it. SourceLocation noLoc; QualType TT = S.CheckTemplateIdType(TemplateName(TemplateD), noLoc, TLI); + if (TT.isNull()) + return nullptr; // Perhaps we can extract this into a new interface. S.RequireCompleteType(fakeLoc, TT, diag::err_tentative_def_incomplete_type); @@ -3753,22 +3784,25 @@ void Deallocate(TCppScope_t scope, TCppObject_t address, TCppIndex_t count) { // FIXME: Add optional arguments to the operator new. TCppObject_t Construct(compat::Interpreter& interp, TCppScope_t scope, void* arena /*=nullptr*/, TCppIndex_t count /*=1UL*/) { - auto* Class = (Decl*)scope; - // FIXME: Diagnose. - if (!HasDefaultConstructor(Class)) - return nullptr; - auto* const Ctor = GetDefaultConstructor(interp, Class); - if (JitCall JC = MakeFunctionCallable(&interp, Ctor)) { - if (arena) { - JC.InvokeConstructor(&arena, count, {}, - (void*)~0); // Tell Invoke to use placement new. - return arena; - } + if (!Cpp::IsConstructor(scope) && !Cpp::IsClass(scope)) + return nullptr; + if (Cpp::IsClass(scope) && !HasDefaultConstructor(scope)) + return nullptr; - void* obj = nullptr; - JC.InvokeConstructor(&obj, count, {}, nullptr); - return obj; + TCppFunction_t ctor = nullptr; + if (Cpp::IsClass(scope)) + ctor = Cpp::GetDefaultConstructor(scope); + else // a ctor + ctor = scope; + + if (JitCall JC = MakeFunctionCallable(&interp, ctor)) { + // invoke the constructor (placement/heap) in one shot + // flag is non-null for placement new, null for normal new + void* is_arena = arena ? reinterpret_cast(1) : nullptr; + void* result = arena; + JC.InvokeConstructor(&result, count, /*args=*/{}, is_arena); + return result; } return nullptr; } diff --git a/interpreter/CppInterOp/unittests/CMakeLists.txt b/interpreter/CppInterOp/unittests/CMakeLists.txt index 0ea65be871ba1..f13b85f68f497 100644 --- a/interpreter/CppInterOp/unittests/CMakeLists.txt +++ b/interpreter/CppInterOp/unittests/CMakeLists.txt @@ -3,16 +3,23 @@ set(CTEST_BUILD_NAME enable_testing() # LLVM builds (not installed llvm) provides gtest. -if (NOT TARGET gtest) +if (NOT TARGET GTest::gtest AND NOT TARGET gtest) include(GoogleTest) endif() if(EMSCRIPTEN) - set(gtest_libs gtest gmock) + if (TARGET GTest::gtest) + # Target names in CMake >= v3.23 + set(gtest_libs GTest::gtest GTest::gmock) + else() + set(gtest_libs gtest gmock) + endif() else() - set(gtest_libs gtest gtest_main) - if (TARGET gmock) - list(APPEND gtest_libs gmock gmock_main) + if (TARGET GTest::gtest) + # Target names in CMake >= v3.23 + set(gtest_libs GTest::gtest GTest::gmock GTest::gtest_main) + else() + set(gtest_libs gtest gtest_main gmock) endif() set(link_pthreads_lib pthread) endif() diff --git a/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt b/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt index 3372dd1477b54..8cc63038acf0a 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt +++ b/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt @@ -112,6 +112,16 @@ if(EMSCRIPTEN) ) endif() +if (EMSCRIPTEN) + if (BUILD_SHARED_LIBS) + target_compile_definitions(CppInterOpTests PRIVATE "EMSCRIPTEN_SHARED_LIBRARY") + target_compile_definitions(DynamicLibraryManagerTests PRIVATE "EMSCRIPTEN_SHARED_LIBRARY") + else() + target_compile_definitions(CppInterOpTests PRIVATE "EMSCRIPTEN_STATIC_LIBRARY") + target_compile_definitions(DynamicLibraryManagerTests PRIVATE "EMSCRIPTEN_STATIC_LIBRARY") + endif() +endif() + set_output_directory(DynamicLibraryManagerTests BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${EXTRA_PATH_TEST_BINARIES} LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${EXTRA_PATH_TEST_BINARIES} diff --git a/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp index 7ca9f9d4376ed..099e55aa2a5e1 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -1969,6 +1969,46 @@ TEST(FunctionReflectionTest, GetFunctionCallWrapper) { EXPECT_TRUE(err_msg.find("instantiation with no body") != std::string::npos); EXPECT_EQ(instantiation_in_host_callable.getKind(), Cpp::JitCall::kUnknown); // expect to fail + + Interp->process(R"( + template + struct tuple {}; + + template + void tuple_tuple(tuple one, tuple two) { return; } + + tuple tuple_one; + tuple tuple_two; + )"); + + unresolved_candidate_methods.clear(); + Cpp::GetClassTemplatedMethods("tuple_tuple", Cpp::GetGlobalScope(), + unresolved_candidate_methods); + EXPECT_EQ(unresolved_candidate_methods.size(), 1); + + Cpp::TCppScope_t tuple_tuple = Cpp::BestOverloadFunctionMatch( + unresolved_candidate_methods, {}, + {Cpp::GetVariableType(Cpp::GetNamed("tuple_one")), + Cpp::GetVariableType(Cpp::GetNamed("tuple_two"))}); + EXPECT_TRUE(tuple_tuple); + + auto tuple_tuple_callable = Cpp::MakeFunctionCallable(tuple_tuple); + EXPECT_EQ(tuple_tuple_callable.getKind(), Cpp::JitCall::kGenericCall); + + Interp->process(R"( + namespace EnumFunctionSameName { + enum foo { FOO = 42 }; + void foo() {} + unsigned int bar(enum foo f) { return (unsigned int)f; } + } + )"); + + Cpp::TCppScope_t bar = + Cpp::GetNamed("bar", Cpp::GetScope("EnumFunctionSameName")); + EXPECT_TRUE(bar); + + auto bar_callable = Cpp::MakeFunctionCallable(bar); + EXPECT_EQ(bar_callable.getKind(), Cpp::JitCall::kGenericCall); } TEST(FunctionReflectionTest, IsConstMethod) { @@ -2082,20 +2122,24 @@ TEST(FunctionReflectionTest, Construct) { GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + std::vector Decls, SubDecls; - Interp->declare(R"( + std::string code = R"( #include extern "C" int printf(const char*,...); class C { + public: int x; C() { x = 12345; printf("Constructor Executed"); } }; - )"); + void construct() { return; } + )"; + GetAllTopLevelDecls(code, Decls, false, interpreter_args); + GetAllSubDecls(Decls[1], SubDecls); testing::internal::CaptureStdout(); Cpp::TCppScope_t scope = Cpp::GetNamed("C"); Cpp::TCppObject_t object = Cpp::Construct(scope); @@ -2115,6 +2159,20 @@ TEST(FunctionReflectionTest, Construct) { EXPECT_EQ(output, "Constructor Executed"); output.clear(); + // Pass a constructor + testing::internal::CaptureStdout(); + where = Cpp::Allocate(scope); + EXPECT_TRUE(where == Cpp::Construct(SubDecls[3], where)); + EXPECT_TRUE(*(int*)where == 12345); + Cpp::Deallocate(scope, where); + output = testing::internal::GetCapturedStdout(); + EXPECT_EQ(output, "Constructor Executed"); + output.clear(); + + // Pass a non-class decl, this should fail + where = Cpp::Allocate(scope); + where = Cpp::Construct(Decls[2], where); + EXPECT_TRUE(where == nullptr); // C API testing::internal::CaptureStdout(); auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); @@ -2460,8 +2518,8 @@ TEST(FunctionReflectionTest, FailingTest1) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif -#ifdef EMSCRIPTEN - GTEST_SKIP() << "Test fails for Emscipten builds"; +#ifdef EMSCRIPTEN_SHARED_LIBRARY + GTEST_SKIP() << "Test fails for Emscipten shared library builds"; #endif Cpp::CreateInterpreter(); EXPECT_FALSE(Cpp::Declare(R"( diff --git a/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp index 841913c54f446..4ef046f92d0b7 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp @@ -99,6 +99,9 @@ TEST(InterpreterTest, DeleteInterpreter) { } TEST(InterpreterTest, ActivateInterpreter) { +#ifdef EMSCRIPTEN_STATIC_LIBRARY + GTEST_SKIP() << "Test fails for Emscipten static library build"; +#endif EXPECT_FALSE(Cpp::ActivateInterpreter(nullptr)); auto* Cpp14 = Cpp::CreateInterpreter({"-std=c++14"}); auto* Cpp17 = Cpp::CreateInterpreter({"-std=c++17"}); @@ -122,6 +125,9 @@ TEST(InterpreterTest, ActivateInterpreter) { } TEST(InterpreterTest, Process) { +#ifdef EMSCRIPTEN_STATIC_LIBRARY + GTEST_SKIP() << "Test fails for Emscipten static library build"; +#endif #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif diff --git a/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp index a62beaa42ca82..4ecdf706e47ad 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp @@ -158,7 +158,8 @@ TEST(ScopeReflectionTest, SizeOf) { EXPECT_EQ(Cpp::SizeOf(Decls[4]), (size_t)0); EXPECT_EQ(Cpp::SizeOf(Decls[5]), (size_t)1); EXPECT_EQ(Cpp::SizeOf(Decls[6]), (size_t)4); - EXPECT_EQ(Cpp::SizeOf(Decls[7]), (size_t)16); + struct B {short a; double b;}; + EXPECT_EQ(Cpp::SizeOf(Decls[7]), sizeof(B)); } diff --git a/interpreter/CppInterOp/unittests/CppInterOp/TypeReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/TypeReflectionTest.cpp index 8a5f7e004f63a..26eb2d6217c26 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/TypeReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/TypeReflectionTest.cpp @@ -78,7 +78,8 @@ TEST(TypeReflectionTest, GetSizeOfType) { EXPECT_EQ(Cpp::GetSizeOfType(Cpp::GetVariableType(Decls[1])), 1); EXPECT_EQ(Cpp::GetSizeOfType(Cpp::GetVariableType(Decls[2])), 4); EXPECT_EQ(Cpp::GetSizeOfType(Cpp::GetVariableType(Decls[3])), 8); - EXPECT_EQ(Cpp::GetSizeOfType(Cpp::GetVariableType(Decls[4])), 16); + struct B {int a; double b;}; + EXPECT_EQ(Cpp::GetSizeOfType(Cpp::GetVariableType(Decls[4])), sizeof(B)); EXPECT_EQ(Cpp::GetSizeOfType(Cpp::GetTypeFromScope(Decls[5])), 0); EXPECT_EQ(Cpp::GetSizeOfType(Cpp::GetVariableType(Decls[6])), sizeof(intptr_t)); diff --git a/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp index b9baf6c55aaa2..f75d1839a032a 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp @@ -299,6 +299,25 @@ TEST(VariableReflectionTest, GetVariableOffset) { auto* VD_C_s_a = Cpp::GetNamed("s_a", Decls[4]); // C::s_a EXPECT_TRUE((bool) Cpp::GetVariableOffset(VD_C_s_a)); + + struct K { + int x; + int y; + int z; + }; + Cpp::Declare("struct K;"); + Cpp::TCppScope_t k = Cpp::GetNamed("K"); + EXPECT_TRUE(k); + + Cpp::Declare("struct K { int x; int y; int z; };"); + + datamembers.clear(); + Cpp::GetDatamembers(k, datamembers); + EXPECT_EQ(datamembers.size(), 3); + + EXPECT_EQ(Cpp::GetVariableOffset(datamembers[0]), offsetof(K, x)); + EXPECT_EQ(Cpp::GetVariableOffset(datamembers[1]), offsetof(K, y)); + EXPECT_EQ(Cpp::GetVariableOffset(datamembers[2]), offsetof(K, z)); } #define CODE \