|
41 | 41 | using type_graph::Class;
|
42 | 42 | using type_graph::Container;
|
43 | 43 | using type_graph::Enum;
|
| 44 | +using type_graph::Member; |
44 | 45 | using type_graph::Type;
|
45 | 46 | using type_graph::Typedef;
|
46 | 47 | using type_graph::TypeGraph;
|
@@ -165,6 +166,81 @@ void genDecls(const TypeGraph& typeGraph, std::string& code) {
|
165 | 166 | }
|
166 | 167 | }
|
167 | 168 |
|
| 169 | +/* |
| 170 | + * Generates a declaration for a given fully-qualified type. |
| 171 | + * |
| 172 | + * e.g. Given "nsA::nsB::Foo" |
| 173 | + * |
| 174 | + * The folowing is generated: |
| 175 | + * namespace nsA::nsB { |
| 176 | + * struct Foo; |
| 177 | + * } // namespace nsA::nsB |
| 178 | + */ |
| 179 | +void declareFullyQualifiedStruct(const std::string& name, std::string& code) { |
| 180 | + if (auto pos = name.rfind("::"); pos != name.npos) { |
| 181 | + auto ns = name.substr(0, pos); |
| 182 | + auto structName = name.substr(pos + 2); |
| 183 | + code += "namespace "; |
| 184 | + code += ns; |
| 185 | + code += " {\n"; |
| 186 | + code += "struct " + structName + ";\n"; |
| 187 | + code += "} // namespace "; |
| 188 | + code += ns; |
| 189 | + code += "\n"; |
| 190 | + } else { |
| 191 | + code += "struct "; |
| 192 | + code += name; |
| 193 | + code += ";\n"; |
| 194 | + } |
| 195 | +} |
| 196 | + |
| 197 | +void genDefsThriftClass(const Class& c, std::string& code) { |
| 198 | + declareFullyQualifiedStruct(c.fqName(), code); |
| 199 | + code += "namespace apache { namespace thrift {\n"; |
| 200 | + code += "template <> struct TStructDataStorage<" + c.fqName() + "> {\n"; |
| 201 | + code += |
| 202 | + " static constexpr const std::size_t fields_size = 1; // Invalid, do " |
| 203 | + "not use\n"; |
| 204 | + code += |
| 205 | + " static const std::array<folly::StringPiece, fields_size> " |
| 206 | + "fields_names;\n"; |
| 207 | + code += " static const std::array<int16_t, fields_size> fields_ids;\n"; |
| 208 | + code += |
| 209 | + " static const std::array<protocol::TType, fields_size> fields_types;\n"; |
| 210 | + code += "\n"; |
| 211 | + code += |
| 212 | + " static const std::array<folly::StringPiece, fields_size> " |
| 213 | + "storage_names;\n"; |
| 214 | + code += |
| 215 | + " static const std::array<int, fields_size> __attribute__((weak)) " |
| 216 | + "isset_indexes;\n"; |
| 217 | + code += "};\n"; |
| 218 | + code += "}} // namespace thrift, namespace apache\n"; |
| 219 | +} |
| 220 | + |
| 221 | +} // namespace |
| 222 | + |
| 223 | +void CodeGen::genDefsThrift(const TypeGraph& typeGraph, std::string& code) { |
| 224 | + for (const Type& t : typeGraph.finalTypes) { |
| 225 | + if (const auto* c = dynamic_cast<const Class*>(&t)) { |
| 226 | + const Member* issetMember = nullptr; |
| 227 | + for (const auto& member : c->members) { |
| 228 | + if (const auto* container = dynamic_cast<const Container*>(member.type); |
| 229 | + container && container->containerInfo_.ctype == THRIFT_ISSET_TYPE) { |
| 230 | + issetMember = &member; |
| 231 | + break; |
| 232 | + } |
| 233 | + } |
| 234 | + if (issetMember) { |
| 235 | + genDefsThriftClass(*c, code); |
| 236 | + thriftIssetMembers_[c] = issetMember; |
| 237 | + } |
| 238 | + } |
| 239 | + } |
| 240 | +} |
| 241 | + |
| 242 | +namespace { |
| 243 | + |
168 | 244 | void genDefsClass(const Class& c, std::string& code) {
|
169 | 245 | if (c.kind() == Class::Kind::Union)
|
170 | 246 | code += "union ";
|
@@ -293,95 +369,129 @@ void addStandardGetSizeFuncDefs(std::string& code) {
|
293 | 369 | void getClassSizeFuncDecl(const Class& c, std::string& code) {
|
294 | 370 | code += "void getSizeType(const " + c.name() + " &t, size_t &returnArg);\n";
|
295 | 371 | }
|
| 372 | +} // namespace |
296 | 373 |
|
297 |
| -void getClassSizeFuncDef(const Class& c, |
298 |
| - SymbolService& symbols, |
299 |
| - bool polymorphicInheritance, |
300 |
| - std::string& code) { |
301 |
| - bool enablePolymorphicInheritance = polymorphicInheritance && c.isDynamic(); |
| 374 | +/* |
| 375 | + * Generates a getSizeType function for the given concrete class. |
| 376 | + * |
| 377 | + * Does not worry about polymorphism. |
| 378 | + */ |
| 379 | +void CodeGen::getClassSizeFuncConcrete(std::string_view funcName, |
| 380 | + const Class& c, |
| 381 | + std::string& code) const { |
| 382 | + code += "void " + std::string{funcName} + "(const " + c.name() + |
| 383 | + " &t, size_t &returnArg) {\n"; |
302 | 384 |
|
303 |
| - std::string funcName = "getSizeType"; |
304 |
| - if (enablePolymorphicInheritance) { |
305 |
| - funcName = "getSizeTypeConcrete"; |
| 385 | + const Member* thriftIssetMember = nullptr; |
| 386 | + if (const auto it = thriftIssetMembers_.find(&c); |
| 387 | + it != thriftIssetMembers_.end()) { |
| 388 | + thriftIssetMember = it->second; |
306 | 389 | }
|
307 | 390 |
|
308 |
| - code += |
309 |
| - "void " + funcName + "(const " + c.name() + " &t, size_t &returnArg) {\n"; |
310 |
| - for (const auto& member : c.members) { |
| 391 | + if (thriftIssetMember) { |
| 392 | + code += " using thrift_data = apache::thrift::TStructDataStorage<" + |
| 393 | + c.fqName() + ">;\n"; |
| 394 | + } |
| 395 | + |
| 396 | + for (size_t i = 0; i < c.members.size(); i++) { |
| 397 | + const auto& member = c.members[i]; |
311 | 398 | if (member.name.starts_with(type_graph::AddPadding::MemberPrefix))
|
312 | 399 | continue;
|
| 400 | + |
| 401 | + if (thriftIssetMember && thriftIssetMember != &member) { |
| 402 | + // Capture Thrift's isset value for each field, except for __isset |
| 403 | + // itself |
| 404 | + std::string issetIdxStr = |
| 405 | + "thrift_data::isset_indexes[" + std::to_string(i) + "]"; |
| 406 | + code += " if (&thrift_data::isset_indexes != nullptr && " + issetIdxStr + |
| 407 | + " != -1) {\n"; |
| 408 | + code += " SAVE_DATA(t." + thriftIssetMember->name + ".get(" + |
| 409 | + issetIdxStr + "));\n"; |
| 410 | + code += " } else {\n"; |
| 411 | + code += " SAVE_DATA(-1);\n"; |
| 412 | + code += " }\n"; |
| 413 | + } |
| 414 | + |
313 | 415 | code += " JLOG(\"" + member.name + " @\");\n";
|
314 | 416 | code += " JLOGPTR(&t." + member.name + ");\n";
|
315 | 417 | code += " getSizeType(t." + member.name + ", returnArg);\n";
|
316 | 418 | }
|
317 | 419 | code += "}\n";
|
| 420 | +} |
318 | 421 |
|
319 |
| - if (enablePolymorphicInheritance) { |
320 |
| - std::vector<SymbolInfo> childVtableAddrs; |
321 |
| - childVtableAddrs.reserve(c.children.size()); |
| 422 | +void CodeGen::getClassSizeFuncDef(const Class& c, std::string& code) { |
| 423 | + if (!config_.features[Feature::PolymorphicInheritance] || !c.isDynamic()) { |
| 424 | + // Just directly use the concrete size function as this class' getSizeType() |
| 425 | + getClassSizeFuncConcrete("getSizeType", c, code); |
| 426 | + return; |
| 427 | + } |
322 | 428 |
|
323 |
| - for (const Type& childType : c.children) { |
324 |
| - auto* childClass = dynamic_cast<const Class*>(&childType); |
325 |
| - if (childClass == nullptr) { |
326 |
| - abort(); // TODO |
327 |
| - } |
328 |
| - // TODO: |
329 |
| - // auto fqChildName = *fullyQualifiedName(child); |
330 |
| - auto fqChildName = "TODO - implement me"; |
331 |
| - |
332 |
| - // We must split this assignment and append because the C++ standard lacks |
333 |
| - // an operator for concatenating std::string and std::string_view... |
334 |
| - std::string childVtableName = "vtable for "; |
335 |
| - childVtableName += fqChildName; |
336 |
| - |
337 |
| - auto optVtableSym = symbols.locateSymbol(childVtableName, true); |
338 |
| - if (!optVtableSym) { |
339 |
| - // LOG(ERROR) << "Failed to find vtable address for '" << |
340 |
| - // childVtableName; LOG(ERROR) << "Falling back to non dynamic |
341 |
| - // mode"; |
342 |
| - childVtableAddrs.clear(); // TODO why?? |
343 |
| - break; |
344 |
| - } |
345 |
| - childVtableAddrs.push_back(*optVtableSym); |
346 |
| - } |
| 429 | + getClassSizeFuncConcrete("getSizeTypeConcrete", c, code); |
347 | 430 |
|
348 |
| - code += |
349 |
| - "void getSizeType(const " + c.name() + " &t, size_t &returnArg) {\n"; |
350 |
| - code += " auto *vptr = *reinterpret_cast<uintptr_t * const *>(&t);\n"; |
351 |
| - code += " uintptr_t topOffset = *(vptr - 2);\n"; |
352 |
| - code += " uintptr_t vptrVal = reinterpret_cast<uintptr_t>(vptr);\n"; |
353 |
| - |
354 |
| - for (size_t i = 0; i < c.children.size(); i++) { |
355 |
| - // The vptr will point to *somewhere* in the vtable of this object's |
356 |
| - // concrete class. The exact offset into the vtable can vary based on a |
357 |
| - // number of factors, so we compare the vptr against the vtable range for |
358 |
| - // each possible class to determine the concrete type. |
359 |
| - // |
360 |
| - // This works for C++ compilers which follow the GNU v3 ABI, i.e. GCC and |
361 |
| - // Clang. Other compilers may differ. |
362 |
| - const Type& child = c.children[i]; |
363 |
| - auto& vtableSym = childVtableAddrs[i]; |
364 |
| - uintptr_t vtableMinAddr = vtableSym.addr; |
365 |
| - uintptr_t vtableMaxAddr = vtableSym.addr + vtableSym.size; |
366 |
| - code += " if (vptrVal >= 0x" + |
367 |
| - (boost::format("%x") % vtableMinAddr).str() + " && vptrVal < 0x" + |
368 |
| - (boost::format("%x") % vtableMaxAddr).str() + ") {\n"; |
369 |
| - code += " SAVE_DATA(" + std::to_string(i) + ");\n"; |
370 |
| - code += |
371 |
| - " uintptr_t baseAddress = reinterpret_cast<uintptr_t>(&t) + " |
372 |
| - "topOffset;\n"; |
373 |
| - code += " getSizeTypeConcrete(*reinterpret_cast<const " + |
374 |
| - child.name() + "*>(baseAddress), returnArg);\n"; |
375 |
| - code += " return;\n"; |
376 |
| - code += " }\n"; |
377 |
| - } |
| 431 | + std::vector<SymbolInfo> childVtableAddrs; |
| 432 | + childVtableAddrs.reserve(c.children.size()); |
378 | 433 |
|
379 |
| - code += " SAVE_DATA(-1);\n"; |
380 |
| - code += " getSizeTypeConcrete(t, returnArg);\n"; |
381 |
| - code += "}\n"; |
| 434 | + for (const Type& childType : c.children) { |
| 435 | + auto* childClass = dynamic_cast<const Class*>(&childType); |
| 436 | + if (childClass == nullptr) { |
| 437 | + abort(); // TODO |
| 438 | + } |
| 439 | + // TODO: |
| 440 | + // auto fqChildName = *fullyQualifiedName(child); |
| 441 | + auto fqChildName = "TODO - implement me"; |
| 442 | + |
| 443 | + // We must split this assignment and append because the C++ standard lacks |
| 444 | + // an operator for concatenating std::string and std::string_view... |
| 445 | + std::string childVtableName = "vtable for "; |
| 446 | + childVtableName += fqChildName; |
| 447 | + |
| 448 | + auto optVtableSym = symbols_.locateSymbol(childVtableName, true); |
| 449 | + if (!optVtableSym) { |
| 450 | + // LOG(ERROR) << "Failed to find vtable address for '" << |
| 451 | + // childVtableName; LOG(ERROR) << "Falling back to non dynamic |
| 452 | + // mode"; |
| 453 | + childVtableAddrs.clear(); // TODO why?? |
| 454 | + break; |
| 455 | + } |
| 456 | + childVtableAddrs.push_back(*optVtableSym); |
| 457 | + } |
| 458 | + |
| 459 | + code += "void getSizeType(const " + c.name() + " &t, size_t &returnArg) {\n"; |
| 460 | + code += " auto *vptr = *reinterpret_cast<uintptr_t * const *>(&t);\n"; |
| 461 | + code += " uintptr_t topOffset = *(vptr - 2);\n"; |
| 462 | + code += " uintptr_t vptrVal = reinterpret_cast<uintptr_t>(vptr);\n"; |
| 463 | + |
| 464 | + for (size_t i = 0; i < c.children.size(); i++) { |
| 465 | + // The vptr will point to *somewhere* in the vtable of this object's |
| 466 | + // concrete class. The exact offset into the vtable can vary based on a |
| 467 | + // number of factors, so we compare the vptr against the vtable range for |
| 468 | + // each possible class to determine the concrete type. |
| 469 | + // |
| 470 | + // This works for C++ compilers which follow the GNU v3 ABI, i.e. GCC and |
| 471 | + // Clang. Other compilers may differ. |
| 472 | + const Type& child = c.children[i]; |
| 473 | + auto& vtableSym = childVtableAddrs[i]; |
| 474 | + uintptr_t vtableMinAddr = vtableSym.addr; |
| 475 | + uintptr_t vtableMaxAddr = vtableSym.addr + vtableSym.size; |
| 476 | + code += " if (vptrVal >= 0x" + |
| 477 | + (boost::format("%x") % vtableMinAddr).str() + " && vptrVal < 0x" + |
| 478 | + (boost::format("%x") % vtableMaxAddr).str() + ") {\n"; |
| 479 | + code += " SAVE_DATA(" + std::to_string(i) + ");\n"; |
| 480 | + code += |
| 481 | + " uintptr_t baseAddress = reinterpret_cast<uintptr_t>(&t) + " |
| 482 | + "topOffset;\n"; |
| 483 | + code += " getSizeTypeConcrete(*reinterpret_cast<const " + child.name() + |
| 484 | + "*>(baseAddress), returnArg);\n"; |
| 485 | + code += " return;\n"; |
| 486 | + code += " }\n"; |
382 | 487 | }
|
| 488 | + |
| 489 | + code += " SAVE_DATA(-1);\n"; |
| 490 | + code += " getSizeTypeConcrete(t, returnArg);\n"; |
| 491 | + code += "}\n"; |
383 | 492 | }
|
384 | 493 |
|
| 494 | +namespace { |
385 | 495 | void getContainerSizeFuncDecl(const Container& c, std::string& code) {
|
386 | 496 | auto fmt =
|
387 | 497 | boost::format(c.containerInfo_.codegen.decl) % c.containerInfo_.typeName;
|
@@ -410,21 +520,21 @@ void addGetSizeFuncDecls(const TypeGraph& typeGraph, std::string& code) {
|
410 | 520 | }
|
411 | 521 | }
|
412 | 522 |
|
413 |
| -void addGetSizeFuncDefs( |
414 |
| - const TypeGraph& typeGraph, |
415 |
| - SymbolService& symbols, |
416 |
| - std::unordered_set<const ContainerInfo*>& definedContainers, |
417 |
| - bool polymorphicInheritance, |
418 |
| - std::string& code) { |
| 523 | +} // namespace |
| 524 | + |
| 525 | +void CodeGen::addGetSizeFuncDefs(const TypeGraph& typeGraph, |
| 526 | + std::string& code) { |
419 | 527 | for (const Type& t : typeGraph.finalTypes) {
|
420 | 528 | if (const auto* c = dynamic_cast<const Class*>(&t)) {
|
421 |
| - getClassSizeFuncDef(*c, symbols, polymorphicInheritance, code); |
| 529 | + getClassSizeFuncDef(*c, code); |
422 | 530 | } else if (const auto* con = dynamic_cast<const Container*>(&t)) {
|
423 |
| - getContainerSizeFuncDef(definedContainers, *con, code); |
| 531 | + getContainerSizeFuncDef(definedContainers_, *con, code); |
424 | 532 | }
|
425 | 533 | }
|
426 | 534 | }
|
427 | 535 |
|
| 536 | +namespace { |
| 537 | + |
428 | 538 | void addStandardTypeHandlers(std::string& code) {
|
429 | 539 | code += R"(
|
430 | 540 | template <typename DB, typename T>
|
@@ -455,6 +565,7 @@ void addStandardTypeHandlers(std::string& code) {
|
455 | 565 | )";
|
456 | 566 | }
|
457 | 567 |
|
| 568 | +// TODO support thrift isset |
458 | 569 | void getClassTypeHandler(const Class& c, std::string& code) {
|
459 | 570 | std::string funcName = "getSizeType";
|
460 | 571 |
|
@@ -646,6 +757,10 @@ void CodeGen::generate(
|
646 | 757 | code += "} // namespace\n} // namespace OIInternal\n";
|
647 | 758 | }
|
648 | 759 |
|
| 760 | + if (config_.features[Feature::CaptureThriftIsset]) { |
| 761 | + genDefsThrift(typeGraph, code); |
| 762 | + } |
| 763 | + |
649 | 764 | /*
|
650 | 765 | * The purpose of the anonymous namespace within `OIInternal` is that
|
651 | 766 | * anything defined within an anonymous namespace has internal-linkage,
|
@@ -676,8 +791,7 @@ void CodeGen::generate(
|
676 | 791 | addGetSizeFuncDecls(typeGraph, code);
|
677 | 792 |
|
678 | 793 | addStandardGetSizeFuncDefs(code);
|
679 |
| - addGetSizeFuncDefs(typeGraph, symbols_, definedContainers_, |
680 |
| - config_.features[Feature::PolymorphicInheritance], code); |
| 794 | + addGetSizeFuncDefs(typeGraph, code); |
681 | 795 | }
|
682 | 796 |
|
683 | 797 | assert(typeGraph.rootTypes().size() == 1);
|
|
0 commit comments