Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticSema.h"
Expand Down Expand Up @@ -1580,6 +1581,50 @@
return INTEROP_RETURN(nullptr);
}

bool IsAllocator(ConstFuncRef Fn) {
INTEROP_TRACE(Fn);
if (!Fn)
return INTEROP_RETURN(false);

Check warning on line 1587 in lib/CppInterOp/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/CppInterOp.cpp#L1587

Added line #L1587 was not covered by tests
const auto* D = unwrap<clang::Decl>(Fn);
if (const auto* FD = dyn_cast<FunctionDecl>(D)) {
if (FD->getBuiltinID() == Builtin::ID::BImalloc)
return INTEROP_RETURN(true);
if (const auto* FDA = FD->getAttr<RestrictAttr>()) {
if (FDA->getSemanticSpelling() != RestrictAttr::Declspec_restrict)
return INTEROP_RETURN(true);
}

if (const auto* FDA = FD->getAttr<OwnershipAttr>()) {
if (FDA->getOwnKind() == OwnershipAttr::Returns)
return INTEROP_RETURN(true);
}

if (FD->hasAttr<CFReturnsRetainedAttr>() ||
FD->hasAttr<NSReturnsRetainedAttr>() ||
FD->hasAttr<OSReturnsRetainedAttr>())
return INTEROP_RETURN(true);
}

return INTEROP_RETURN(false);
}

bool IsDeallocator(ConstFuncRef Fn) {
INTEROP_TRACE(Fn);
if (!Fn)
INTEROP_RETURN(false);

Check warning on line 1614 in lib/CppInterOp/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/CppInterOp.cpp#L1614

Added line #L1614 was not covered by tests
const auto* D = unwrap<clang::Decl>(Fn);
if (const auto* FD = dyn_cast<FunctionDecl>(D)) {
if (FD->getBuiltinID() == Builtin::ID::BIfree)
return INTEROP_RETURN(true);
if (const auto* FDA = FD->getAttr<OwnershipAttr>()) {
if (FDA->getOwnKind() == OwnershipAttr::Takes)
return INTEROP_RETURN(true);
}
}

return INTEROP_RETURN(false);
}

