Skip to content

Commit 0e30059

Browse files
fix GetVariableOffset in cases of multi-level and multiple inheritance
1 parent ef2f0a1 commit 0e30059

File tree

3 files changed

+139
-9
lines changed

3 files changed

+139
-9
lines changed

include/clang/Interpreter/CppInterOp.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,8 @@ namespace Cpp {
446446

447447
/// Gets the address of the variable, you can use it to get the
448448
/// value stored in the variable.
449-
CPPINTEROP_API intptr_t GetVariableOffset(TCppScope_t var);
449+
CPPINTEROP_API intptr_t GetVariableOffset(TCppScope_t var,
450+
TCppScope_t parent = nullptr);
450451

451452
/// Checks if the provided variable is a 'Public' variable.
452453
CPPINTEROP_API bool IsPublicVariable(TCppScope_t var);

lib/Interpreter/CppInterOp.cpp

+53-8
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "llvm/Support/Debug.h"
3636
#include "llvm/Support/raw_os_ostream.h"
3737

38+
#include <map>
3839
#include <set>
3940
#include <sstream>
4041
#include <string>
@@ -1222,20 +1223,22 @@ namespace Cpp {
12221223
return 0;
12231224
}
12241225

1225-
intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D) {
1226+
intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D,
1227+
CXXRecordDecl* BaseCXXRD) {
12261228
if (!D)
12271229
return 0;
12281230

12291231
auto& C = I.getSema().getASTContext();
12301232

12311233
if (auto* FD = llvm::dyn_cast<FieldDecl>(D)) {
1232-
const clang::RecordDecl* RD = FD->getParent();
1234+
clang::RecordDecl* FieldParentRecordDecl = FD->getParent();
12331235
intptr_t offset =
12341236
C.toCharUnitsFromBits(C.getFieldOffset(FD)).getQuantity();
1235-
while (RD->isAnonymousStructOrUnion()) {
1236-
const clang::RecordDecl* anon = RD;
1237-
RD = llvm::dyn_cast<RecordDecl>(anon->getParent());
1238-
for (auto F = RD->field_begin(); F != RD->field_end(); ++F) {
1237+
while (FieldParentRecordDecl->isAnonymousStructOrUnion()) {
1238+
clang::RecordDecl* anon = FieldParentRecordDecl;
1239+
FieldParentRecordDecl = llvm::dyn_cast<RecordDecl>(anon->getParent());
1240+
for (auto F = FieldParentRecordDecl->field_begin();
1241+
F != FieldParentRecordDecl->field_end(); ++F) {
12391242
const auto* RT = F->getType()->getAs<RecordType>();
12401243
if (!RT)
12411244
continue;
@@ -1246,6 +1249,46 @@ namespace Cpp {
12461249
}
12471250
offset += C.toCharUnitsFromBits(C.getFieldOffset(FD)).getQuantity();
12481251
}
1252+
if (BaseCXXRD && BaseCXXRD != FieldParentRecordDecl) {
1253+
// FieldDecl FD belongs to some class C, but the base class BaseCXXRD is
1254+
// not C. That means BaseCXXRD derives from C. Offset needs to be
1255+
// calculated for Derived class
1256+
1257+
// Depth first Search is performed to the class that declears FD from
1258+
// the base class
1259+
std::vector<CXXRecordDecl*> stack;
1260+
std::map<CXXRecordDecl*, CXXRecordDecl*> direction;
1261+
stack.push_back(BaseCXXRD);
1262+
while (!stack.empty()) {
1263+
CXXRecordDecl* RD = stack.back();
1264+
stack.pop_back();
1265+
size_t num_bases = GetNumBases(RD);
1266+
bool flag = false;
1267+
for (size_t i = 0; i < num_bases; i++) {
1268+
auto* CRD = static_cast<CXXRecordDecl*>(GetBaseClass(RD, i));
1269+
direction[CRD] = RD;
1270+
if (CRD == FieldParentRecordDecl) {
1271+
flag = true;
1272+
break;
1273+
}
1274+
stack.push_back(CRD);
1275+
}
1276+
if (flag)
1277+
break;
1278+
}
1279+
if (auto* RD = llvm::dyn_cast<CXXRecordDecl>(FieldParentRecordDecl)) {
1280+
// add in the offsets for the (multi level) base classes
1281+
while (BaseCXXRD != RD) {
1282+
CXXRecordDecl* Parent = direction.at(RD);
1283+
offset += C.getASTRecordLayout(Parent)
1284+
.getBaseClassOffset(RD)
1285+
.getQuantity();
1286+
RD = Parent;
1287+
}
1288+
} else {
1289+
assert(false && "Unreachable");
1290+
}
1291+
}
12491292
return offset;
12501293
}
12511294

@@ -1321,9 +1364,11 @@ namespace Cpp {
13211364
return 0;
13221365
}
13231366

1324-
intptr_t GetVariableOffset(TCppScope_t var) {
1367+
intptr_t GetVariableOffset(TCppScope_t var, TCppScope_t parent) {
13251368
auto* D = static_cast<Decl*>(var);
1326-
return GetVariableOffset(getInterp(), D);
1369+
auto* RD =
1370+
llvm::dyn_cast_or_null<CXXRecordDecl>(static_cast<Decl*>(parent));
1371+
return GetVariableOffset(getInterp(), D, RD);
13271372
}
13281373

13291374
// Check if the Access Specifier of the variable matches the provided value.

unittests/CppInterOp/VariableReflectionTest.cpp

+84
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include "gtest/gtest.h"
99

10+
#include <cstddef>
11+
1012
using namespace TestUtils;
1113
using namespace llvm;
1214
using namespace clang;
@@ -258,6 +260,88 @@ TEST(VariableReflectionTest, GetVariableOffset) {
258260
EXPECT_TRUE((bool) Cpp::GetVariableOffset(VD_C_s_a));
259261
}
260262

263+
#define CODE \
264+
class BaseA { \
265+
public: \
266+
virtual ~BaseA() {} \
267+
int a; \
268+
BaseA(int a) : a(a) {} \
269+
}; \
270+
\
271+
class BaseB : public BaseA { \
272+
public: \
273+
virtual ~BaseB() {} \
274+
std::string b; \
275+
BaseB(int x, std::string b) : BaseA(x), b(b) {} \
276+
}; \
277+
\
278+
class Base1 { \
279+
public: \
280+
virtual ~Base1() {} \
281+
int i; \
282+
std::string s; \
283+
Base1(int i, std::string s) : i(i), s(s) {} \
284+
}; \
285+
\
286+
class MyKlass : public BaseB, public Base1 { \
287+
public: \
288+
virtual ~MyKlass() {} \
289+
int k; \
290+
MyKlass(int k, int i, int x, std::string b, std::string s) \
291+
: BaseB(x, b), Base1(i, s), k(k) {} \
292+
} my_k(5, 4, 3, "Cpp", "Python");
293+
294+
CODE
295+
296+
TEST(VariableReflectionTest, VariableOffsetsWithInheritance) {
297+
if (llvm::sys::RunningOnValgrind())
298+
GTEST_SKIP() << "XFAIL due to Valgrind report";
299+
300+
Cpp::Declare("#include<string>");
301+
302+
#define Stringify(s) Stringifyx(s)
303+
#define Stringifyx(...) #__VA_ARGS__
304+
Cpp::Declare(Stringify(CODE));
305+
#undef Stringifyx
306+
#undef Stringify
307+
#undef CODE
308+
309+
Cpp::TCppScope_t myklass = Cpp::GetNamed("MyKlass");
310+
EXPECT_TRUE(myklass);
311+
312+
size_t num_bases = Cpp::GetNumBases(myklass);
313+
EXPECT_EQ(num_bases, 2);
314+
315+
std::vector<Cpp::TCppScope_t> datamembers;
316+
Cpp::GetDatamembers(myklass, datamembers);
317+
for (size_t i = 0; i < num_bases; i++) {
318+
Cpp::TCppScope_t base = Cpp::GetBaseClass(myklass, i);
319+
EXPECT_TRUE(base);
320+
for (size_t i = 0; i < Cpp::GetNumBases(base); i++) {
321+
Cpp::TCppScope_t bbase = Cpp::GetBaseClass(base, i);
322+
EXPECT_TRUE(base);
323+
Cpp::GetDatamembers(bbase, datamembers);
324+
}
325+
Cpp::GetDatamembers(base, datamembers);
326+
}
327+
EXPECT_EQ(datamembers.size(), 5);
328+
329+
EXPECT_EQ(Cpp::GetVariableOffset(datamembers[0], myklass),
330+
((intptr_t)&(my_k.k)) - ((intptr_t)&(my_k)));
331+
332+
EXPECT_EQ(Cpp::GetVariableOffset(datamembers[1], myklass),
333+
((intptr_t)&(my_k.a)) - ((intptr_t)&(my_k)));
334+
335+
EXPECT_EQ(Cpp::GetVariableOffset(datamembers[2], myklass),
336+
((intptr_t)&(my_k.b)) - ((intptr_t)&(my_k)));
337+
338+
EXPECT_EQ(Cpp::GetVariableOffset(datamembers[3], myklass),
339+
((intptr_t)&(my_k.i)) - ((intptr_t)&(my_k)));
340+
341+
EXPECT_EQ(Cpp::GetVariableOffset(datamembers[4], myklass),
342+
((intptr_t)&(my_k.s)) - ((intptr_t)&(my_k)));
343+
}
344+
261345
TEST(VariableReflectionTest, IsPublicVariable) {
262346
std::vector<Decl *> Decls, SubDecls;
263347
std::string code = R"(

0 commit comments

Comments
 (0)