object-introspection/oi/CodeGen.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1342 lines
41 KiB
C++
Raw Normal View History

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CodeGen.h"
#include <glog/logging.h>
#include <boost/format.hpp>
#include <iostream>
2023-08-16 20:40:36 +01:00
#include <numeric>
2023-04-20 18:30:59 +01:00
#include <set>
#include <string_view>
#include "oi/FuncGen.h"
#include "oi/Headers.h"
#include "oi/SymbolService.h"
#include "type_graph/AddChildren.h"
#include "type_graph/AddPadding.h"
#include "type_graph/AlignmentCalc.h"
#include "type_graph/DrgnExporter.h"
#include "type_graph/DrgnParser.h"
#include "type_graph/EnforceCompatibility.h"
#include "type_graph/Flattener.h"
#include "type_graph/IdentifyContainers.h"
2023-09-22 14:35:33 +01:00
#include "type_graph/KeyCapture.h"
#include "type_graph/NameGen.h"
#include "type_graph/Prune.h"
#include "type_graph/RemoveMembers.h"
#include "type_graph/RemoveTopLevelPointer.h"
#include "type_graph/TopoSorter.h"
#include "type_graph/TypeIdentifier.h"
#include "type_graph/Types.h"
template <typename T>
inline constexpr bool always_false_v = false;
namespace oi::detail {
using type_graph::AddChildren;
using type_graph::AddPadding;
using type_graph::AlignmentCalc;
2023-09-22 14:35:33 +01:00
using type_graph::CaptureKeys;
using type_graph::Class;
using type_graph::Container;
using type_graph::DrgnParser;
using type_graph::DrgnParserOptions;
using type_graph::EnforceCompatibility;
using type_graph::Enum;
using type_graph::Flattener;
using type_graph::IdentifyContainers;
2023-09-22 14:35:33 +01:00
using type_graph::KeyCapture;
using type_graph::Member;
using type_graph::NameGen;
2023-08-16 20:40:36 +01:00
using type_graph::Primitive;
using type_graph::Prune;
using type_graph::RemoveMembers;
using type_graph::RemoveTopLevelPointer;
2023-08-16 20:40:36 +01:00
using type_graph::TemplateParam;
using type_graph::TopoSorter;
using type_graph::Type;
using type_graph::Typedef;
using type_graph::TypeGraph;
using type_graph::TypeIdentifier;
template <typename T>
using ref = std::reference_wrapper<T>;
namespace {
2023-08-16 20:40:36 +01:00
std::vector<std::string_view> enumerateTypeNames(Type& type) {
std::vector<std::string_view> names;
Type* t = &type;
while (const Typedef* td = dynamic_cast<Typedef*>(t)) {
names.emplace_back(t->inputName());
t = &td->underlyingType();
}
names.emplace_back(t->inputName());
return names;
}
void defineMacros(std::string& code) {
if (true /* TODO: config.useDataSegment*/) {
code += R"(
#define SAVE_SIZE(val)
#define SAVE_DATA(val) StoreData(val, returnArg)
2023-04-20 18:30:59 +01:00
)";
} else {
code += R"(
#define SAVE_SIZE(val) AddData(val, returnArg)
#define SAVE_DATA(val)
)";
}
}
2023-09-22 14:35:33 +01:00
void defineInternalTypes(std::string& code) {
2023-04-20 18:30:59 +01:00
code += R"(
template<typename T, int N>
struct OIArray {
T vals[N];
};
2023-09-22 14:35:33 +01:00
// Just here to give a different type name to containers whose keys we'll capture
template <typename T>
struct OICaptureKeys : public T {
};
2023-04-20 18:30:59 +01:00
)";
}
void addIncludes(const TypeGraph& typeGraph,
FeatureSet features,
std::string& code) {
std::set<std::string_view> includes{"cstddef"};
if (features[Feature::TreeBuilderV2]) {
code += "#define DEFINE_DESCRIBE 1\n"; // added before all includes
2023-04-20 18:30:59 +01:00
includes.emplace("functional");
includes.emplace("oi/exporters/inst.h");
includes.emplace("oi/types/dy.h");
includes.emplace("oi/types/st.h");
}
if (features[Feature::Library]) {
includes.emplace("memory");
includes.emplace("oi/IntrospectionResult.h");
includes.emplace("vector");
}
2023-06-22 17:27:21 +01:00
if (features[Feature::JitTiming]) {
includes.emplace("chrono");
}
for (const Type& t : typeGraph.finalTypes) {
if (const auto* c = dynamic_cast<const Container*>(&t)) {
2023-04-20 18:30:59 +01:00
includes.emplace(c->containerInfo_.header);
}
}
2023-04-20 18:30:59 +01:00
for (const auto& include : includes) {
code += "#include <";
code += include;
code += ">\n";
}
}
void genDeclsClass(const Class& c, std::string& code) {
if (c.kind() == Class::Kind::Union)
code += "union ";
else
code += "struct ";
code += c.name() + ";\n";
}
void genDeclsEnum(const Enum& e, std::string& code) {
2023-09-19 17:25:58 +01:00
code += "enum class ";
code += e.name();
code += " : ";
switch (e.size()) {
case 8:
code += "uint64_t";
break;
case 4:
code += "uint32_t";
break;
case 2:
code += "uint16_t";
break;
case 1:
code += "uint8_t";
break;
default:
abort(); // TODO
}
2023-09-19 17:25:58 +01:00
code += " {};\n";
}
void genDecls(const TypeGraph& typeGraph, std::string& code) {
for (const Type& t : typeGraph.finalTypes) {
if (const auto* c = dynamic_cast<const Class*>(&t)) {
genDeclsClass(*c, code);
} else if (const auto* e = dynamic_cast<const Enum*>(&t)) {
genDeclsEnum(*e, code);
}
}
}
namespace {
size_t calculateExclusiveSize(const Type& t) {
if (const auto* c = dynamic_cast<const Class*>(&t)) {
return std::accumulate(
c->members.cbegin(), c->members.cend(), 0, [](size_t a, const auto& m) {
if (m.name.starts_with(AddPadding::MemberPrefix))
return a + m.type().size();
return a;
});
}
return t.size();
}
} // namespace
void genNames(const TypeGraph& typeGraph, std::string& code) {
code += R"(
template <typename T>
struct NameProvider {};
)";
for (const Type& t : typeGraph.finalTypes) {
if (dynamic_cast<const Typedef*>(&t))
continue;
code += "template <> struct NameProvider<";
code += t.name();
code += "> { static constexpr std::array<std::string_view, 1> names = {\"";
code += t.inputName();
code += "\"}; };\n";
}
}
void genExclusiveSizes(const TypeGraph& typeGraph, std::string& code) {
code += R"(
template <typename T>
struct ExclusiveSizeProvider {
static constexpr size_t size = sizeof(T);
};
)";
for (const Type& t : typeGraph.finalTypes) {
size_t exclusiveSize = calculateExclusiveSize(t);
if (exclusiveSize != t.size()) {
code += "template <> struct ExclusiveSizeProvider<";
code += t.name();
code += "> { static constexpr size_t size = ";
code += std::to_string(exclusiveSize);
code += "; };\n";
}
}
}
/*
* Generates a declaration for a given fully-qualified type.
*
* e.g. Given "nsA::nsB::Foo"
*
* The folowing is generated:
* namespace nsA::nsB {
* struct Foo;
* } // namespace nsA::nsB
*/
void declareFullyQualifiedStruct(const std::string& name, std::string& code) {
if (auto pos = name.rfind("::"); pos != name.npos) {
auto ns = name.substr(0, pos);
auto structName = name.substr(pos + 2);
code += "namespace ";
code += ns;
code += " {\n";
code += "struct " + structName + ";\n";
code += "} // namespace ";
code += ns;
code += "\n";
} else {
code += "struct ";
code += name;
code += ";\n";
}
}
void genDefsThriftClass(const Class& c, std::string& code) {
declareFullyQualifiedStruct(c.fqName(), code);
code += "namespace apache { namespace thrift {\n";
code += "template <> struct TStructDataStorage<" + c.fqName() + "> {\n";
code +=
" static constexpr const std::size_t fields_size = 1; // Invalid, do "
"not use\n";
code +=
" static const std::array<folly::StringPiece, fields_size> "
"fields_names;\n";
code += " static const std::array<int16_t, fields_size> fields_ids;\n";
code +=
" static const std::array<protocol::TType, fields_size> fields_types;\n";
code += "\n";
code +=
" static const std::array<folly::StringPiece, fields_size> "
"storage_names;\n";
code +=
" static const std::array<int, fields_size> __attribute__((weak)) "
"isset_indexes;\n";
code += "};\n";
code += "}} // namespace thrift, namespace apache\n";
}
} // namespace
void CodeGen::genDefsThrift(const TypeGraph& typeGraph, std::string& code) {
for (const Type& t : typeGraph.finalTypes) {
if (const auto* c = dynamic_cast<const Class*>(&t)) {
const Member* issetMember = nullptr;
for (const auto& member : c->members) {
if (const auto* container =
dynamic_cast<const Container*>(&member.type());
container && container->containerInfo_.ctype == THRIFT_ISSET_TYPE) {
issetMember = &member;
break;
}
}
if (issetMember) {
genDefsThriftClass(*c, code);
thriftIssetMembers_[c] = issetMember;
}
}
}
}
namespace {
void genDefsClass(const Class& c, std::string& code) {
if (c.kind() == Class::Kind::Union)
code += "union ";
else
code += "struct ";
if (c.packed()) {
code += "__attribute__((__packed__)) ";
}
if (c.members.size() == 1 &&
c.members[0].name.starts_with(AddPadding::MemberPrefix)) {
// Need to specify alignment manually for types which have been stubbed.
// It would be nice to do this for all types, but our alignment information
// is not complete, so it would result in some errors.
//
// Once we are able to read alignment info from DWARF, then this should be
// able to be applied to everything.
code += "alignas(" + std::to_string(c.align()) + ") ";
}
code += c.name() + " {\n";
for (const auto& mem : c.members) {
code += " " + mem.type().name() + " " + mem.name;
if (mem.bitsize) {
code += " : " + std::to_string(mem.bitsize);
}
code += ";\n";
}
code += "};\n\n";
}
void genDefsTypedef(const Typedef& td, std::string& code) {
code += "using " + td.name() + " = " + td.underlyingType().name() + ";\n";
}
void genDefs(const TypeGraph& typeGraph, std::string& code) {
for (const Type& t : typeGraph.finalTypes) {
if (const auto* c = dynamic_cast<const Class*>(&t)) {
genDefsClass(*c, code);
} else if (const auto* td = dynamic_cast<const Typedef*>(&t)) {
genDefsTypedef(*td, code);
}
}
}
void genStaticAssertsClass(const Class& c, std::string& code) {
code += "static_assert(validate_size<" + c.name() + ", " +
std::to_string(c.size()) + ">::value);\n";
for (const auto& member : c.members) {
if (member.bitsize > 0)
continue;
code += "static_assert(validate_offset<offsetof(" + c.name() + ", " +
member.name + "), " + std::to_string(member.bitOffset / 8) +
">::value, \"Unexpected offset of " + c.name() +
"::" + member.name + "\");\n";
}
code.push_back('\n');
}
void genStaticAssertsContainer(const Container& c, std::string& code) {
code += "static_assert(validate_size<" + c.name() + ", " +
std::to_string(c.size()) + ">::value);\n";
code.push_back('\n');
}
void genStaticAsserts(const TypeGraph& typeGraph, std::string& code) {
for (const Type& t : typeGraph.finalTypes) {
if (const auto* c = dynamic_cast<const Class*>(&t)) {
genStaticAssertsClass(*c, code);
} else if (const auto* con = dynamic_cast<const Container*>(&t)) {
genStaticAssertsContainer(*con, code);
}
}
}
void addStandardGetSizeFuncDecls(std::string& code) {
code += R"(
template <typename T>
void getSizeType(const T &t, size_t& returnArg);
template<typename T>
void getSizeType(/*const*/ T* s_ptr, size_t& returnArg);
void getSizeType(/*const*/ void *s_ptr, size_t& returnArg);
template <typename T, int N>
void getSizeType(const OIArray<T,N>& container, size_t& returnArg);
)";
}
void addStandardGetSizeFuncDefs(std::string& code) {
// TODO use macros, not StoreData directly
code += R"(
template <typename T>
void getSizeType(const T &t, size_t& returnArg) {
JLOG("obj @");
JLOGPTR(&t);
SAVE_SIZE(sizeof(T));
}
)";
// TODO const and non-const versions
// OR maybe just remove const everywhere
code += R"(
template<typename T>
void getSizeType(/*const*/ T* s_ptr, size_t& returnArg)
{
JLOG("ptr val @");
JLOGPTR(s_ptr);
StoreData((uintptr_t)(s_ptr), returnArg);
if (s_ptr && ctx.pointers.add((uintptr_t)s_ptr)) {
StoreData(1, returnArg);
getSizeType(*(s_ptr), returnArg);
} else {
StoreData(0, returnArg);
}
}
void getSizeType(/*const*/ void *s_ptr, size_t& returnArg)
{
JLOG("void ptr @");
JLOGPTR(s_ptr);
StoreData((uintptr_t)(s_ptr), returnArg);
}
template <typename T, int N>
void getSizeType(const OIArray<T,N>& container, size_t& returnArg)
{
SAVE_DATA((uintptr_t)N);
SAVE_SIZE(sizeof(container));
for (size_t i=0; i<N; i++) {
// undo the static size that has already been added per-element
SAVE_SIZE(-sizeof(container.vals[i]));
getSizeType(container.vals[i], returnArg);
}
}
)";
}
void getClassSizeFuncDecl(const Class& c, std::string& code) {
code += "void getSizeType(const " + c.name() + " &t, size_t &returnArg);\n";
}
} // namespace
/*
* Generates a getSizeType function for the given concrete class.
*
* Does not worry about polymorphism.
*/
void CodeGen::getClassSizeFuncConcrete(std::string_view funcName,
const Class& c,
std::string& code) const {
code += "void " + std::string{funcName} + "(const " + c.name() +
" &t, size_t &returnArg) {\n";
const Member* thriftIssetMember = nullptr;
if (const auto it = thriftIssetMembers_.find(&c);
it != thriftIssetMembers_.end()) {
thriftIssetMember = it->second;
}
if (thriftIssetMember) {
code += " using thrift_data = apache::thrift::TStructDataStorage<" +
c.fqName() + ">;\n";
}
for (size_t i = 0; i < c.members.size(); i++) {
const auto& member = c.members[i];
if (member.name.starts_with(AddPadding::MemberPrefix))
continue;
if (thriftIssetMember && thriftIssetMember != &member) {
// Capture Thrift's isset value for each field, except for __isset
// itself
std::string issetIdxStr =
"thrift_data::isset_indexes[" + std::to_string(i) + "]";
code += " if (&thrift_data::isset_indexes != nullptr && " + issetIdxStr +
" != -1) {\n";
code += " SAVE_DATA(t." + thriftIssetMember->name + ".get(" +
issetIdxStr + "));\n";
code += " } else {\n";
code += " SAVE_DATA(-1);\n";
code += " }\n";
}
code += " JLOG(\"" + member.name + " @\");\n";
if (member.bitsize == 0)
code += " JLOGPTR(&t." + member.name + ");\n";
code += " getSizeType(t." + member.name + ", returnArg);\n";
}
code += "}\n";
}
void CodeGen::getClassSizeFuncDef(const Class& c, std::string& code) {
if (!config_.features[Feature::PolymorphicInheritance] || !c.isDynamic()) {
// Just directly use the concrete size function as this class' getSizeType()
getClassSizeFuncConcrete("getSizeType", c, code);
return;
}
getClassSizeFuncConcrete("getSizeTypeConcrete", c, code);
std::vector<SymbolInfo> childVtableAddrs;
childVtableAddrs.reserve(c.children.size());
for (const Type& childType : c.children) {
auto* childClass = dynamic_cast<const Class*>(&childType);
if (childClass == nullptr) {
abort(); // TODO
}
// TODO:
// auto fqChildName = *fullyQualifiedName(child);
auto fqChildName = "TODO - implement me";
// We must split this assignment and append because the C++ standard lacks
// an operator for concatenating std::string and std::string_view...
std::string childVtableName = "vtable for ";
childVtableName += fqChildName;
auto optVtableSym = symbols_.locateSymbol(childVtableName, true);
if (!optVtableSym) {
// LOG(ERROR) << "Failed to find vtable address for '" <<
// childVtableName; LOG(ERROR) << "Falling back to non dynamic
// mode";
childVtableAddrs.clear(); // TODO why??
break;
}
childVtableAddrs.push_back(*optVtableSym);
}
code += "void getSizeType(const " + c.name() + " &t, size_t &returnArg) {\n";
code += " auto *vptr = *reinterpret_cast<uintptr_t * const *>(&t);\n";
code += " uintptr_t topOffset = *(vptr - 2);\n";
code += " uintptr_t vptrVal = reinterpret_cast<uintptr_t>(vptr);\n";
for (size_t i = 0; i < c.children.size(); i++) {
// The vptr will point to *somewhere* in the vtable of this object's
// concrete class. The exact offset into the vtable can vary based on a
// number of factors, so we compare the vptr against the vtable range for
// each possible class to determine the concrete type.
//
// This works for C++ compilers which follow the GNU v3 ABI, i.e. GCC and
// Clang. Other compilers may differ.
const Type& child = c.children[i];
auto& vtableSym = childVtableAddrs[i];
uintptr_t vtableMinAddr = vtableSym.addr;
uintptr_t vtableMaxAddr = vtableSym.addr + vtableSym.size;
code += " if (vptrVal >= 0x" +
(boost::format("%x") % vtableMinAddr).str() + " && vptrVal < 0x" +
(boost::format("%x") % vtableMaxAddr).str() + ") {\n";
code += " SAVE_DATA(" + std::to_string(i) + ");\n";
code +=
" uintptr_t baseAddress = reinterpret_cast<uintptr_t>(&t) + "
"topOffset;\n";
code += " getSizeTypeConcrete(*reinterpret_cast<const " + child.name() +
"*>(baseAddress), returnArg);\n";
code += " return;\n";
code += " }\n";
}
code += " SAVE_DATA(-1);\n";
code += " getSizeTypeConcrete(t, returnArg);\n";
code += "}\n";
}
namespace {
void getContainerSizeFuncDecl(const Container& c, std::string& code) {
auto fmt =
boost::format(c.containerInfo_.codegen.decl) % c.containerInfo_.typeName;
code += fmt.str();
}
void getContainerSizeFuncDef(std::unordered_set<const ContainerInfo*>& used,
const Container& c,
std::string& code) {
if (!used.insert(&c.containerInfo_).second) {
return;
}
auto fmt =
boost::format(c.containerInfo_.codegen.func) % c.containerInfo_.typeName;
code += fmt.str();
}
void addGetSizeFuncDecls(const TypeGraph& typeGraph, std::string& code) {
for (const Type& t : typeGraph.finalTypes) {
if (const auto* c = dynamic_cast<const Class*>(&t)) {
getClassSizeFuncDecl(*c, code);
} else if (const auto* con = dynamic_cast<const Container*>(&t)) {
getContainerSizeFuncDecl(*con, code);
}
}
}
} // namespace
void CodeGen::addGetSizeFuncDefs(const TypeGraph& typeGraph,
std::string& code) {
for (const Type& t : typeGraph.finalTypes) {
if (const auto* c = dynamic_cast<const Class*>(&t)) {
getClassSizeFuncDef(*c, code);
} else if (const auto* con = dynamic_cast<const Container*>(&t)) {
getContainerSizeFuncDef(definedContainers_, *con, code);
}
}
}
2023-04-20 18:30:59 +01:00
namespace {
2023-08-10 21:34:06 +01:00
// Find the last member that isn't padding's index. Return -1 if no such member.
size_t getLastNonPaddingMemberIndex(const std::vector<Member>& members) {
for (size_t i = members.size() - 1; i != (size_t)-1; --i) {
const auto& el = members[i];
if (!el.name.starts_with(AddPadding::MemberPrefix))
return i;
}
return -1;
2023-04-20 18:30:59 +01:00
}
2023-07-03 18:51:25 +01:00
} // namespace
2023-08-10 21:34:06 +01:00
// Generate the function body that walks the type. Uses the monadic
// `delegate()` form to handle each field except for the last. The last field
// instead uses `consume()` as we must not accidentally handle the first half
// of a pair as the last field.
2023-08-10 21:34:06 +01:00
void CodeGen::genClassTraversalFunction(const Class& c, std::string& code) {
2023-04-20 18:30:59 +01:00
std::string funcName = "getSizeType";
2023-08-10 21:34:06 +01:00
code += " static types::st::Unit<DB> ";
code += funcName;
code += "(\n Ctx& ctx,\n";
code += " const ";
2023-08-10 21:34:06 +01:00
code += c.name();
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
code += "& t,\n typename TypeHandler<Ctx, ";
2023-08-10 21:34:06 +01:00
code += c.name();
code += ">::type returnArg) {\n";
2023-07-03 18:51:25 +01:00
const Member* thriftIssetMember = nullptr;
if (const auto it = thriftIssetMembers_.find(&c);
it != thriftIssetMembers_.end()) {
thriftIssetMember = it->second;
2023-08-10 21:34:06 +01:00
}
2023-07-03 18:51:25 +01:00
2023-08-10 21:34:06 +01:00
size_t emptySize = code.size();
size_t lastNonPaddingElement = getLastNonPaddingMemberIndex(c.members);
for (size_t i = 0; i < lastNonPaddingElement + 1; i++) {
const auto& member = c.members[i];
if (member.name.starts_with(AddPadding::MemberPrefix)) {
continue;
}
2023-08-10 21:34:06 +01:00
if (code.size() == emptySize) {
code += " return returnArg";
2023-08-10 21:34:06 +01:00
}
2023-07-03 18:51:25 +01:00
2023-08-10 21:34:06 +01:00
if (thriftIssetMember != nullptr && thriftIssetMember != &member) {
code += "\n .write(getThriftIsset(t, " + std::to_string(i) + "))";
}
2023-07-03 18:51:25 +01:00
code += "\n .";
if (i == lastNonPaddingElement) {
code += "consume";
2023-08-10 21:34:06 +01:00
} else {
code += "delegate";
2023-08-10 21:34:06 +01:00
}
code +=
"([&ctx, &t](auto ret) { return OIInternal::getSizeType<Ctx>(ctx, t.";
code += member.name;
code += ", ret); })";
2023-07-03 18:51:25 +01:00
}
2023-08-10 21:34:06 +01:00
if (code.size() == emptySize) {
code += " return returnArg;";
2023-07-03 18:51:25 +01:00
}
code += ";\n }\n";
2023-08-10 21:34:06 +01:00
}
2023-04-20 18:30:59 +01:00
2023-08-10 21:34:06 +01:00
// Generate the static type for the class's representation in the data buffer.
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
// For `class { int a,b,c; }` we generate (Ctx/DB omitted for clarity):
2023-08-10 21:34:06 +01:00
// Pair<TypeHandler<int>::type,
// Pair<TypeHandler<int>::type,
// TypeHandler<int>::type
// >>
void CodeGen::genClassStaticType(const Class& c, std::string& code) {
const Member* thriftIssetMember = nullptr;
if (const auto it = thriftIssetMembers_.find(&c);
it != thriftIssetMembers_.end()) {
thriftIssetMember = it->second;
}
2023-04-20 18:30:59 +01:00
2023-08-10 21:34:06 +01:00
size_t lastNonPaddingElement = getLastNonPaddingMemberIndex(c.members);
size_t pairs = 0;
2023-04-20 18:30:59 +01:00
2023-08-10 21:34:06 +01:00
size_t emptySize = code.size();
for (size_t i = 0; i < lastNonPaddingElement + 1; i++) {
const auto& member = c.members[i];
if (member.name.starts_with(AddPadding::MemberPrefix)) {
continue;
}
2023-07-03 18:51:25 +01:00
2023-08-10 21:34:06 +01:00
if (i != lastNonPaddingElement) {
code += "types::st::Pair<DB, ";
pairs++;
}
2023-04-20 18:30:59 +01:00
2023-08-10 21:34:06 +01:00
if (thriftIssetMember != nullptr && thriftIssetMember != &member) {
// Return an additional VarInt before every field except for __isset
// itself.
pairs++;
if (i == lastNonPaddingElement) {
code += "types::st::Pair<DB, types::st::VarInt<DB>, ";
} else {
code += "types::st::VarInt<DB>, types::st::Pair<DB, ";
2023-04-20 18:30:59 +01:00
}
}
2023-08-10 21:34:06 +01:00
code +=
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
(boost::format("typename TypeHandler<Ctx, decltype(%1%::%2%)>::type") %
2023-08-10 21:34:06 +01:00
c.name() % member.name)
.str();
if (i != lastNonPaddingElement) {
code += ", ";
2023-04-20 18:30:59 +01:00
}
}
2023-08-10 21:34:06 +01:00
code += std::string(pairs, '>');
2023-04-20 18:30:59 +01:00
2023-08-10 21:34:06 +01:00
if (code.size() == emptySize) {
code += "types::st::Unit<DB>";
}
}
2023-04-20 18:30:59 +01:00
2023-08-16 20:40:36 +01:00
void CodeGen::genClassTreeBuilderInstructions(const Class& c,
std::string& code) {
code += " private:\n";
size_t index = 0;
for (const auto& m : c.members) {
++index;
if (m.name.starts_with(AddPadding::MemberPrefix))
continue;
auto names = enumerateTypeNames(m.type());
code += " static constexpr std::array<std::string_view, " +
std::to_string(names.size()) + "> member_" + std::to_string(index) +
"_type_names = {";
for (const auto& name : names) {
code += "\"";
code += name;
code += "\",";
}
code += "};\n";
}
code += " public:\n";
size_t numFields =
std::count_if(c.members.cbegin(), c.members.cend(), [](const auto& m) {
return !m.name.starts_with(AddPadding::MemberPrefix);
});
code += " static constexpr std::array<inst::Field, ";
code += std::to_string(numFields);
code += "> fields{\n";
index = 0;
for (const auto& m : c.members) {
++index;
if (m.name.starts_with(AddPadding::MemberPrefix))
continue;
std::string fullName = c.name() + "::" + m.name;
code += " inst::Field{sizeof(" + fullName + "), " +
std::to_string(calculateExclusiveSize(m.type())) + ",\"" +
m.inputName + "\", member_" + std::to_string(index) +
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
"_type_names, TypeHandler<Ctx, decltype(" + fullName +
")>::fields, TypeHandler<Ctx, decltype(" + fullName +
2023-08-16 20:40:36 +01:00
")>::processors},\n";
}
code += " };\n";
code +=
"static constexpr std::array<exporters::inst::ProcessorInst, 0> "
"processors{};\n";
}
2023-08-10 21:34:06 +01:00
void CodeGen::genClassTypeHandler(const Class& c, std::string& code) {
std::string helpers;
2023-07-17 19:45:21 +01:00
2023-08-10 21:34:06 +01:00
if (const auto it = thriftIssetMembers_.find(&c);
it != thriftIssetMembers_.end()) {
const Member& thriftIssetMember = *it->second;
2023-07-03 18:51:25 +01:00
2023-08-10 21:34:06 +01:00
helpers += (boost::format(R"(
static int getThriftIsset(const %1%& t, size_t i) {
using thrift_data = apache::thrift::TStructDataStorage<%2%>;
2023-04-20 18:30:59 +01:00
2023-08-10 21:34:06 +01:00
if (&thrift_data::isset_indexes == nullptr) return -1;
auto idx = thrift_data::isset_indexes[i];
if (idx == -1) return -1;
2023-04-20 18:30:59 +01:00
2023-08-10 21:34:06 +01:00
return t.%3%.get(idx);
2023-04-20 18:30:59 +01:00
}
2023-08-10 21:34:06 +01:00
)") % c.name() % c.fqName() %
thriftIssetMember.name)
.str();
}
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
code += "template <typename Ctx>\n";
code += "class TypeHandler<Ctx, ";
2023-08-10 21:34:06 +01:00
code += c.name();
code += "> {\n";
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
code += " using DB = typename Ctx::DataBuffer;\n";
2023-08-10 21:34:06 +01:00
code += helpers;
code += " public:\n";
code += " using type = ";
genClassStaticType(c, code);
code += ";\n";
genClassTreeBuilderInstructions(c, code);
2023-08-10 21:34:06 +01:00
genClassTraversalFunction(c, code);
code += "};\n";
2023-04-20 18:30:59 +01:00
}
2023-07-03 18:51:25 +01:00
namespace {
void genContainerTypeHandler(std::unordered_set<const ContainerInfo*>& used,
2023-08-16 20:40:36 +01:00
const ContainerInfo& c,
std::span<const TemplateParam> templateParams,
std::string& code) {
2023-08-16 20:40:36 +01:00
if (!used.insert(&c).second)
return;
2023-09-22 14:35:33 +01:00
code += c.codegen.extra;
// TODO: Move this check into the ContainerInfo parsing once always enabled.
2023-08-16 20:40:36 +01:00
const auto& func = c.codegen.traversalFunc;
const auto& processors = c.codegen.processors;
if (func.empty()) {
LOG(ERROR)
<< "`codegen.traversal_func` must be specified for all containers "
"under \"-ftree-builder-v2\", not specified for \"" +
c.typeName + "\"";
throw std::runtime_error("missing `codegen.traversal_func`");
}
std::string containerWithTypes = c.typeName;
if (!templateParams.empty())
containerWithTypes += '<';
size_t types = 0, values = 0;
for (const auto& p : templateParams) {
if (types > 0 || values > 0)
containerWithTypes += ", ";
if (p.value) {
containerWithTypes += "N" + std::to_string(values++);
} else {
containerWithTypes += "T" + std::to_string(types++);
}
}
if (!templateParams.empty())
containerWithTypes += '>';
2023-09-22 14:35:33 +01:00
if (c.captureKeys) {
containerWithTypes = "OICaptureKeys<" + containerWithTypes + ">";
}
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
code += "template <typename Ctx";
2023-08-16 20:40:36 +01:00
types = 0, values = 0;
for (const auto& p : templateParams) {
if (p.value) {
code += ", ";
code += p.type().name();
code += " N" + std::to_string(values++);
} else {
code += ", typename T" + std::to_string(types++);
}
}
code += ">\n";
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
code += "struct TypeHandler<Ctx, ";
2023-08-16 20:40:36 +01:00
code += containerWithTypes;
code += "> {\n";
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
code += " using DB = typename Ctx::DataBuffer;\n";
2023-08-16 20:40:36 +01:00
2023-09-22 14:35:33 +01:00
if (c.captureKeys) {
code += " static constexpr bool captureKeys = true;\n";
} else {
code += " static constexpr bool captureKeys = false;\n";
}
code += " using container_type = ";
code += containerWithTypes;
code += ";\n";
2023-08-16 20:40:36 +01:00
code += " using type = ";
if (processors.empty()) {
code += "types::st::Unit<DB>";
} else {
for (auto it = processors.cbegin(); it != processors.cend(); ++it) {
if (it != processors.cend() - 1)
code += "types::st::Pair<DB, ";
code += it->type;
if (it != processors.cend() - 1)
code += ", ";
}
code += std::string(processors.size() - 1, '>');
}
code += ";\n";
code += " static types::st::Unit<DB> getSizeType(\n";
code += " Ctx& ctx,\n";
2023-08-16 20:40:36 +01:00
code += " const ";
code += containerWithTypes;
code += "& container,\n";
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
code += " typename TypeHandler<Ctx, ";
2023-08-16 20:40:36 +01:00
code += containerWithTypes;
code += ">::type returnArg) {\n";
code += func; // has rubbish indentation
code += " }\n";
code += " private:\n";
size_t count = 0;
for (const auto& pr : processors) {
code += " static void processor_";
code += std::to_string(count++);
code +=
"(result::Element& el, std::function<void(inst::Inst)> stack_ins, "
"ParsedData d) {\n";
2023-08-16 20:40:36 +01:00
code += pr.func; // bad indentation
code += " }\n";
}
code += " public:\n";
code +=
" static constexpr std::array<exporters::inst::Field, 0> fields{};\n";
code += " static constexpr std::array<exporters::inst::ProcessorInst, ";
code += std::to_string(processors.size());
code += "> processors{\n";
count = 0;
for (const auto& pr : processors) {
code += " exporters::inst::ProcessorInst{";
code += pr.type;
code += "::describe, &processor_";
code += std::to_string(count++);
code += "},\n";
}
code += " };\n";
code += "};\n\n";
}
2023-09-22 14:35:33 +01:00
void addCaptureKeySupport(std::string& code) {
code += R"(
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
template <typename Ctx, typename T>
2023-09-22 14:35:33 +01:00
class CaptureKeyHandler {
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
using DB = typename Ctx::DataBuffer;
2023-09-22 14:35:33 +01:00
public:
using type = types::st::Sum<DB, types::st::VarInt<DB>, types::st::VarInt<DB>>;
static auto captureKey(const T& key, auto returnArg) {
// Save scalars keys directly, otherwise save pointers for complex types
if constexpr (std::is_scalar_v<T>) {
return returnArg.template write<0>().write(static_cast<uint64_t>(key));
}
return returnArg.template write<1>().write(reinterpret_cast<uintptr_t>(&key));
}
};
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
template <bool CaptureKeys, typename Ctx, typename T>
auto maybeCaptureKey(Ctx& ctx, const T& key, auto returnArg) {
2023-09-22 14:35:33 +01:00
if constexpr (CaptureKeys) {
return returnArg.delegate([&key](auto ret) {
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
return CaptureKeyHandler<Ctx, T>::captureKey(key, ret);
2023-09-22 14:35:33 +01:00
});
} else {
return returnArg;
}
}
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
template <typename Ctx, typename T>
2023-09-22 14:35:33 +01:00
static constexpr inst::ProcessorInst CaptureKeysProcessor{
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
CaptureKeyHandler<Ctx, T>::type::describe,
2023-09-22 14:35:33 +01:00
[](result::Element& el, std::function<void(inst::Inst)> stack_ins, ParsedData d) {
if constexpr (std::is_same_v<
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
typename CaptureKeyHandler<Ctx, T>::type,
types::st::List<typename Ctx::DataBuffer, types::st::VarInt<typename Ctx::DataBuffer>>>) {
2023-09-22 14:35:33 +01:00
// String
auto& str = el.data.emplace<std::string>();
auto list = std::get<ParsedData::List>(d.val);
size_t strlen = list.length;
for (size_t i = 0; i < strlen; i++) {
auto value = list.values().val;
auto c = std::get<ParsedData::VarInt>(value).value;
str.push_back(c);
}
} else {
auto sum = std::get<ParsedData::Sum>(d.val);
if (sum.index == 0) {
el.data = oi::result::Element::Scalar{std::get<ParsedData::VarInt>(sum.value().val).value};
} else {
el.data = oi::result::Element::Pointer{std::get<ParsedData::VarInt>(sum.value().val).value};
}
}
}
};
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
template <bool CaptureKeys, typename Ctx, typename T>
2023-09-22 14:35:33 +01:00
static constexpr auto maybeCaptureKeysProcessor() {
if constexpr (CaptureKeys) {
return std::array<inst::ProcessorInst, 1>{
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
CaptureKeysProcessor<Ctx, T>,
2023-09-22 14:35:33 +01:00
};
}
else {
return std::array<inst::ProcessorInst, 0>{};
}
}
)";
}
2023-08-16 20:40:36 +01:00
void addStandardTypeHandlers(TypeGraph& typeGraph,
FeatureSet features,
std::string& code) {
2023-09-22 14:35:33 +01:00
if (features[Feature::TreeBuilderV2])
addCaptureKeySupport(code);
2023-08-16 20:40:36 +01:00
// Provide a wrapper function, getSizeType, to infer T instead of having to
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
// explicitly specify it with TypeHandler<Ctx, T>::getSizeType every time.
2023-08-16 20:40:36 +01:00
code += R"(
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
template <typename Ctx, typename T>
types::st::Unit<typename Ctx::DataBuffer>
getSizeType(Ctx& ctx, const T &t, typename TypeHandler<Ctx, T>::type returnArg) {
2023-08-16 20:40:36 +01:00
JLOG("obj @");
JLOGPTR(&t);
return TypeHandler<Ctx, T>::getSizeType(ctx, t, returnArg);
2023-08-16 20:40:36 +01:00
}
)";
2023-08-25 18:36:42 +01:00
if (features[Feature::TreeBuilderV2]) {
code += R"(
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
template <typename Ctx, typename T>
2023-08-25 18:36:42 +01:00
constexpr inst::Field make_field(std::string_view name) {
return inst::Field{
sizeof(T),
ExclusiveSizeProvider<T>::size,
2023-08-25 18:36:42 +01:00
name,
NameProvider<T>::names,
tbv2: replace DB template param with Ctx (#409) Summary: tbv2: replace DB template param with Ctx TreeBuilder v2 adds a DB template parameter to every function. This is used as part of the static type to decide what type of DataBuffer is being used: currently `BackInserterDataBuffer<std::vector<uint8_t>>` for OIL and it would be `DataSegmentDataBuffer` for OID. This change replaces the `DB` template parameter with a more general `Ctx`. Due to issues with dependent naming it also adds a `using DB` to each `TypeHandler` which has the same function as before. This allows us to add more "static context" (typedefs and constants) to functions without changing this signature again, because changing the signature of everything is a massive pain. Currently this change achieves nothing because Ctx contains only DB in a static wrapper. In the next change I'm going to pass a reference of type Ctx around to add a "dynamic context" to invocations which will contain the pointer array. In future we'll then be able to add either static or dynamic context without any signature adjustments. Test Plan: - CI --- Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebookexperimental/object-introspection/pull/409). * https://github.com/facebookexperimental/object-introspection/issues/410 * __->__ https://github.com/facebookexperimental/object-introspection/issues/409 Reviewed By: ajor Differential Revision: D51352092 Pulled By: JakeHillion
2023-11-15 19:28:21 +00:00
TypeHandler<Ctx, T>::fields,
TypeHandler<Ctx, T>::processors,
2023-08-25 18:36:42 +01:00
};
}
)";
}
2023-08-16 20:40:36 +01:00
// TODO: bit of a hack - making ContainerInfo a node in the type graph and
// traversing for it would remove the need for this set altogether.
std::unordered_set<const ContainerInfo*> used{};
std::vector<TemplateParam> arrayParams{
TemplateParam{typeGraph.makeType<Primitive>(Primitive::Kind::UInt64)},
TemplateParam{typeGraph.makeType<Primitive>(Primitive::Kind::UInt64),
"0"},
};
genContainerTypeHandler(
used, FuncGen::GetOiArrayContainerInfo(), arrayParams, code);
2023-04-20 18:30:59 +01:00
}
2023-07-03 18:51:25 +01:00
} // namespace
void CodeGen::addTypeHandlers(const TypeGraph& typeGraph, std::string& code) {
2023-04-20 18:30:59 +01:00
for (const Type& t : typeGraph.finalTypes) {
if (const auto* c = dynamic_cast<const Class*>(&t)) {
2023-08-10 21:34:06 +01:00
genClassTypeHandler(*c, code);
2023-04-20 18:30:59 +01:00
} else if (const auto* con = dynamic_cast<const Container*>(&t)) {
genContainerTypeHandler(
definedContainers_, con->containerInfo_, con->templateParams, code);
2023-09-22 14:35:33 +01:00
} else if (const auto* cap = dynamic_cast<const CaptureKeys*>(&t)) {
genContainerTypeHandler(definedContainers_,
2023-09-22 14:35:33 +01:00
cap->containerInfo(),
cap->container().templateParams,
code);
2023-04-20 18:30:59 +01:00
}
}
}
bool CodeGen::codegenFromDrgn(struct drgn_type* drgnType,
std::string linkageName,
std::string& code) {
return codegenFromDrgn(drgnType, code, ExactName{std::move(linkageName)});
}
bool CodeGen::codegenFromDrgn(struct drgn_type* drgnType, std::string& code) {
return codegenFromDrgn(
drgnType, code, HashedComponent{SymbolService::getTypeName(drgnType)});
}
bool CodeGen::codegenFromDrgn(struct drgn_type* drgnType,
std::string& code,
RootFunctionName name) {
try {
containerInfos_.reserve(config_.containerConfigPaths.size());
for (const auto& path : config_.containerConfigPaths) {
registerContainer(path);
}
} catch (const ContainerInfoError& err) {
LOG(ERROR) << "Error reading container TOML file " << err.what();
return false;
}
try {
addDrgnRoot(drgnType, typeGraph_);
} catch (const type_graph::DrgnParserError& err) {
LOG(ERROR) << "Error parsing DWARF: " << err.what();
return false;
}
transform(typeGraph_);
generate(typeGraph_, code, std::move(name));
return true;
}
void CodeGen::exportDrgnTypes(TypeHierarchy& th,
std::list<drgn_type>& drgnTypes,
drgn_type** rootType) const {
assert(typeGraph_.rootTypes().size() == 1);
type_graph::DrgnExporter drgnExporter{th, drgnTypes};
for (auto& type : typeGraph_.rootTypes()) {
*rootType = drgnExporter.accept(type);
}
}
void CodeGen::registerContainer(std::unique_ptr<ContainerInfo> info) {
VLOG(1) << "Registered container: " << info->typeName;
containerInfos_.emplace_back(std::move(info));
}
void CodeGen::registerContainer(const fs::path& path) {
auto info = std::make_unique<ContainerInfo>(path);
if (info->requiredFeatures != (config_.features & info->requiredFeatures)) {
VLOG(1) << "Skipping container (feature conflict): " << info->typeName;
return;
}
registerContainer(std::move(info));
}
void CodeGen::addDrgnRoot(struct drgn_type* drgnType, TypeGraph& typeGraph) {
DrgnParserOptions options{
.chaseRawPointers = config_.features[Feature::ChaseRawPointers],
};
DrgnParser drgnParser{typeGraph, options};
Type& parsedRoot = drgnParser.parse(drgnType);
typeGraph.addRoot(parsedRoot);
}
void CodeGen::transform(TypeGraph& typeGraph) {
type_graph::PassManager pm;
// Simplify the type graph first so there is less work for later passes
pm.addPass(RemoveTopLevelPointer::createPass());
pm.addPass(IdentifyContainers::createPass(containerInfos_));
pm.addPass(Flattener::createPass());
pm.addPass(AlignmentCalc::createPass());
pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes));
if (config_.features[Feature::PruneTypeGraph])
pm.addPass(Prune::createPass());
if (config_.features[Feature::PolymorphicInheritance]) {
// Parse new children nodes
DrgnParserOptions options{
.chaseRawPointers = config_.features[Feature::ChaseRawPointers],
};
DrgnParser drgnParser{typeGraph, options};
pm.addPass(AddChildren::createPass(drgnParser, symbols_));
// Re-run passes over newly added children
pm.addPass(IdentifyContainers::createPass(containerInfos_));
pm.addPass(Flattener::createPass());
pm.addPass(AlignmentCalc::createPass());
pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes));
if (config_.features[Feature::PruneTypeGraph])
pm.addPass(Prune::createPass());
}
pm.addPass(RemoveMembers::createPass(config_.membersToStub));
2023-09-22 14:35:33 +01:00
if (!config_.features[Feature::TreeBuilderV2])
pm.addPass(EnforceCompatibility::createPass());
2023-09-22 14:35:33 +01:00
if (config_.features[Feature::TreeBuilderV2] &&
!config_.keysToCapture.empty())
pm.addPass(KeyCapture::createPass(config_.keysToCapture, containerInfos_));
// Add padding to fill in the gaps of removed members and ensure their
// alignments
pm.addPass(AddPadding::createPass());
pm.addPass(NameGen::createPass());
pm.addPass(TopoSorter::createPass());
pm.run(typeGraph);
LOG(INFO) << "Sorted types:\n";
for (Type& t : typeGraph.finalTypes) {
LOG(INFO) << " " << t.name() << std::endl;
};
}
void CodeGen::generate(TypeGraph& typeGraph,
std::string& code,
RootFunctionName rootName) {
code = headers::oi_OITraceCode_cpp;
2023-08-16 20:40:36 +01:00
if (!config_.features[Feature::Library]) {
FuncGen::DeclareExterns(code);
}
if (!config_.features[Feature::TreeBuilderV2]) {
2023-04-20 18:30:59 +01:00
defineMacros(code);
}
addIncludes(typeGraph, config_.features, code);
2023-09-22 14:35:33 +01:00
defineInternalTypes(code);
2023-09-27 02:14:23 +01:00
FuncGen::DefineJitLog(code, config_.features);
2023-04-20 18:30:59 +01:00
if (config_.features[Feature::TreeBuilderV2]) {
2023-08-16 20:40:36 +01:00
if (config_.features[Feature::Library]) {
FuncGen::DefineBackInserterDataBuffer(code);
} else {
FuncGen::DefineDataSegmentDataBuffer(code);
}
code += "using namespace oi;\n";
code += "using namespace oi::detail;\n";
code += "using oi::exporters::ParsedData;\n";
code += "using namespace oi::exporters;\n";
2023-04-20 18:30:59 +01:00
code += "namespace OIInternal {\nnamespace {\n";
2023-08-16 20:40:36 +01:00
FuncGen::DefineBasicTypeHandlers(code, config_.features);
2023-04-20 18:30:59 +01:00
code += "} // namespace\n} // namespace OIInternal\n";
}
if (config_.features[Feature::CaptureThriftIsset]) {
genDefsThrift(typeGraph, code);
}
if (!config_.features[Feature::TreeBuilderV2]) {
code += "namespace {\n";
code += "static struct Context {\n";
code += " PointerHashSet<> pointers;\n";
code += "} ctx;\n";
code += "} // namespace\n";
}
/*
* The purpose of the anonymous namespace within `OIInternal` is that
* anything defined within an anonymous namespace has internal-linkage,
* and therefore won't appear in the symbol table of the resulting object
* file. Both OIL and OID do a linear search through the symbol table for
* the top-level `getSize` function to locate the probe entry point, so
* by keeping the contents of the symbol table to a minimum, we make that
* process faster.
*/
code += "namespace OIInternal {\nnamespace {\n";
if (!config_.features[Feature::TreeBuilderV2]) {
2023-04-20 18:30:59 +01:00
FuncGen::DefineEncodeData(code);
FuncGen::DefineEncodeDataSize(code);
FuncGen::DefineStoreData(code);
}
FuncGen::DeclareGetContainer(code);
genDecls(typeGraph, code);
genDefs(typeGraph, code);
genStaticAsserts(typeGraph, code);
if (config_.features[Feature::TreeBuilderV2]) {
genNames(typeGraph, code);
genExclusiveSizes(typeGraph, code);
}
if (config_.features[Feature::TreeBuilderV2]) {
2023-08-16 20:40:36 +01:00
addStandardTypeHandlers(typeGraph, config_.features, code);
2023-07-03 18:51:25 +01:00
addTypeHandlers(typeGraph, code);
2023-04-20 18:30:59 +01:00
} else {
addStandardGetSizeFuncDecls(code);
addGetSizeFuncDecls(typeGraph, code);
2023-04-20 18:30:59 +01:00
addStandardGetSizeFuncDefs(code);
addGetSizeFuncDefs(typeGraph, code);
2023-04-20 18:30:59 +01:00
}
assert(typeGraph.rootTypes().size() == 1);
Type& rootType = typeGraph.rootTypes()[0];
code += "\nusing __ROOT_TYPE__ = " + rootType.name() + ";\n";
code += "} // namespace\n} // namespace OIInternal\n";
const auto& typeToHash = std::visit(
[](const auto& v) -> const std::string& {
using T = std::decay_t<decltype(v)>;
if constexpr (std::is_same_v<ExactName, T> ||
std::is_same_v<HashedComponent, T>) {
return v.name;
} else {
static_assert(always_false_v<T>, "missing visit");
}
},
rootName);
if (config_.features[Feature::TreeBuilderV2]) {
FuncGen::DefineTopLevelIntrospect(code, typeToHash);
2023-04-20 18:30:59 +01:00
} else {
FuncGen::DefineTopLevelGetSizeRef(code, typeToHash, config_.features);
}
2023-08-16 20:40:36 +01:00
if (config_.features[Feature::TreeBuilderV2]) {
FuncGen::DefineTreeBuilderInstructions(code,
typeToHash,
2023-08-16 20:40:36 +01:00
calculateExclusiveSize(rootType),
enumerateTypeNames(rootType));
2023-04-20 18:30:59 +01:00
}
if (auto* n = std::get_if<ExactName>(&rootName))
FuncGen::DefineTopLevelIntrospectNamed(code, typeToHash, n->name);
if (VLOG_IS_ON(3)) {
VLOG(3) << "Generated trace code:\n";
2023-08-24 13:41:39 +01:00
// VLOG truncates output, so use std::cerr
std::cerr << code;
}
}
} // namespace oi::detail