Skip to content

Commit 622d14f

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

File tree

3 files changed

+129
-9
lines changed

3 files changed

+129
-9
lines changed

include/clang/Interpreter/CppInterOp.h

Lines changed: 2 additions & 1 deletion
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

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,20 +1222,22 @@ namespace Cpp {
12221222
return 0;
12231223
}
12241224

1225-
intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D) {
1225+
intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D,
1226+
CXXRecordDecl* BaseCXXRD) {
12261227
if (!D)
12271228
return 0;
12281229

12291230
auto& C = I.getSema().getASTContext();
12301231

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

@@ -1321,9 +1363,11 @@ namespace Cpp {
13211363
return 0;
13221364
}
13231365

1324-
intptr_t GetVariableOffset(TCppScope_t var) {
1366+
intptr_t GetVariableOffset(TCppScope_t var, TCppScope_t parent) {
13251367
auto* D = static_cast<Decl*>(var);
1326-
return GetVariableOffset(getInterp(), D);
1368+
auto* RD =
1369+
llvm::dyn_cast_or_null<CXXRecordDecl>(static_cast<Decl*>(parent));
1370+
return GetVariableOffset(getInterp(), D, RD);
13271371
}
13281372

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

unittests/CppInterOp/VariableReflectionTest.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,81 @@ TEST(VariableReflectionTest, GetVariableOffset) {
258258
EXPECT_TRUE((bool) Cpp::GetVariableOffset(VD_C_s_a));
259259
}
260260

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

0 commit comments

Comments
 (0)