blob: 4a2f50920649a0e2ae383774606590ed6b0c8ff0 [file] [edit]
//===- CCallbackTest.cpp --------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Tests for orc-rt's CCallback.h APIs.
//
//===----------------------------------------------------------------------===//
#include "orc-rt/CCallback.h"
#include "gtest/gtest.h"
using namespace orc_rt;
namespace {
// Increments an int through a pointer member. Because the pointer is const in
// a const method but its pointee is not, even the const methods can mutate
// observable state -- this lets the const-callback tests confirm the wrapped
// method actually ran, and also exercises RetT / ArgTs plumbing.
class Incrementer {
public:
Incrementer(int *P) : P(P) {}
void inc() { ++*P; }
void incNoexcept() noexcept { ++*P; }
void incConst() const { ++*P; }
void incConstNoexcept() const noexcept { ++*P; }
// Non-void return and by-value argument.
int advance(int Y) {
*P += Y;
return *P;
}
private:
int *P;
};
// A non-empty base placed *before* Incrementer so that the Incrementer
// subobject lands at a non-zero offset inside DerivedIncrementer. This is what
// makes context-pointer adjustment observable: handing the callback a raw
// pointer to the derived object (rather than the adjusted subobject pointer)
// would compute the wrong `this`.
class NonEmptyBase {
[[maybe_unused]] void *Pad = nullptr;
};
class DerivedIncrementer : public NonEmptyBase, public Incrementer {
public:
DerivedIncrementer(int *P) : Incrementer(P) {}
};
} // namespace
// C-style trampolines that only know the callback as a plain function pointer,
// mirroring how a C API would store and invoke it.
static void invokeVoidVoid(void (*Callback)(void *Ctx), void *Ctx) {
Callback(Ctx);
}
static void invokeVoidVoidConst(void (*Callback)(const void *Ctx),
const void *Ctx) {
Callback(Ctx);
}
TEST(CCallbackTest, NonConstVoidMethod) {
int X = 0;
Incrementer I(&X);
invokeVoidVoid(asCCallback<&Incrementer::inc>,
asCCallbackContext<&Incrementer::inc>(I));
EXPECT_EQ(X, 1);
}
TEST(CCallbackTest, NoexceptVoidMethod) {
int X = 0;
Incrementer I(&X);
// A noexcept source method must match a specialization and yield a noexcept
// function pointer; the explicit type here asserts both.
void (*Callback)(void *) noexcept = asCCallback<&Incrementer::incNoexcept>;
invokeVoidVoid(Callback, asCCallbackContext<&Incrementer::incNoexcept>(I));
EXPECT_EQ(X, 1);
}
TEST(CCallbackTest, ConstVoidMethod) {
int X = 0;
const Incrementer I(&X);
invokeVoidVoidConst(asCCallback<&Incrementer::incConst>,
asCCallbackContext<&Incrementer::incConst>(I));
EXPECT_EQ(X, 1);
}
TEST(CCallbackTest, ConstNoexceptVoidMethod) {
int X = 0;
const Incrementer I(&X);
void (*Callback)(const void *) noexcept =
asCCallback<&Incrementer::incConstNoexcept>;
invokeVoidVoidConst(Callback,
asCCallbackContext<&Incrementer::incConstNoexcept>(I));
EXPECT_EQ(X, 1);
}
TEST(CCallbackTest, ForwardsArgumentAndReturnValue) {
int X = 10;
Incrementer I(&X);
int (*Callback)(void *, int) = asCCallback<&Incrementer::advance>;
EXPECT_EQ(Callback(asCCallbackContext<&Incrementer::advance>(I), 5), 15);
EXPECT_EQ(X, 15);
}
TEST(CCallbackTest, NonConstContextWithBaseOffset) {
int X = 0;
DerivedIncrementer DI(&X);
invokeVoidVoid(asCCallback<&Incrementer::inc>,
asCCallbackContext<&Incrementer::inc>(DI));
EXPECT_EQ(X, 1);
}
TEST(CCallbackTest, ConstContextWithBaseOffset) {
int X = 0;
const DerivedIncrementer DI(&X);
invokeVoidVoidConst(asCCallback<&Incrementer::incConst>,
asCCallbackContext<&Incrementer::incConst>(DI));
EXPECT_EQ(X, 1);
}