bool IsFunctionProtoType(ConstTypeRef TyRef) {
INTEROP_TRACE(TyRef);
QualType QT = QualType::getFromOpaquePtr(TyRef.data);
Expand Down
16 changes: 16 additions & 0 deletions lib/CppInterOp/CppInterOp.td
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,22 @@ def GetFunctionReturnType : CppInterOpAPI {
let Args = [Arg<"ConstFuncRef", "func">];
}

def IsAllocator : CppInterOpAPI {
let Doc = "Checks if the provided function is an allocator function";
let ReturnType = "bool";
let Args = [
Arg<"ConstFuncRef", "func">
];
}

def IsDeallocator : CppInterOpAPI {
let Doc = "Checks if the provided function is a deallocator function";
let ReturnType = "bool";
let Args = [
Arg<"ConstFuncRef", "func">
];
}

def GetFunctionSignature : CppInterOpAPI {
let Doc = [{\\returns a stringified version of a given function signature in the
form: void N::f(int i, double d, long l = 0, char ch = 'a').}];
Expand Down
6 changes: 6 additions & 0 deletions unittests/CppInterOp/APINotes/TestHeader.apinotes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Name: TestHeader
Functions:
- Name: testAlloc
RetainCountConvention: CFReturnsRetained
- Name: testNotAlloc
RetainCountConvention: CFReturnsNotRetained
7 changes: 7 additions & 0 deletions unittests/CppInterOp/APINotes/TestHeader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef UNITTESTS_CPPINTEROP_APINOTES_TESTHEADER_H
#define UNITTESTS_CPPINTEROP_APINOTES_TESTHEADER_H

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: header guard does not follow preferred style [llvm-header-guard]

Suggested change
#define UNITTESTS_CPPINTEROP_APINOTES_TESTHEADER_H
#ifndef GITHUB_WORKSPACE_UNITTESTS_CPPINTEROP_APINOTES_TESTHEADER_H
#define GITHUB_WORKSPACE_UNITTESTS_CPPINTEROP_APINOTES_TESTHEADER_H


void* testAlloc(int value);
Comment thread
keremsahn marked this conversation as resolved.
void testNotAlloc(void* ptr);

#endif
1 change: 1 addition & 0 deletions unittests/CppInterOp/APINotes/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module TestHeader {header "TestHeader.h"}
3 changes: 3 additions & 0 deletions unittests/CppInterOp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ if(TARGET CppInterOpTableGen)
add_dependencies(CAPITestC CppInterOpTableGen)
endif()
target_link_libraries(CppInterOpTests PRIVATE CAPITestC)
target_compile_definitions(CppInterOpTests PRIVATE
"CPPINTEROP_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../\""
)

set_source_files_properties(InterpreterTest.cpp PROPERTIES COMPILE_DEFINITIONS
"LLVM_BINARY_DIR=\"${LLVM_BINARY_DIR}\""
Expand Down
80 changes: 80 additions & 0 deletions unittests/CppInterOp/FunctionReflectionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,86 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_GetFunctionReturnType) {
"double");
}

TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsAllocator) {
std::vector<Decl*> Decls;
std::string code = R"(
class Klass{
int val;
};
__attribute__((ownership_returns(malloc)))
Klass* Allocator(){
Klass* obj = new Klass;
return obj;
}
Klass* __attribute__((malloc)) Allocator2(){
Klass* obj = new Klass;
return obj;
}
void foo();
)";
GetAllTopLevelDecls(code, Decls, true);
EXPECT_TRUE(Cpp::IsAllocator(Decls[1]));
EXPECT_TRUE(Cpp::IsAllocator(Decls[2]));
EXPECT_FALSE(Cpp::IsAllocator(Decls[3]));
// Builtin check
code = R"(
//There is nothing lstdlib.h is included at args
)";
TestFixture::CreateInterpreter({"-include", "stdlib.h"});
Interp->process(code);
auto mallocDecl = Cpp::GetNamed("malloc");
EXPECT_TRUE(Cpp::IsAllocator(Cpp::ConstFuncRef{mallocDecl.data}));
Cpp::DeleteInterpreter();

// APINotes check
#if !defined(CPPINTEROP_USE_CLING) && !defined(__EMSCRIPTEN__)
std::string include_flag =
"-I" + std::string(CPPINTEROP_DIR) + "unittests/CppInterOp/APINotes";
std::vector<const char*> interpreter_args = {
"-fmodules", "-fimplicit-module-maps", "-fapinotes-modules",
include_flag.c_str()};
TestFixture::CreateInterpreter(interpreter_args);
code = R"(
#include "TestHeader.h"
)";
Interp->process(code);
auto testAllocDecl = Cpp::GetNamed("testAlloc");
EXPECT_TRUE(Cpp::IsAllocator(Cpp::ConstFuncRef{testAllocDecl.data}));

auto testNotAllocDecl = Cpp::GetNamed("testNotAlloc");
EXPECT_FALSE(Cpp::IsAllocator(Cpp::ConstFuncRef{testNotAllocDecl.data}));
Cpp::DeleteInterpreter();
#endif
}

TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsDeallocator) {
std::vector<Decl*> Decls;
std::string code = R"(
class Klass{
int val;
};
__attribute__((ownership_takes(malloc, 1)))
void Deallocator(Klass* arg){
delete arg;
}
void foo();
)";
GetAllTopLevelDecls(code, Decls, true);
EXPECT_TRUE(Cpp::IsDeallocator(Decls[1]));
EXPECT_FALSE(Cpp::IsDeallocator(Decls[2]));

code = R"(
#include <stdlib.h>
void test(){
//Do Nothing
}
)";
TestFixture::CreateInterpreter();
Interp->process(code);
auto freeDecl = Cpp::GetNamed("free");
EXPECT_TRUE(Cpp::IsDeallocator(Cpp::ConstFuncRef{freeDecl.data}));
}

TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_GetFunctionNumArgs) {
std::vector<Decl*> Decls, TemplateSubDecls;
std::string code = R"(
Expand Down
Loading