This commit is contained in:
Jake Hillion 2023-08-16 12:40:36 -07:00 committed by Jake Hillion
parent 57210cf06c
commit 5071519e45
103 changed files with 2566 additions and 481 deletions

View File

@ -334,9 +334,18 @@ target_link_libraries(treebuilder
## OI Outputs ## OI Outputs
### Object Introspection as a Library (OIL) ### Object Introspection as a Library (OIL)
add_library(oil oi/OILibrary.cpp oi/OILibraryImpl.cpp) add_library(oil
oi/IntrospectionResult.cpp
oi/exporters/ParsedData.cpp
)
target_link_libraries(oil folly_headers)
target_include_directories(oil PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(oil PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(oil oicore)
add_library(oil_jit
oi/OILibrary.cpp
oi/OILibraryImpl.cpp
)
target_link_libraries(oil_jit oicore oil)
### Object Introspection as a Library Generator (OILGen) ### Object Introspection as a Library Generator (OILGen)
add_executable(oilgen add_executable(oilgen

View File

@ -0,0 +1,78 @@
/*
* 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.
*/
#if defined(INCLUDED_OI_INTROSPECTIONRESULT_INL_H) || \
!defined(INCLUDED_OI_INTROSPECTIONRESULT_H)
static_assert(false,
"IntrospectionResult-inl.h provides inline declarations for "
"IntrospectionResult.h and should only "
"be included by IntrospectionResult.h");
#endif
#define INCLUDED_OI_INTROSPECTIONRESULT_INL_H 1
#include <cassert>
#include "IntrospectionResult.h"
namespace oi {
inline IntrospectionResult::IntrospectionResult(std::vector<uint8_t> buf,
exporters::inst::Inst inst)
: buf_(std::move(buf)), inst_(inst) {
}
inline IntrospectionResult::const_iterator::const_iterator(
std::vector<uint8_t>::const_iterator data, exporters::inst::Inst type)
: data_(data), stack_({type}) {
}
inline IntrospectionResult::const_iterator::const_iterator(
std::vector<uint8_t>::const_iterator data)
: data_(data) {
}
inline IntrospectionResult::const_iterator IntrospectionResult::cbegin() const {
return ++const_iterator{buf_.cbegin(), inst_};
}
inline IntrospectionResult::const_iterator IntrospectionResult::cend() const {
return {buf_.cend()};
}
inline IntrospectionResult::const_iterator
IntrospectionResult::const_iterator::operator++(int) {
auto old = *this;
operator++();
return old;
}
inline const result::Element& IntrospectionResult::const_iterator::operator*()
const {
assert(next_);
return *next_;
}
inline const result::Element* IntrospectionResult::const_iterator::operator->()
const {
return &*next_;
}
inline bool IntrospectionResult::const_iterator::operator==(
const IntrospectionResult::const_iterator& that) const {
return this->data_ == that.data_ && !this->next_.has_value() &&
!that.next_.has_value(); // TODO: is this sufficient? kind of hacky as
// this only works for comparing to .end()
}
inline bool IntrospectionResult::const_iterator::operator!=(
const IntrospectionResult::const_iterator& that) const {
return !(*this == that);
}
} // namespace oi

View File

@ -0,0 +1,70 @@
/*
* 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.
*/
#ifndef INCLUDED_OI_INTROSPECTIONRESULT_H
#define INCLUDED_OI_INTROSPECTIONRESULT_H 1
#include <oi/exporters/inst.h>
#include <oi/result/Element.h>
#include <oi/types/dy.h>
#include <cstdint>
#include <optional>
#include <span>
#include <stack>
#include <string_view>
#include <vector>
namespace oi {
class IntrospectionResult {
public:
class const_iterator {
friend class IntrospectionResult;
public:
bool operator==(const const_iterator& that) const;
bool operator!=(const const_iterator& that) const;
const result::Element& operator*() const;
const result::Element* operator->() const;
const_iterator& operator++();
const_iterator operator++(int);
private:
const_iterator(std::vector<uint8_t>::const_iterator data,
exporters::inst::Inst type);
const_iterator(std::vector<uint8_t>::const_iterator data);
std::vector<uint8_t>::const_iterator data_;
std::stack<exporters::inst::Inst> stack_;
std::optional<result::Element> next_;
std::vector<std::string_view> type_path_;
};
IntrospectionResult(std::vector<uint8_t> buf, exporters::inst::Inst inst);
const_iterator cbegin() const;
const_iterator cend() const;
private:
std::vector<uint8_t> buf_;
exporters::inst::Inst inst_;
};
} // namespace oi
#include <oi/IntrospectionResult-inl.h>
#endif

View File

@ -0,0 +1,44 @@
/*
* 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.
*/
#ifndef INCLUDED_OI_EXPORTERS_JSON_H
#define INCLUDED_OI_EXPORTERS_JSON_H 1
#include <oi/IntrospectionResult.h>
#include <ostream>
namespace oi::exporters {
class Json {
public:
Json(std::ostream& out);
void print(const IntrospectionResult&);
void print(IntrospectionResult::const_iterator& it,
IntrospectionResult::const_iterator end);
void setPretty(bool pretty) {
pretty_ = pretty;
}
private:
bool pretty_ = false;
std::ostream& out_;
};
} // namespace oi::exporters
#endif

View File

@ -0,0 +1,80 @@
/*
* 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.
*/
#ifndef INCLUDED_OI_EXPORTERS_PARSED_DATA_H
#define INCLUDED_OI_EXPORTERS_PARSED_DATA_H 1
#include <oi/types/dy.h>
#include <cassert>
#include <cstdint>
#include <variant>
#include <vector>
namespace oi::exporters {
struct ParsedData {
class Lazy {
public:
Lazy(std::vector<uint8_t>::const_iterator& it, types::dy::Dynamic ty)
: it_(it), ty_(ty) {
}
ParsedData operator()() {
return ParsedData::parse(it_, ty_);
}
private:
std::vector<uint8_t>::const_iterator& it_;
types::dy::Dynamic ty_;
};
struct Unit {};
struct VarInt {
uint64_t value;
};
struct Pair {
Lazy first;
Lazy second;
};
struct List {
uint64_t length;
Lazy values;
};
struct Sum {
uint64_t index;
Lazy value;
};
static ParsedData parse(std::vector<uint8_t>::const_iterator& it,
types::dy::Dynamic ty);
ParsedData(Unit&& val_) : val(val_) {
}
ParsedData(VarInt&& val_) : val(val_) {
}
ParsedData(Pair&& val_) : val(val_) {
}
ParsedData(List&& val_) : val(val_) {
}
ParsedData(Sum&& val_) : val(val_) {
}
std::variant<Unit, VarInt, Pair, List, Sum> val;
};
} // namespace oi::exporters
#endif

View File

@ -0,0 +1,90 @@
/*
* 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.
*/
#ifndef INCLUDED_OI_EXPORTERS_INST_H
#define INCLUDED_OI_EXPORTERS_INST_H 1
#include <oi/exporters/ParsedData.h>
#include <oi/result/Element.h>
#include <oi/types/dy.h>
#include <array>
#include <cstdint>
#include <initializer_list>
#include <stack>
#include <utility>
#include <variant>
namespace oi::exporters::inst {
struct PopTypePath;
struct Field;
using Inst = std::variant<PopTypePath, std::reference_wrapper<const Field>>;
using Processor = void (*)(result::Element&, std::stack<Inst>&, ParsedData);
using ProcessorInst = std::pair<types::dy::Dynamic, Processor>;
struct PopTypePath {};
struct Field {
template <size_t N0, size_t N1, size_t N2>
constexpr Field(size_t static_size_,
size_t exclusive_size_,
std::string_view name_,
const std::array<std::string_view, N0>& type_names_,
const std::array<Field, N1>& fields_,
const std::array<ProcessorInst, N2>& processors_);
template <size_t N0, size_t N1, size_t N2>
constexpr Field(size_t static_size_,
std::string_view name_,
const std::array<std::string_view, N0>& type_names_,
const std::array<Field, N1>& fields_,
const std::array<ProcessorInst, N2>& processors_)
: Field(static_size_,
static_size_,
name_,
type_names_,
fields_,
processors_) {
}
constexpr Field(const Field&) = default; // no idea why this is needed
size_t static_size;
size_t exclusive_size;
std::string_view name;
std::span<const std::string_view> type_names;
std::span<const Field> fields;
std::span<const ProcessorInst> processors;
};
template <size_t N0, size_t N1, size_t N2>
constexpr Field::Field(size_t static_size_,
size_t exclusive_size_,
std::string_view name_,
const std::array<std::string_view, N0>& type_names_,
const std::array<Field, N1>& fields_,
const std::array<ProcessorInst, N2>& processors_)
: static_size(static_size_),
exclusive_size(exclusive_size_),
name(name_),
type_names(type_names_),
fields(fields_),
processors(processors_) {
}
} // namespace oi::exporters::inst
#endif

91
include/oi/oi-jit-inl.h Normal file
View File

@ -0,0 +1,91 @@
/*
* 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.
*/
#if defined(INCLUDED_OI_OI_JIT_INL_H) || !defined(INCLUDED_OI_OI_JIT_H)
static_assert(
false,
"oi-jit-inl.h provides inline declarations for oi-jit.h and should only "
"be included by oi-jit.h");
#endif
#define INCLUDED_OI_OI_JIT_INL_H 1
#include <stdexcept>
#include "oi-jit.h"
namespace oi {
template <typename T, Feature... Fs>
inline std::optional<IntrospectionResult> setupAndIntrospect(
const T& objectAddr, const GeneratorOptions& opts) {
if (!CodegenHandler<T, Fs...>::init(opts))
return std::nullopt;
return CodegenHandler<T, Fs...>::introspect(objectAddr);
}
template <typename T, Feature... Fs>
inline std::atomic<bool>& CodegenHandler<T, Fs...>::getIsCritical() {
static std::atomic<bool> isCritical = false;
return isCritical;
}
template <typename T, Feature... Fs>
inline std::atomic<void (*)(const T&, std::vector<uint8_t>&)>&
CodegenHandler<T, Fs...>::getIntrospectionFunc() {
static std::atomic<void (*)(const T&, std::vector<uint8_t>&)> func = nullptr;
return func;
}
template <typename T, Feature... Fs>
inline std::atomic<const exporters::inst::Inst*>&
CodegenHandler<T, Fs...>::getTreeBuilderInstructions() {
static std::atomic<const exporters::inst::Inst*> ty = nullptr;
return ty;
}
template <typename T, Feature... Fs>
inline bool CodegenHandler<T, Fs...>::init(const GeneratorOptions& opts) {
if (getIntrospectionFunc().load() != nullptr &&
getTreeBuilderInstructions().load() != nullptr)
return true; // already initialised
if (getIsCritical().exchange(true))
return false; // other thread is initialising/has failed
auto lib =
OILibrary(reinterpret_cast<void*>(&getIntrospectionFunc), {Fs...}, opts);
auto [vfp, ty] = lib.init();
getIntrospectionFunc().store(reinterpret_cast<func_type>(vfp));
getTreeBuilderInstructions().store(&ty);
return true;
}
template <typename T, Feature... Fs>
inline IntrospectionResult CodegenHandler<T, Fs...>::introspect(
const T& objectAddr) {
func_type func = getIntrospectionFunc().load();
const exporters::inst::Inst* ty = getTreeBuilderInstructions().load();
if (func == nullptr || ty == nullptr)
throw std::logic_error("introspect(const T&) called when uninitialised");
std::vector<uint8_t> buf;
static_assert(sizeof(std::vector<uint8_t>) == 24);
func(objectAddr, buf);
return IntrospectionResult{std::move(buf), *ty};
}
} // namespace oi

82
include/oi/oi-jit.h Normal file
View File

@ -0,0 +1,82 @@
/*
* 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.
*/
#ifndef INCLUDED_OI_OI_JIT_H
#define INCLUDED_OI_OI_JIT_H 1
#include <atomic>
#include <filesystem>
#include <memory>
#include <optional>
#include <unordered_set>
#include <utility>
#include <vector>
#include "exporters/inst.h"
#include "oi.h"
namespace oi::detail {
class OILibraryImpl;
}
namespace oi {
struct GeneratorOptions {
std::filesystem::path configFilePath;
std::filesystem::path sourceFileDumpPath;
int debugLevel = 0;
};
class OILibrary {
public:
OILibrary(void* atomicHome,
std::unordered_set<Feature>,
GeneratorOptions opts);
~OILibrary();
std::pair<void*, const exporters::inst::Inst&> init();
private:
std::unique_ptr<detail::OILibraryImpl> pimpl_;
};
/*
* setupAndIntrospect
*
* Execute JIT compilation then introspect the given object. Throws on error.
* Returns std::nullopt rather than duplicating initialisation if its ongoing.
*/
template <typename T, Feature... Fs>
std::optional<IntrospectionResult> setupAndIntrospect(
const T& objectAddr, const GeneratorOptions& opts);
template <typename T, Feature... Fs>
class CodegenHandler {
public:
static bool init(const GeneratorOptions& opts);
static IntrospectionResult introspect(const T& objectAddr);
private:
using func_type = void (*)(const T&, std::vector<uint8_t>&);
static std::atomic<bool>& getIsCritical();
static std::atomic<func_type>& getIntrospectionFunc();
static std::atomic<const exporters::inst::Inst*>&
getTreeBuilderInstructions();
};
} // namespace oi
#include "oi-jit-inl.h"
#endif

47
include/oi/oi.h Normal file
View File

@ -0,0 +1,47 @@
/*
* 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.
*/
#ifndef INCLUDED_OI_OI_H
#define INCLUDED_OI_OI_H 1
#include <oi/IntrospectionResult.h>
#include <oi/types/dy.h>
#include <cstdint>
#include <vector>
namespace oi {
enum class Feature {
ChaseRawPointers,
CaptureThriftIsset,
GenJitDebug,
};
template <typename T, Feature... Fs>
IntrospectionResult __attribute__((noinline)) introspect(const T& objectAddr);
} // namespace oi
#ifndef OIL_AOT_COMPILATION
#include "oi-jit.h"
#else
template <typename T, Feature... Fs>
IntrospectionResult __attribute__((noinline)) introspect(const T& objectAddr);
{ static_assert(false, "OIL v2 does not yet support AoT compilation."); }
#endif
#endif

View File

@ -0,0 +1,49 @@
/*
* 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.
*/
#ifndef INCLUDED_OI_RESULT_ELEMENT_H
#define INCLUDED_OI_RESULT_ELEMENT_H 1
#include <optional>
#include <span>
#include <string_view>
#include <vector>
namespace oi::result {
struct Element {
struct ContainerStats {
size_t capacity;
size_t length;
};
struct IsSetStats {
bool is_set;
};
std::string_view name;
std::vector<std::string_view>
type_path; // TODO: should be span<const std::string_view>
std::span<const std::string_view> type_names;
size_t static_size;
size_t exclusive_size;
std::optional<uintptr_t> pointer;
std::optional<ContainerStats> container_stats;
std::optional<IsSetStats> is_set_stats;
};
} // namespace oi::result
#endif

View File

@ -14,8 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef OI_TYPES_DY_H #ifndef INCLUDED_OI_TYPES_DY_H
#define OI_TYPES_DY_H 1 #define INCLUDED_OI_TYPES_DY_H 1
/* /*
* Dynamic Types * Dynamic Types
@ -45,11 +45,11 @@
namespace oi::types::dy { namespace oi::types::dy {
class Unit; struct Unit;
class VarInt; struct VarInt;
class Pair; struct Pair;
class Sum; struct Sum;
class List; struct List;
/* /*
* Dynamic * Dynamic
@ -63,11 +63,10 @@ using Dynamic = std::variant<std::reference_wrapper<const Unit>,
std::reference_wrapper<const Sum>, std::reference_wrapper<const Sum>,
std::reference_wrapper<const List> >; std::reference_wrapper<const List> >;
class Unit {}; struct Unit {};
class VarInt {}; struct VarInt {};
class Pair { struct Pair {
public:
constexpr Pair(Dynamic first_, Dynamic second_) constexpr Pair(Dynamic first_, Dynamic second_)
: first(first_), second(second_) { : first(first_), second(second_) {
} }
@ -76,8 +75,7 @@ class Pair {
Dynamic second; Dynamic second;
}; };
class Sum { struct Sum {
public:
template <size_t N> template <size_t N>
constexpr Sum(const std::array<Dynamic, N>& variants_) : variants(variants_) { constexpr Sum(const std::array<Dynamic, N>& variants_) : variants(variants_) {
} }
@ -85,8 +83,7 @@ class Sum {
std::span<const Dynamic> variants; std::span<const Dynamic> variants;
}; };
class List { struct List {
public:
constexpr List(Dynamic element_) : element(element_) { constexpr List(Dynamic element_) : element(element_) {
} }

View File

@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#ifndef OI_TYPES_ST_H #ifndef INCLUDED_OI_TYPES_ST_H
#define OI_TYPES_ST_H 1 #define INCLUDED_OI_TYPES_ST_H 1
/* /*
* Static Types * Static Types
@ -123,7 +123,7 @@ class VarInt {
} }
Unit<DataBuffer> write(uint64_t val) { Unit<DataBuffer> write(uint64_t val) {
while (val >= 128) { while (val >= 0x80) {
_buf.write_byte(0x80 | (val & 0x7f)); _buf.write_byte(0x80 | (val & 0x7f));
val >>= 7; val >>= 7;
} }

View File

@ -52,4 +52,10 @@ target_link_libraries(codegen
glog::glog glog::glog
) )
add_library(exporters_json
exporters/Json.cpp
)
target_include_directories(exporters_json PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(exporters_json oil)
add_subdirectory(type_graph) add_subdirectory(type_graph)

View File

@ -19,6 +19,7 @@
#include <boost/format.hpp> #include <boost/format.hpp>
#include <iostream> #include <iostream>
#include <numeric>
#include <set> #include <set>
#include <string_view> #include <string_view>
@ -54,9 +55,11 @@ using type_graph::Enum;
using type_graph::Flattener; using type_graph::Flattener;
using type_graph::Member; using type_graph::Member;
using type_graph::NameGen; using type_graph::NameGen;
using type_graph::Primitive;
using type_graph::Prune; using type_graph::Prune;
using type_graph::RemoveMembers; using type_graph::RemoveMembers;
using type_graph::RemoveTopLevelPointer; using type_graph::RemoveTopLevelPointer;
using type_graph::TemplateParam;
using type_graph::TopoSorter; using type_graph::TopoSorter;
using type_graph::Type; using type_graph::Type;
using type_graph::Typedef; using type_graph::Typedef;
@ -67,6 +70,18 @@ template <typename T>
using ref = std::reference_wrapper<T>; using ref = std::reference_wrapper<T>;
namespace { namespace {
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) { void defineMacros(std::string& code) {
if (true /* TODO: config.useDataSegment*/) { if (true /* TODO: config.useDataSegment*/) {
code += R"( code += R"(
@ -93,6 +108,23 @@ struct OIArray {
void defineJitLog(FeatureSet features, std::string& code) { void defineJitLog(FeatureSet features, std::string& code) {
if (features[Feature::JitLogging]) { if (features[Feature::JitLogging]) {
code += R"( code += R"(
extern int logFile;
void __jlogptr(uintptr_t ptr) {
static constexpr char hexdigits[] = "0123456789abcdef";
static constexpr size_t ptrlen = 2 * sizeof(ptr);
static char hexstr[ptrlen + 1] = {};
size_t i = ptrlen;
while (i--) {
hexstr[i] = hexdigits[ptr & 0xf];
ptr = ptr >> 4;
}
hexstr[ptrlen] = '\n';
write(logFile, hexstr, sizeof(hexstr));
}
#define JLOG(str) \ #define JLOG(str) \
do { \ do { \
if (__builtin_expect(logFile, 0)) { \ if (__builtin_expect(logFile, 0)) { \
@ -128,6 +160,10 @@ void addIncludes(const TypeGraph& typeGraph,
code += "#define DEFINE_DESCRIBE 1\n"; // added before all includes code += "#define DEFINE_DESCRIBE 1\n"; // added before all includes
} }
if (features[Feature::TreeBuilderV2])
includes.emplace("oi/exporters/inst.h");
if (features[Feature::Library])
includes.emplace("vector");
if (features[Feature::JitTiming]) { if (features[Feature::JitTiming]) {
includes.emplace("chrono"); includes.emplace("chrono");
} }
@ -569,38 +605,6 @@ void CodeGen::addGetSizeFuncDefs(const TypeGraph& typeGraph,
namespace { namespace {
void addStandardTypeHandlers(std::string& code) {
// Provide a wrapper function, getSizeType, to infer T instead of having to
// explicitly specify it with TypeHandler<DB, T>::getSizeType every time.
code += R"(
template <typename DB, typename T>
types::st::Unit<DB>
getSizeType(const T &t, typename TypeHandler<DB, T>::type returnArg) {
JLOG("obj @");
JLOGPTR(&t);
return TypeHandler<DB, T>::getSizeType(t, returnArg);
}
)";
code += R"(
template<typename DB, typename T0, long unsigned int N>
struct TypeHandler<DB, OIArray<T0, N>> {
using type = types::st::List<DB, typename TypeHandler<DB, T0>::type>;
static types::st::Unit<DB> getSizeType(
const OIArray<T0, N> &container,
typename TypeHandler<DB, OIArray<T0,N>>::type returnArg) {
auto tail = returnArg.write(N);
for (size_t i=0; i<N; i++) {
tail = tail.delegate([&container, i](auto ret) {
return TypeHandler<DB, T0>::getSizeType(container.vals[i], ret);
});
}
return tail.finish();
}
};
)";
}
// Find the last member that isn't padding's index. Return -1 if no such member. // Find the last member that isn't padding's index. Return -1 if no such member.
size_t getLastNonPaddingMemberIndex(const std::vector<Member>& members) { size_t getLastNonPaddingMemberIndex(const std::vector<Member>& members) {
for (size_t i = members.size() - 1; i != (size_t)-1; --i) { for (size_t i = members.size() - 1; i != (size_t)-1; --i) {
@ -724,6 +728,70 @@ void CodeGen::genClassStaticType(const Class& c, std::string& 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 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) +
"_type_names, TypeHandler<DB, decltype(" + fullName +
")>::fields, TypeHandler<DB, decltype(" + fullName +
")>::processors},\n";
}
code += " };\n";
code +=
"static constexpr std::array<exporters::inst::ProcessorInst, 0> "
"processors{};\n";
}
void CodeGen::genClassTypeHandler(const Class& c, std::string& code) { void CodeGen::genClassTypeHandler(const Class& c, std::string& code) {
std::string helpers; std::string helpers;
@ -756,30 +824,159 @@ void CodeGen::genClassTypeHandler(const Class& c, std::string& code) {
code += " using type = "; code += " using type = ";
genClassStaticType(c, code); genClassStaticType(c, code);
code += ";\n"; code += ";\n";
if (config_.features[Feature::TreeBuilderV2])
genClassTreeBuilderInstructions(c, code);
genClassTraversalFunction(c, code); genClassTraversalFunction(c, code);
code += "};\n"; code += "};\n";
} }
namespace { namespace {
void getContainerTypeHandler(std::unordered_set<const ContainerInfo*>& used, void genContainerTypeHandler(FeatureSet features,
const Container& c, std::unordered_set<const ContainerInfo*>& used,
const ContainerInfo& c,
std::span<const TemplateParam> templateParams,
std::string& code) { std::string& code) {
if (!used.insert(&c.containerInfo_).second) { if (!used.insert(&c).second)
return;
if (!features[Feature::TreeBuilderV2]) {
const auto& handler = c.codegen.handler;
if (handler.empty()) {
LOG(ERROR) << "`codegen.handler` must be specified for all containers "
"under \"-ftyped-data-segment\", not specified for \"" +
c.typeName + "\"";
throw std::runtime_error("missing `codegen.handler`");
}
auto fmt = boost::format(c.codegen.handler) % c.typeName;
code += fmt.str();
return; return;
} }
const auto& handler = c.containerInfo_.codegen.handler;
// TODO: Move this check into the ContainerInfo parsing once always enabled. // TODO: Move this check into the ContainerInfo parsing once always enabled.
if (handler.empty()) { const auto& func = c.codegen.traversalFunc;
LOG(ERROR) << "`codegen.handler` must be specified for all containers " const auto& processors = c.codegen.processors;
"under \"-ftyped-data-segment\", not specified for \"" +
c.containerInfo_.typeName + "\""; if (func.empty()) {
throw std::runtime_error("missing `codegen.handler`"); 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`");
} }
auto fmt = boost::format(c.containerInfo_.codegen.handler) %
c.containerInfo_.typeName; std::string containerWithTypes = c.typeName;
code += fmt.str(); 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 += '>';
code += "template <typename DB";
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";
code += "struct TypeHandler<DB, ";
code += containerWithTypes;
code += "> {\n";
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 += " const ";
code += containerWithTypes;
code += "& container,\n";
code += " typename TypeHandler<DB, ";
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::stack<inst::Inst>& ins, ParsedData d) {\n";
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";
}
void addStandardTypeHandlers(TypeGraph& typeGraph,
FeatureSet features,
std::string& code) {
// Provide a wrapper function, getSizeType, to infer T instead of having to
// explicitly specify it with TypeHandler<DB, T>::getSizeType every time.
code += R"(
template <typename DB, typename T>
types::st::Unit<DB>
getSizeType(const T &t, typename TypeHandler<DB, T>::type returnArg) {
JLOG("obj @");
JLOGPTR(&t);
return TypeHandler<DB, T>::getSizeType(t, returnArg);
}
)";
// 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(features, used, FuncGen::GetOiArrayContainerInfo(),
arrayParams, code);
} }
} // namespace } // namespace
@ -789,7 +986,8 @@ void CodeGen::addTypeHandlers(const TypeGraph& typeGraph, std::string& code) {
if (const auto* c = dynamic_cast<const Class*>(&t)) { if (const auto* c = dynamic_cast<const Class*>(&t)) {
genClassTypeHandler(*c, code); genClassTypeHandler(*c, code);
} else if (const auto* con = dynamic_cast<const Container*>(&t)) { } else if (const auto* con = dynamic_cast<const Container*>(&t)) {
getContainerTypeHandler(definedContainers_, *con, code); genContainerTypeHandler(config_.features, definedContainers_,
con->containerInfo_, con->templateParams, code);
} }
} }
} }
@ -886,6 +1084,9 @@ void CodeGen::generate(
struct drgn_type* drgnType /* TODO: this argument should not be required */ struct drgn_type* drgnType /* TODO: this argument should not be required */
) { ) {
code = headers::oi_OITraceCode_cpp; code = headers::oi_OITraceCode_cpp;
if (!config_.features[Feature::Library]) {
FuncGen::DeclareExterns(code);
}
if (!config_.features[Feature::TypedDataSegment]) { if (!config_.features[Feature::TypedDataSegment]) {
defineMacros(code); defineMacros(code);
} }
@ -894,12 +1095,19 @@ void CodeGen::generate(
defineJitLog(config_.features, code); defineJitLog(config_.features, code);
if (config_.features[Feature::TypedDataSegment]) { if (config_.features[Feature::TypedDataSegment]) {
FuncGen::DefineDataSegmentDataBuffer(code); if (config_.features[Feature::Library]) {
FuncGen::DefineBackInserterDataBuffer(code);
} else {
FuncGen::DefineDataSegmentDataBuffer(code);
}
code += "using namespace oi;\n"; code += "using namespace oi;\n";
code += "using namespace oi::detail;\n"; code += "using namespace oi::detail;\n";
if (config_.features[Feature::TreeBuilderV2]) {
code += "using oi::exporters::ParsedData;\n";
code += "using namespace oi::exporters;\n";
}
code += "namespace OIInternal {\nnamespace {\n"; code += "namespace OIInternal {\nnamespace {\n";
FuncGen::DefineBasicTypeHandlers(code); FuncGen::DefineBasicTypeHandlers(code, config_.features);
code += "} // namespace\n} // namespace OIInternal\n"; code += "} // namespace\n} // namespace OIInternal\n";
} }
@ -930,7 +1138,7 @@ void CodeGen::generate(
genStaticAsserts(typeGraph, code); genStaticAsserts(typeGraph, code);
if (config_.features[Feature::TypedDataSegment]) { if (config_.features[Feature::TypedDataSegment]) {
addStandardTypeHandlers(code); addStandardTypeHandlers(typeGraph, config_.features, code);
addTypeHandlers(typeGraph, code); addTypeHandlers(typeGraph, code);
} else { } else {
addStandardGetSizeFuncDecls(code); addStandardGetSizeFuncDecls(code);
@ -946,13 +1154,19 @@ void CodeGen::generate(
code += "} // namespace\n} // namespace OIInternal\n"; code += "} // namespace\n} // namespace OIInternal\n";
const auto typeName = SymbolService::getTypeName(drgnType); const auto typeName = SymbolService::getTypeName(drgnType);
if (config_.features[Feature::TypedDataSegment]) { if (config_.features[Feature::Library]) {
FuncGen::DefineTopLevelIntrospect(code, typeName);
} else if (config_.features[Feature::TypedDataSegment]) {
FuncGen::DefineTopLevelGetSizeRefTyped(code, typeName, config_.features); FuncGen::DefineTopLevelGetSizeRefTyped(code, typeName, config_.features);
} else { } else {
FuncGen::DefineTopLevelGetSizeRef(code, typeName, config_.features); FuncGen::DefineTopLevelGetSizeRef(code, typeName, config_.features);
} }
if (config_.features[Feature::TreeBuilderTypeChecking]) { if (config_.features[Feature::TreeBuilderV2]) {
FuncGen::DefineTreeBuilderInstructions(code, typeName,
calculateExclusiveSize(rootType),
enumerateTypeNames(rootType));
} else if (config_.features[Feature::TreeBuilderTypeChecking]) {
FuncGen::DefineOutputType(code, typeName); FuncGen::DefineOutputType(code, typeName);
} }

View File

@ -80,6 +80,8 @@ class CodeGen {
void genClassTypeHandler(const type_graph::Class& c, std::string& code); void genClassTypeHandler(const type_graph::Class& c, std::string& code);
void genClassStaticType(const type_graph::Class& c, std::string& code); void genClassStaticType(const type_graph::Class& c, std::string& code);
void genClassTraversalFunction(const type_graph::Class& c, std::string& code); void genClassTraversalFunction(const type_graph::Class& c, std::string& code);
void genClassTreeBuilderInstructions(const type_graph::Class& c,
std::string& code);
}; };
} // namespace oi::detail } // namespace oi::detail

View File

@ -265,6 +265,38 @@ ContainerInfo::ContainerInfo(const fs::path& path) {
codegenToml["handler"].value<std::string>()) { codegenToml["handler"].value<std::string>()) {
codegen.handler = std::move(*str); codegen.handler = std::move(*str);
} }
if (std::optional<std::string> str =
codegenToml["traversal_func"].value<std::string>()) {
codegen.traversalFunc = std::move(*str);
}
if (toml::array* arr = codegenToml["processor"].as_array()) {
codegen.processors.reserve(arr->size());
arr->for_each([&](auto&& el) {
if (toml::table* proc = el.as_table()) {
std::string type, func;
if (std::optional<std::string> str =
(*proc)["type"].value<std::string>()) {
type = std::move(*str);
} else {
throw ContainerInfoError(
path, "codegen.processor.type is a required field");
}
if (std::optional<std::string> str =
(*proc)["func"].value<std::string>()) {
func = std::move(*str);
} else {
throw ContainerInfoError(
path, "codegen.processor.func is a required field");
}
codegen.processors.emplace_back(
Processor{std::move(type), std::move(func)});
} else {
throw ContainerInfoError(
path, "codegen.processor should only contain tables");
}
});
}
} }
ContainerInfo::ContainerInfo(std::string typeName_, ContainerInfo::ContainerInfo(std::string typeName_,
@ -275,5 +307,5 @@ ContainerInfo::ContainerInfo(std::string typeName_,
ctype(ctype_), ctype(ctype_),
header(std::move(header_)), header(std::move(header_)),
codegen(Codegen{"// DummyDecl %1%\n", "// DummyFunc %1%\n", codegen(Codegen{"// DummyDecl %1%\n", "// DummyFunc %1%\n",
"// DummyHandler %1%\n"}) { "// DummyHandler %1%\n", "// DummyFunc\n"}) {
} }

View File

@ -27,10 +27,17 @@ ContainerTypeEnum containerTypeEnumFromStr(std::string& str);
const char* containerTypeEnumToStr(ContainerTypeEnum ty); const char* containerTypeEnumToStr(ContainerTypeEnum ty);
struct ContainerInfo { struct ContainerInfo {
struct Processor {
std::string type;
std::string func;
};
struct Codegen { struct Codegen {
std::string decl; std::string decl;
std::string func; std::string func;
std::string handler = ""; std::string handler = "";
std::string traversalFunc = "";
std::vector<Processor> processors{};
}; };
explicit ContainerInfo(const std::filesystem::path& path); // Throws explicit ContainerInfo(const std::filesystem::path& path); // Throws

View File

@ -45,6 +45,8 @@ std::optional<std::string_view> featureHelp(Feature f) {
case Feature::TreeBuilderTypeChecking: case Feature::TreeBuilderTypeChecking:
return "Use Typed Data Segment to perform runtime Type Checking in " return "Use Typed Data Segment to perform runtime Type Checking in "
"TreeBuilder."; "TreeBuilder.";
case Feature::Library:
return std::nullopt; // Hide in OID help
case Feature::TreeBuilderV2: case Feature::TreeBuilderV2:
return "Use Tree Builder v2 for reading the data segment"; return "Use Tree Builder v2 for reading the data segment";
case Feature::GenJitDebug: case Feature::GenJitDebug:
@ -72,6 +74,20 @@ std::span<const Feature> requirements(Feature f) {
case Feature::TreeBuilderV2: case Feature::TreeBuilderV2:
static constexpr std::array tb2 = {Feature::TreeBuilderTypeChecking}; static constexpr std::array tb2 = {Feature::TreeBuilderTypeChecking};
return tb2; return tb2;
case Feature::Library:
static constexpr std::array lib = {Feature::TreeBuilderV2};
return lib;
default:
return {};
}
}
std::span<const Feature> conflicts(Feature f) {
switch (f) {
case Feature::Library:
static constexpr std::array lib = {Feature::JitLogging,
Feature::JitTiming};
return lib;
default: default:
return {}; return {};
} }
@ -147,6 +163,19 @@ std::optional<FeatureSet> handleFeatureConflicts(FeatureSet enabled,
} }
} }
for (const auto f : allFeatures) {
if (!enabled[f])
continue;
for (const auto c : conflicts(f)) {
if (enabled[c]) {
LOG(ERROR) << featureToStr(f) << " feature conflicts with "
<< featureToStr(c) << " but both are enabled!";
return std::nullopt;
}
}
}
return enabled; return enabled;
} }

View File

@ -31,6 +31,7 @@
X(PruneTypeGraph, "prune-type-graph") \ X(PruneTypeGraph, "prune-type-graph") \
X(TypedDataSegment, "typed-data-segment") \ X(TypedDataSegment, "typed-data-segment") \
X(TreeBuilderTypeChecking, "tree-builder-type-checking") \ X(TreeBuilderTypeChecking, "tree-builder-type-checking") \
X(Library, "library") \
X(TreeBuilderV2, "tree-builder-v2") \ X(TreeBuilderV2, "tree-builder-v2") \
X(GenJitDebug, "gen-jit-debug") \ X(GenJitDebug, "gen-jit-debug") \
X(JitLogging, "jit-logging") \ X(JitLogging, "jit-logging") \

View File

@ -147,6 +147,16 @@ void FuncGen::DeclareTopLevelGetSize(std::string& testCode,
boost::format fmt = boost::format("void getSizeType(const %1% &t);\n") % type; boost::format fmt = boost::format("void getSizeType(const %1% &t);\n") % type;
testCode.append(fmt.str()); testCode.append(fmt.str());
} }
void FuncGen::DeclareExterns(std::string& code) {
constexpr std::string_view vars = R"(
extern uint8_t* dataBase;
extern size_t dataSize;
extern uintptr_t cookieValue;
)";
code.append(vars);
}
void FuncGen::DeclareStoreData(std::string& testCode) { void FuncGen::DeclareStoreData(std::string& testCode) {
testCode.append("void StoreData(uintptr_t data, size_t& dataSegOffset);\n"); testCode.append("void StoreData(uintptr_t data, size_t& dataSegOffset);\n");
} }
@ -235,6 +245,35 @@ void FuncGen::DefineTopLevelGetObjectSize(std::string& testCode,
testCode.append(fmt.str()); testCode.append(fmt.str());
} }
void FuncGen::DefineTopLevelIntrospect(std::string& code,
const std::string& type) {
std::string func = R"(
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-attributes"
/* RawType: %1% */
void __attribute__((used, retain)) introspect_%2$016x(
const OIInternal::__ROOT_TYPE__& t,
std::vector<uint8_t>& v)
#pragma GCC diagnostic pop
{
pointers.initialize();
pointers.add((uintptr_t)&t);
v.clear();
v.reserve(4096);
using DataBufferType = DataBuffer::BackInserter<std::vector<uint8_t>>;
using ContentType = OIInternal::TypeHandler<DataBufferType, OIInternal::__ROOT_TYPE__>::type;
ContentType ret{DataBufferType{v}};
OIInternal::getSizeType<DataBufferType>(t, ret);
}
)";
code.append(
(boost::format(func) % type % std::hash<std::string>{}(type)).str());
}
void FuncGen::DefineTopLevelGetSizeRef(std::string& testCode, void FuncGen::DefineTopLevelGetSizeRef(std::string& testCode,
const std::string& rawType, const std::string& rawType,
FeatureSet features) { FeatureSet features) {
@ -373,15 +412,58 @@ void FuncGen::DefineOutputType(std::string& code, const std::string& rawType) {
#pragma GCC diagnostic ignored "-Wunknown-attributes" #pragma GCC diagnostic ignored "-Wunknown-attributes"
/* RawType: %1% */ /* RawType: %1% */
extern const types::dy::Dynamic __attribute__((used, retain)) outputType%2$016x = extern const types::dy::Dynamic __attribute__((used, retain)) outputType%2$016x =
OIInternal::TypeHandler<DataBuffer::DataSegment, OIInternal::__ROOT_TYPE__>::type::describe; OIInternal::TypeHandler<DataBuffer::DataSegment, OIInternal::__ROOT_TYPE__>::type::describe;
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
)"; )";
boost::format fmt = boost::format fmt =
boost::format(func) % rawType % std::hash<std::string>{}(rawType); boost::format(func) % rawType % std::hash<std::string>{}(rawType);
code.append(fmt.str()); code.append(fmt.str());
} }
void FuncGen::DefineTreeBuilderInstructions(
std::string& code,
const std::string& rawType,
size_t exclusiveSize,
std::span<const std::string_view> typeNames) {
std::string typeHash =
(boost::format("%1$016x") % std::hash<std::string>{}(rawType)).str();
code += R"(
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-attributes"
namespace {
const std::array<std::string_view, )";
code += std::to_string(typeNames.size());
code += "> typeNames";
code += typeHash;
code += '{';
for (const auto& name : typeNames) {
code += '"';
code += name;
code += "\",";
}
code += "};\n";
code += "const exporters::inst::Field rootInstructions";
code += typeHash;
code += "{sizeof(OIInternal::__ROOT_TYPE__), ";
code += std::to_string(exclusiveSize);
code += ", \"a0\", typeNames";
code += typeHash;
code +=
", OIInternal::TypeHandler<int, OIInternal::__ROOT_TYPE__>::fields, "
"OIInternal::TypeHandler<int, OIInternal::__ROOT_TYPE__>::processors};\n";
code += "} // namespace\n";
code +=
"extern const exporters::inst::Inst __attribute__((used, retain)) "
"treeBuilderInstructions";
code += typeHash;
code += " = rootInstructions";
code += typeHash;
code += ";\n";
code += "#pragma GCC diagnostic pop\n";
}
void FuncGen::DefineTopLevelGetSizeRefRet(std::string& testCode, void FuncGen::DefineTopLevelGetSizeRefRet(std::string& testCode,
const std::string& rawType) { const std::string& rawType) {
std::string func = R"( std::string func = R"(
@ -564,6 +646,34 @@ void FuncGen::DefineDataSegmentDataBuffer(std::string& testCode) {
testCode.append(func); testCode.append(func);
} }
/*
* DefineBackInserterDataBuffer
*
* Provides a DataBuffer implementation that takes anything convertible with
* std::back_inserter.
*/
void FuncGen::DefineBackInserterDataBuffer(std::string& code) {
constexpr std::string_view buf = R"(
namespace oi::detail::DataBuffer {
template <class Container>
class BackInserter {
public:
BackInserter(Container& v) : buf(v) {}
void write_byte(uint8_t byte) {
*buf = byte;
}
private:
std::back_insert_iterator<Container> buf;
};
} // namespace oi::detail::DataBuffer
)";
code.append(buf);
}
/* /*
* DefineBasicTypeHandlers * DefineBasicTypeHandlers
* *
@ -573,8 +683,8 @@ void FuncGen::DefineDataSegmentDataBuffer(std::string& testCode) {
* pointer's value always, then the value of the pointer if it is unique. void * pointer's value always, then the value of the pointer if it is unique. void
* is of type Unit and always stores nothing. * is of type Unit and always stores nothing.
*/ */
void FuncGen::DefineBasicTypeHandlers(std::string& testCode) { void FuncGen::DefineBasicTypeHandlers(std::string& code, FeatureSet features) {
constexpr std::string_view tHandler = R"( code += R"(
template <typename DB, typename T> template <typename DB, typename T>
struct TypeHandler { struct TypeHandler {
private: private:
@ -582,10 +692,8 @@ void FuncGen::DefineBasicTypeHandlers(std::string& testCode) {
if constexpr(std::is_pointer_v<T>) { if constexpr(std::is_pointer_v<T>) {
return std::type_identity<types::st::Pair<DB, return std::type_identity<types::st::Pair<DB,
types::st::VarInt<DB>, types::st::VarInt<DB>,
types::st::Sum<DB, types::st::Sum<DB, types::st::Unit<DB>, typename TypeHandler<DB, std::remove_pointer_t<T>>::type>
types::st::Unit<DB>, >>();
typename TypeHandler<DB, std::remove_pointer_t<T>>::type
>>>();
} else { } else {
return std::type_identity<types::st::Unit<DB>>(); return std::type_identity<types::st::Unit<DB>>();
} }
@ -593,7 +701,56 @@ void FuncGen::DefineBasicTypeHandlers(std::string& testCode) {
public: public:
using type = typename decltype(choose_type())::type; using type = typename decltype(choose_type())::type;
)";
if (features[Feature::TreeBuilderV2]) {
code += R"(private:
static void process_pointer(result::Element& el, std::stack<inst::Inst>& ins, ParsedData d) {
el.pointer = std::get<ParsedData::VarInt>(d.val).value;
}
static void process_pointer_content(result::Element& el, std::stack<inst::Inst>& ins, ParsedData d) {
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr auto childField = inst::Field{
sizeof(T),
"*",
names,
TypeHandler<DB, T>::fields,
TypeHandler<DB, T>::processors,
};
const ParsedData::Sum& sum = std::get<ParsedData::Sum>(d.val);
el.container_stats.emplace(result::Element::ContainerStats{ .capacity = 1 });
if (sum.index == 0)
return;
el.container_stats->length = 1;
ins.emplace(childField);
}
static constexpr auto choose_fields() {
if constexpr(std::is_pointer_v<T>) {
return std::array<exporters::inst::Field, 0>{};
} else {
return std::array<exporters::inst::Field, 0>{};
}
}
static constexpr auto choose_processors() {
if constexpr(std::is_pointer_v<T>) {
return std::array<inst::ProcessorInst, 2>{
{types::st::VarInt<DB>::describe, &process_pointer},
{types::st::Sum<DB, types::st::Unit<DB>, typename TypeHandler<DB, std::remove_pointer_t<T>>::type>::describe, &process_pointer_content},
};
} else {
return std::array<inst::ProcessorInst, 0>{};
}
}
public:
static constexpr auto fields = choose_fields();
static constexpr auto processors = choose_processors();
)";
}
code += R"(
static types::st::Unit<DB> getSizeType( static types::st::Unit<DB> getSizeType(
const T& t, const T& t,
typename TypeHandler<DB, T>::type returnArg) { typename TypeHandler<DB, T>::type returnArg) {
@ -619,16 +776,75 @@ void FuncGen::DefineBasicTypeHandlers(std::string& testCode) {
}; };
)"; )";
constexpr std::string_view voidHandler = R"( code += R"(
template <typename DB> template <typename DB>
class TypeHandler<DB, void> { class TypeHandler<DB, void> {
public: public:
using type = types::st::Unit<DB>; using type = types::st::Unit<DB>;
}; )";
)"; if (features[Feature::TreeBuilderV2]) {
code +=
"static constexpr std::array<exporters::inst::Field, 0> fields{};\n";
code +=
"static constexpr std::array<exporters::inst::ProcessorInst, 0> "
"processors{};\n";
}
code += "};\n";
}
testCode.append(tHandler); ContainerInfo FuncGen::GetOiArrayContainerInfo() {
testCode.append(voidHandler); ContainerInfo oiArray{"OIArray", UNKNOWN_TYPE,
"cstdint"}; // TODO: remove the need for a dummy header
oiArray.codegen.handler = R"(
template<typename DB, typename T0, long unsigned int N>
struct TypeHandler<DB, %1%<T0, N>> {
using type = types::st::List<DB, typename TypeHandler<DB, T0>::type>;
static types::st::Unit<DB> getSizeType(
const %1%<T0, N> &container,
typename TypeHandler<DB, %1%<T0,N>>::type returnArg) {
auto tail = returnArg.write(N);
for (size_t i=0; i<N; i++) {
tail = tail.delegate([&container, i](auto ret) {
return TypeHandler<DB, T0>::getSizeType(container.vals[i], ret);
});
}
return tail.finish();
}
};
)";
oiArray.codegen.traversalFunc = R"(
auto tail = returnArg.write(N0);
for (size_t i=0; i<N0; i++) {
tail = tail.delegate([&container, i](auto ret) {
return TypeHandler<DB, T0>::getSizeType(container.vals[i], ret);
});
}
return tail.finish();
)";
oiArray.codegen.processors.emplace_back(ContainerInfo::Processor{
.type = "types::st::List<DB, typename TypeHandler<DB, T0>::type>",
.func = R"(
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr auto childField = inst::Field{
sizeof(T0),
"[]",
names,
TypeHandler<DB, T0>::fields,
TypeHandler<DB, T0>::processors,
};
el.exclusive_size = 0;
el.container_stats.emplace(result::Element::ContainerStats{ .capacity = N0, .length = N0 });
auto list = std::get<ParsedData::List>(d.val);
// assert(list.length == N0);
for (size_t i = 0; i < N0; i++)
ins.emplace(childField);
)",
});
return oiArray;
} }
} // namespace oi::detail } // namespace oi::detail

View File

@ -17,6 +17,7 @@
#include <filesystem> #include <filesystem>
#include <map> #include <map>
#include <set> #include <set>
#include <span>
#include <string> #include <string>
#include "oi/ContainerInfo.h" #include "oi/ContainerInfo.h"
@ -28,6 +29,8 @@ namespace oi::detail {
class FuncGen { class FuncGen {
public: public:
static void DeclareExterns(std::string& code);
static void DeclareStoreData(std::string& testCode); static void DeclareStoreData(std::string& testCode);
static void DefineStoreData(std::string& testCode); static void DefineStoreData(std::string& testCode);
@ -56,6 +59,8 @@ class FuncGen {
static void DefineTopLevelGetObjectSize(std::string& testCode, static void DefineTopLevelGetObjectSize(std::string& testCode,
const std::string& type, const std::string& type,
const std::string& linkageName); const std::string& linkageName);
static void DefineTopLevelIntrospect(std::string& code,
const std::string& type);
static void DefineTopLevelGetSizeRef(std::string& testCode, static void DefineTopLevelGetSizeRef(std::string& testCode,
const std::string& rawType, const std::string& rawType,
@ -65,6 +70,11 @@ class FuncGen {
FeatureSet features); FeatureSet features);
static void DefineOutputType(std::string& testCode, static void DefineOutputType(std::string& testCode,
const std::string& rawType); const std::string& rawType);
static void DefineTreeBuilderInstructions(
std::string& testCode,
const std::string& rawType,
size_t exclusiveSize,
std::span<const std::string_view> typeNames);
static void DefineTopLevelGetSizeRefRet(std::string& testCode, static void DefineTopLevelGetSizeRefRet(std::string& testCode,
const std::string& type); const std::string& type);
@ -77,7 +87,10 @@ class FuncGen {
const std::string& ctype); const std::string& ctype);
static void DefineDataSegmentDataBuffer(std::string& testCode); static void DefineDataSegmentDataBuffer(std::string& testCode);
static void DefineBasicTypeHandlers(std::string& testCode); static void DefineBackInserterDataBuffer(std::string& code);
static void DefineBasicTypeHandlers(std::string& code, FeatureSet features);
static ContainerInfo GetOiArrayContainerInfo();
}; };
} // namespace oi::detail } // namespace oi::detail

View File

@ -19,7 +19,10 @@ namespace oi::detail::headers {
// These externs are provided by our build system. See resources/CMakeLists.txt // These externs are provided by our build system. See resources/CMakeLists.txt
extern const std::string_view oi_OITraceCode_cpp; extern const std::string_view oi_OITraceCode_cpp;
extern const std::string_view oi_types_st_h; extern const std::string_view oi_exporters_ParsedData_h;
extern const std::string_view oi_exporters_inst_h;
extern const std::string_view oi_result_Element_h;
extern const std::string_view oi_types_dy_h; extern const std::string_view oi_types_dy_h;
extern const std::string_view oi_types_st_h;
} // namespace oi::detail::headers } // namespace oi::detail::headers

View File

@ -0,0 +1,80 @@
/*
* 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 <oi/IntrospectionResult.h>
#include <oi/exporters/ParsedData.h>
#include <oi/types/dy.h>
#include <cassert>
#include <iterator>
#include <stdexcept>
template <typename T>
inline constexpr bool always_false_v = false;
namespace oi {
IntrospectionResult::const_iterator&
IntrospectionResult::const_iterator::operator++() {
if (stack_.empty()) {
next_ = std::nullopt;
return *this;
}
auto el = stack_.top();
stack_.pop();
return std::visit(
[this](auto&& r) -> IntrospectionResult::const_iterator& {
using U = std::decay_t<decltype(r)>;
if constexpr (std::is_same_v<U, exporters::inst::PopTypePath>) {
type_path_.pop_back();
return operator++();
} else {
// reference wrapper
auto ty = r.get();
using T = std::decay_t<decltype(ty)>;
if constexpr (std::is_same_v<T, exporters::inst::Field>) {
type_path_.emplace_back(ty.name);
stack_.emplace(exporters::inst::PopTypePath{});
next_ = result::Element{
.name = ty.name,
.type_path = type_path_,
.type_names = ty.type_names,
.static_size = ty.static_size,
.exclusive_size = ty.exclusive_size,
.container_stats = std::nullopt,
.is_set_stats = std::nullopt,
};
for (const auto& [dy, handler] : ty.processors) {
auto parsed = exporters::ParsedData::parse(data_, dy);
handler(*next_, stack_, parsed);
}
for (auto it = ty.fields.rbegin(); it != ty.fields.rend(); ++it) {
stack_.emplace(*it);
}
return *this;
} else {
static_assert(always_false_v<T>, "non-exhaustive visitor!");
}
}
},
el);
}
} // namespace oi

View File

@ -2983,6 +2983,7 @@ void OICodeGen::declareThriftStruct(std::string& code, std::string_view name) {
} }
bool OICodeGen::generateJitCode(std::string& code) { bool OICodeGen::generateJitCode(std::string& code) {
FuncGen::DeclareExterns(code);
// Include relevant headers // Include relevant headers
code.append("// relevant header includes -----\n"); code.append("// relevant header includes -----\n");

View File

@ -37,6 +37,7 @@
#include <llvm/Support/TargetSelect.h> #include <llvm/Support/TargetSelect.h>
#include <llvm/Support/raw_os_ostream.h> #include <llvm/Support/raw_os_ostream.h>
#include <array>
#include <boost/range/combine.hpp> #include <boost/range/combine.hpp>
#include <boost/scope_exit.hpp> #include <boost/scope_exit.hpp>
@ -516,21 +517,32 @@ bool OICompiler::compile(const std::string& code,
path.c_str(), clang::frontend::IncludeDirGroup::System, false, false); path.c_str(), clang::frontend::IncludeDirGroup::System, false, false);
} }
if (config.features[Feature::TypedDataSegment]) { static const auto syntheticHeaders = std::array<
std::pair<Feature, std::pair<std::string_view, std::string>>, 5>{{
{Feature::TypedDataSegment, {headers::oi_types_st_h, "oi/types/st.h"}},
{Feature::TreeBuilderTypeChecking,
{headers::oi_types_dy_h, "oi/types/dy.h"}},
{Feature::TreeBuilderV2,
{headers::oi_exporters_inst_h, "oi/exporters/inst.h"}},
{Feature::TreeBuilderV2,
{headers::oi_exporters_ParsedData_h, "oi/exporters/ParsedData.h"}},
{Feature::TreeBuilderV2,
{headers::oi_result_Element_h, "oi/result/Element.h"}},
}};
for (const auto& [k, v] : syntheticHeaders) {
if (!config.features[k])
continue;
compInv->getPreprocessorOpts().addRemappedFile( compInv->getPreprocessorOpts().addRemappedFile(
"/synthetic/headers/oi/types/st.h", std::string{"/synthetic/headers/"} + v.second,
MemoryBuffer::getMemBuffer(headers::oi_types_st_h).release()); MemoryBuffer::getMemBuffer(v.first).release());
headerSearchOptions.AddPath(
"/synthetic/headers", clang::frontend::IncludeDirGroup::IndexHeaderMap,
false, false);
} }
if (config.features[Feature::TreeBuilderTypeChecking]) { for (const auto& [k, _] : syntheticHeaders) {
compInv->getPreprocessorOpts().addRemappedFile( if (config.features[k]) {
"/synthetic/headers/oi/types/dy.h", headerSearchOptions.AddPath(
MemoryBuffer::getMemBuffer(headers::oi_types_dy_h).release()); "/synthetic/headers",
headerSearchOptions.AddPath( clang::frontend::IncludeDirGroup::IndexHeaderMap, false, false);
"/synthetic/headers", clang::frontend::IncludeDirGroup::IndexHeaderMap, break;
false, false); }
} }
compInv->getFrontendOpts().OutputFile = objectPath; compInv->getFrontendOpts().OutputFile = objectPath;

View File

@ -14,49 +14,21 @@
* limitations under the License. * limitations under the License.
*/ */
#include "oi/OILibraryImpl.h" #include "oi/OILibraryImpl.h"
#include "oi/oi-jit.h"
bool debug = false; namespace oi {
namespace ObjectIntrospection { OILibrary::OILibrary(void* atomicHole,
std::unordered_set<Feature> fs,
bool operator==(const options& lhs, const options& rhs) { GeneratorOptions opts)
return lhs.configFilePath == rhs.configFilePath && : pimpl_{std::make_unique<detail::OILibraryImpl>(
lhs.debugFilePath == rhs.debugFilePath && atomicHole, std::move(fs), std::move(opts))} {
lhs.debugLevel == rhs.debugLevel &&
lhs.chaseRawPointers == rhs.chaseRawPointers;
} }
bool operator!=(const options& lhs, const options& rhs) {
return !(lhs == rhs);
}
OILibrary::OILibrary(void* TemplateFunc, options opt) : opts(opt) {
this->pimpl_ = new OILibraryImpl(this, TemplateFunc);
}
OILibrary::~OILibrary() { OILibrary::~OILibrary() {
delete pimpl_;
} }
int OILibrary::init() { std::pair<void*, const exporters::inst::Inst&> OILibrary::init() {
if (!pimpl_->processConfigFile()) { return pimpl_->init();
return Response::OIL_BAD_CONFIG_FILE;
}
if (!pimpl_->mapSegment()) {
return Response::OIL_SEGMENT_INIT_FAIL;
}
pimpl_->initCompiler();
return pimpl_->compileCode();
} }
int OILibrary::getObjectSize(void* ObjectAddr, size_t& size) { } // namespace oi
if (fp == nullptr) {
return Response::OIL_UNINITIALISED;
}
size = (*fp)(ObjectAddr);
return Response::OIL_SUCCESS;
}
} // namespace ObjectIntrospection

View File

@ -13,208 +13,241 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#include "oi/OILibraryImpl.h" #include "OILibraryImpl.h"
#include <fcntl.h>
#include <glog/logging.h> #include <glog/logging.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <unistd.h>
#include <boost/core/demangle.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <cerrno>
#include <cstring>
#include <fstream> #include <fstream>
#include <stdexcept>
#include "oi/DrgnUtils.h"
#include "oi/Headers.h" #include "oi/Headers.h"
#include "oi/OIParser.h"
#include "oi/OIUtils.h" #include "oi/OIUtils.h"
extern "C" { namespace oi::detail {
#include <libelf.h> namespace {
// Map between the high level feature requests in the OIL API and the underlying
// codegen features.
std::map<Feature, bool> convertFeatures(std::unordered_set<oi::Feature> fs);
// Extract the root type from an atomic function pointer
drgn_qualified_type getTypeFromAtomicHole(drgn_program* prog, void* hole);
} // namespace
OILibraryImpl::LocalTextSegment::LocalTextSegment(size_t size) {
void* base = mmap(NULL, size, PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (base == MAP_FAILED)
throw std::runtime_error(std::string("segment map failed: ") +
std::strerror(errno));
data_ = {static_cast<uint8_t*>(base), size};
} }
namespace ObjectIntrospection { OILibraryImpl::LocalTextSegment::~LocalTextSegment() {
if (data_.empty())
return;
using namespace oi::detail; PLOG_IF(ERROR, munmap(data_.data(), data_.size()) != 0)
<< "segment unmap failed";
OILibraryImpl::OILibraryImpl(OILibrary* self, void* TemplateFunc)
: _self(self), _TemplateFunc(TemplateFunc) {
if (_self->opts.debugLevel != 0) {
google::LogToStderr();
google::SetStderrLogging(0);
google::SetVLOGLevel("*", _self->opts.debugLevel);
// Upstream glog defines `GLOG_INFO` as 0 https://fburl.com/ydjajhz0,
// but internally it's defined as 1 https://fburl.com/code/9fwams75
//
// We don't want to link gflags in OIL, so setting it via the flags rather
// than with gflags::SetCommandLineOption
FLAGS_minloglevel = 0;
}
} }
OILibraryImpl::~OILibraryImpl() { OILibraryImpl::MemoryFile::MemoryFile(const char* name) {
unmapSegment(); fd_ = memfd_create(name, 0);
if (fd_ == -1)
throw std::runtime_error(std::string("memfd creation failed: ") +
std::strerror(errno));
} }
bool OILibraryImpl::mapSegment() { OILibraryImpl::MemoryFile::~MemoryFile() {
void* textSeg = if (fd_ == -1)
mmap(NULL, segConfig.textSegSize, PROT_EXEC | PROT_READ | PROT_WRITE, return;
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (textSeg == MAP_FAILED) {
PLOG(ERROR) << "error mapping text segment";
return false;
}
segConfig.textSegBase = textSeg;
return true; PLOG_IF(ERROR, close(fd_) == -1) << "memfd close failed";
} }
bool OILibraryImpl::unmapSegment() { std::filesystem::path OILibraryImpl::MemoryFile::path() {
if (segConfig.textSegBase != nullptr && return {(boost::format("/dev/fd/%1%") % fd_).str()};
munmap(segConfig.textSegBase, segConfig.textSegSize) != 0) {
PLOG(ERROR) << "error unmapping text segment";
return false;
}
return true;
} }
void OILibraryImpl::initCompiler() { OILibraryImpl::OILibraryImpl(void* atomicHole,
symbols = std::make_shared<SymbolService>(getpid()); std::unordered_set<oi::Feature> fs,
GeneratorOptions opts)
generatorConfig.useDataSegment = false; : atomicHole_(atomicHole),
requestedFeatures_(convertFeatures(std::move(fs))),
opts_(std::move(opts)) {
} }
bool OILibraryImpl::processConfigFile() { std::pair<void*, const exporters::inst::Inst&> OILibraryImpl::init() {
auto features = utils::processConfigFile( processConfigFile();
_self->opts.configFilePath,
{ constexpr size_t TextSegSize = 1u << 22;
{Feature::ChaseRawPointers, _self->opts.chaseRawPointers}, textSeg = {TextSegSize};
{Feature::PackStructs, true},
{Feature::PruneTypeGraph, true}, return compileCode();
{Feature::GenJitDebug, _self->opts.generateJitDebugInfo},
},
compilerConfig, generatorConfig);
if (!features) {
return false;
}
generatorConfig.features = *features;
compilerConfig.features = *features;
return true;
} }
template <class T, class F> void OILibraryImpl::processConfigFile() {
class Cleanup { auto features =
T resource; utils::processConfigFile(opts_.configFilePath, requestedFeatures_,
F cleanupFunc; compilerConfig_, generatorConfig_);
if (!features)
throw std::runtime_error("failed to process configuration");
public: generatorConfig_.features = *features;
Cleanup(T _resource, F _cleanupFunc) compilerConfig_.features = *features;
: resource{_resource}, cleanupFunc{_cleanupFunc} {};
~Cleanup() {
cleanupFunc(resource);
}
};
void close_file(std::FILE* fp) {
std::fclose(fp);
} }
int OILibraryImpl::compileCode() { std::pair<void*, const exporters::inst::Inst&> OILibraryImpl::compileCode() {
OICompiler compiler{symbols, compilerConfig}; auto symbols = std::make_shared<SymbolService>(getpid());
int objectMemfd = memfd_create("oil_object_code", 0); auto* prog = symbols->getDrgnProgram();
if (!objectMemfd) { CHECK(prog != nullptr) << "does this check need to exist?";
PLOG(ERROR) << "failed to create memfd for object code";
return Response::OIL_COMPILATION_FAILURE;
}
using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>; auto rootType = getTypeFromAtomicHole(symbols->getDrgnProgram(), atomicHole_);
unique_file_t objectStream(fdopen(objectMemfd, "w+"), &close_file);
if (!objectStream) {
PLOG(ERROR) << "failed to convert memfd to stream";
// This only needs to be cleaned up in the error case, as the fclose
// on the unique_file_t will clean up the underlying fd if it was
// created successfully.
close(objectMemfd);
return Response::OIL_COMPILATION_FAILURE;
}
auto objectPath =
fs::path((boost::format("/dev/fd/%1%") % objectMemfd).str());
struct drgn_program* prog = symbols->getDrgnProgram(); CodeGen codegen{generatorConfig_, *symbols};
if (!prog) {
return Response::OIL_COMPILATION_FAILURE;
}
struct drgn_symbol* sym;
if (auto err = drgn_program_find_symbol_by_address(
prog, (uintptr_t)_TemplateFunc, &sym)) {
LOG(ERROR) << "Error when finding symbol by address " << err->code << " "
<< err->message;
drgn_error_destroy(err);
return Response::OIL_COMPILATION_FAILURE;
}
const char* name = drgn_symbol_name(sym);
drgn_symbol_destroy(sym);
// TODO: change this to the new drgn interface from symbol -> type std::string code;
auto rootType = symbols->getRootType(irequest{"entry", name, "arg0"}); if (!codegen.codegenFromDrgn(rootType.type, code))
if (!rootType.has_value()) { throw std::runtime_error("oil jit codegen failed!");
LOG(ERROR) << "Failed to get type of probe argument";
return Response::OIL_COMPILATION_FAILURE;
}
std::string code(headers::oi_OITraceCode_cpp); std::string sourcePath = opts_.sourceFileDumpPath;
if (sourcePath.empty()) {
auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols); sourcePath = "oil_jit.cpp"; // fake path for JIT debug info
if (!codegen) {
return OIL_COMPILATION_FAILURE;
}
codegen->setRootType(rootType->type);
if (!codegen->generate(code)) {
return Response::OIL_COMPILATION_FAILURE;
}
std::string sourcePath = _self->opts.sourceFileDumpPath;
if (_self->opts.sourceFileDumpPath.empty()) {
// This is the path Clang acts as if it has compiled from e.g. for debug
// information. It does not need to exist.
sourcePath = "oil_jit.cpp";
} else { } else {
std::ofstream outputFile(sourcePath); std::ofstream outputFile(sourcePath);
outputFile << code; outputFile << code;
} }
if (!compiler.compile(code, sourcePath, objectPath)) { auto object = MemoryFile("oil_object_code");
return Response::OIL_COMPILATION_FAILURE; OICompiler compiler{symbols, compilerConfig_};
} if (!compiler.compile(code, sourcePath, object.path()))
throw std::runtime_error("oil jit compilation failed!");
auto relocRes = compiler.applyRelocs( auto relocRes = compiler.applyRelocs(
reinterpret_cast<uint64_t>(segConfig.textSegBase), {objectPath}, {}); reinterpret_cast<uint64_t>(textSeg.data().data()), {object.path()}, {});
if (!relocRes.has_value()) { if (!relocRes)
return Response::OIL_RELOCATION_FAILURE; throw std::runtime_error("oil jit relocation failed!");
}
const auto& [_, segments, jitSymbols] = relocRes.value(); const auto& [_, segments, jitSymbols] = *relocRes;
// Locate the probe's entry point std::string nameHash =
_self->fp = nullptr; (boost::format("%1$016x") %
std::hash<std::string>{}(SymbolService::getTypeName(rootType.type)))
.str();
std::string functionSymbolPrefix = "_Z27introspect_" + nameHash;
std::string typeSymbolName = "treeBuilderInstructions" + nameHash;
void* fp = nullptr;
const exporters::inst::Inst* ty = nullptr;
for (const auto& [symName, symAddr] : jitSymbols) { for (const auto& [symName, symAddr] : jitSymbols) {
if (symName.starts_with("_Z7getSize")) { if (fp == nullptr && symName.starts_with(functionSymbolPrefix)) {
_self->fp = (size_t(*)(const void*))symAddr; fp = reinterpret_cast<void*>(symAddr);
break; if (ty != nullptr)
break;
} else if (ty == nullptr && symName == typeSymbolName) {
ty = reinterpret_cast<const exporters::inst::Inst*>(symAddr);
if (fp != nullptr)
break;
} }
} }
if (!_self->fp) {
return Response::OIL_RELOCATION_FAILURE;
}
// Copy relocated segments in their final destination CHECK(fp != nullptr && ty != nullptr)
for (const auto& [BaseAddr, RelocAddr, Size] : segments) << "failed to find always present symbols!";
memcpy((void*)RelocAddr, (void*)BaseAddr, Size);
return Response::OIL_SUCCESS; for (const auto& [baseAddr, relocAddr, size] : segments)
std::memcpy(reinterpret_cast<void*>(relocAddr),
reinterpret_cast<void*>(baseAddr), size);
textSeg.release(); // don't munmap() the region containing the code
return {fp, *ty};
} }
} // namespace ObjectIntrospection namespace {
std::map<Feature, bool> convertFeatures(std::unordered_set<oi::Feature> fs) {
std::map<Feature, bool> out{
{Feature::TypeGraph, true},
{Feature::TypedDataSegment, true},
{Feature::TreeBuilderTypeChecking, true},
{Feature::TreeBuilderV2, true},
{Feature::Library, true},
{Feature::PackStructs, true},
{Feature::PruneTypeGraph, true},
};
for (const auto f : fs) {
switch (f) {
case oi::Feature::ChaseRawPointers:
out[Feature::ChaseRawPointers] = true;
break;
case oi::Feature::CaptureThriftIsset:
out[Feature::CaptureThriftIsset] = true;
break;
case oi::Feature::GenJitDebug:
out[Feature::GenJitDebug] = true;
break;
}
}
return out;
}
drgn_qualified_type getTypeFromAtomicHole(drgn_program* prog, void* hole) {
// get the getter type:
// std::atomic<std::vector<uint8_t> (*)(const T&)>& getIntrospectionFunc();
auto atomicGetterType =
SymbolService::findTypeOfAddr(prog, reinterpret_cast<uintptr_t>(hole));
if (!atomicGetterType)
throw std::runtime_error("failed to lookup function");
// get the return type:
// std::atomic<std::vector<uint8_t> (*)(const T&)>&
CHECK(drgn_type_has_type(atomicGetterType->type))
<< "functions have a return type";
auto retType = drgn_type_type(atomicGetterType->type);
// get the atomic type:
// std::atomic<std::vector<uint8_t> (*)(const T&)>
CHECK(drgn_type_has_type(retType.type)) << "pointers have a value type";
auto atomicType = drgn_type_type(retType.type);
// get the function pointer type:
// std::vector<uint8_t> (*)(const T&)
CHECK(drgn_type_has_template_parameters(atomicType.type))
<< "atomic should have template parameters";
CHECK(drgn_type_num_template_parameters(atomicType.type) == 1)
<< "atomic should have 1 template parameter";
auto* templateParam = drgn_type_template_parameters(atomicType.type);
struct drgn_qualified_type funcPointerType;
if (auto err = drgn_template_parameter_type(templateParam, &funcPointerType))
throw drgnplusplus::error(err);
// get the function type:
// std::vector<uint8_t>(const T&)
CHECK(drgn_type_has_type(funcPointerType.type))
<< "function pointers have a value type";
auto funcType = drgn_type_type(funcPointerType.type);
// get the argument type:
// const T&
CHECK(drgn_type_has_parameters(funcType.type)) << "functions have parameters";
CHECK(drgn_type_num_parameters(funcType.type) == 2)
<< "function should have 2 parameters";
drgn_qualified_type argType;
if (auto err =
drgn_parameter_type(drgn_type_parameters(funcType.type), &argType))
throw drgnplusplus::error(err);
// get the type
CHECK(drgn_type_has_type(argType.type))
<< "reference types have a value type";
return drgn_type_type(argType.type);
}
} // namespace
} // namespace oi::detail

View File

@ -14,38 +14,76 @@
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <oi/oi.h>
#include "ObjectIntrospection.h" #include <filesystem>
#include "oi/OICodeGen.h" #include <map>
#include <span>
#include <unordered_set>
#include <utility>
#include "oi/CodeGen.h"
#include "oi/Features.h"
#include "oi/OICompiler.h" #include "oi/OICompiler.h"
#include "oi/SymbolService.h"
namespace ObjectIntrospection { namespace oi::detail {
class OILibraryImpl { class OILibraryImpl {
public: private:
OILibraryImpl(OILibrary*, void*); class LocalTextSegment {
~OILibraryImpl(); public:
LocalTextSegment() = default;
LocalTextSegment(size_t size);
~LocalTextSegment();
LocalTextSegment(const LocalTextSegment&) = delete;
LocalTextSegment& operator=(const LocalTextSegment&) = delete;
LocalTextSegment(LocalTextSegment&& that) {
std::swap(this->data_, that.data_);
}
LocalTextSegment& operator=(LocalTextSegment&& that) {
std::swap(this->data_, that.data_);
return *this;
}
bool mapSegment(); std::span<uint8_t> data() {
bool unmapSegment(); return data_;
void initCompiler(); }
int compileCode(); void release() {
bool processConfigFile(); data_ = {};
void enableLayoutAnalysis(); }
private:
std::span<uint8_t> data_;
};
class MemoryFile {
public:
MemoryFile(const char* name);
~MemoryFile();
std::filesystem::path path();
private:
int fd_ = -1;
};
public:
OILibraryImpl(void* atomicHole,
std::unordered_set<oi::Feature> fs,
GeneratorOptions opts);
std::pair<void*, const exporters::inst::Inst&> init();
private: private:
class OILibrary* _self; void* atomicHole_;
std::map<Feature, bool> requestedFeatures_;
GeneratorOptions opts_;
void* _TemplateFunc; oi::detail::OICompiler::Config compilerConfig_{};
oi::detail::OICodeGen::Config generatorConfig_{};
oi::detail::OICompiler::Config compilerConfig{}; LocalTextSegment textSeg;
oi::detail::OICodeGen::Config generatorConfig{};
std::shared_ptr<oi::detail::SymbolService> symbols{};
struct c { void processConfigFile();
void* textSegBase = nullptr; std::pair<void*, const exporters::inst::Inst&> compileCode();
size_t textSegSize = 1u << 22;
} segConfig;
}; };
} // namespace ObjectIntrospection
} // namespace oi::detail

View File

@ -34,12 +34,6 @@
#define C10_USING_CUSTOM_GENERATED_MACROS #define C10_USING_CUSTOM_GENERATED_MACROS
// These globals are set by oid, see end of OIDebugger::compileCode()
extern uint8_t* dataBase;
extern size_t dataSize;
extern uintptr_t cookieValue;
extern int logFile;
constexpr int oidMagicId = 0x01DE8; constexpr int oidMagicId = 0x01DE8;
#include <array> #include <array>
@ -89,21 +83,6 @@ class {
} }
} static pointers; } static pointers;
void __jlogptr(uintptr_t ptr) {
static constexpr char hexdigits[] = "0123456789abcdef";
static constexpr size_t ptrlen = 2 * sizeof(ptr);
static char hexstr[ptrlen + 1] = {};
size_t i = ptrlen;
while (i--) {
hexstr[i] = hexdigits[ptr & 0xf];
ptr = ptr >> 4;
}
hexstr[ptrlen] = '\n';
write(logFile, hexstr, sizeof(hexstr));
}
} // namespace } // namespace
// alignas(0) is ignored according to docs so can be default // alignas(0) is ignored according to docs so can be default

View File

@ -298,7 +298,7 @@ bool SymbolService::loadModules() {
return true; return true;
} }
static std::optional<drgn_qualified_type> findTypeOfSymbol( std::optional<drgn_qualified_type> SymbolService::findTypeOfSymbol(
drgn_program* prog, const std::string& symbolName) { drgn_program* prog, const std::string& symbolName) {
drgn_symbol* sym; drgn_symbol* sym;
if (auto* err = if (auto* err =
@ -313,6 +313,16 @@ static std::optional<drgn_qualified_type> findTypeOfSymbol(
uint64_t addr = drgn_symbol_address(sym); uint64_t addr = drgn_symbol_address(sym);
drgn_symbol_destroy(sym); drgn_symbol_destroy(sym);
if (auto t = findTypeOfAddr(prog, addr)) {
return t;
} else {
LOG(ERROR) << "Failed to lookup symbol '" << symbolName;
return std::nullopt;
}
}
std::optional<drgn_qualified_type> SymbolService::findTypeOfAddr(
drgn_program* prog, uintptr_t addr) {
drgn_object obj; drgn_object obj;
drgn_object_init(&obj, prog); drgn_object_init(&obj, prog);
@ -320,7 +330,7 @@ static std::optional<drgn_qualified_type> findTypeOfSymbol(
if (auto* err = if (auto* err =
drgn_program_find_function_by_address(prog, addr, &name, &obj); drgn_program_find_function_by_address(prog, addr, &name, &obj);
err != nullptr) { err != nullptr) {
LOG(ERROR) << "Failed to lookup function '" << symbolName LOG(ERROR) << "Failed to lookup function '" << reinterpret_cast<void*>(addr)
<< "': " << err->code << " " << err->message; << "': " << err->code << " " << err->message;
drgn_error_destroy(err); drgn_error_destroy(err);
return std::nullopt; return std::nullopt;
@ -598,7 +608,7 @@ static std::optional<std::shared_ptr<FuncDesc>> createFuncDesc(
struct drgn_program* prog, const irequest& request) { struct drgn_program* prog, const irequest& request) {
VLOG(1) << "Creating function description for: " << request.func; VLOG(1) << "Creating function description for: " << request.func;
auto ft = findTypeOfSymbol(prog, request.func); auto ft = SymbolService::findTypeOfSymbol(prog, request.func);
if (!ft) { if (!ft) {
return std::nullopt; return std::nullopt;
} }

View File

@ -56,6 +56,11 @@ class SymbolService {
static std::string getTypeName(struct drgn_type*); static std::string getTypeName(struct drgn_type*);
std::optional<RootInfo> getRootType(const irequest&); std::optional<RootInfo> getRootType(const irequest&);
static std::optional<drgn_qualified_type> findTypeOfSymbol(
drgn_program*, const std::string& symbolName);
static std::optional<drgn_qualified_type> findTypeOfAddr(drgn_program*,
uintptr_t addr);
std::unordered_map<std::string, std::shared_ptr<FuncDesc>> funcDescs; std::unordered_map<std::string, std::shared_ptr<FuncDesc>> funcDescs;
std::unordered_map<std::string, std::shared_ptr<GlobalDesc>> globalDescs; std::unordered_map<std::string, std::shared_ptr<GlobalDesc>> globalDescs;

125
oi/exporters/Json.cpp Normal file
View File

@ -0,0 +1,125 @@
/*
* 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 <oi/exporters/Json.h>
#include <stdexcept>
template <class>
inline constexpr bool always_false_v = false;
namespace oi::exporters {
namespace {
template <typename It>
void printStringList(std::ostream& out, It it, It end, bool pretty) {
out << '[';
for (; it != end; ++it) {
out << '"' << (*it) << '"';
if (it != end - 1)
out << (pretty ? ", " : ",");
}
out << ']';
}
std::string makeIndent(std::vector<std::string_view>& tp) {
return std::string((tp.size() - 1) * 4, ' ');
}
} // namespace
Json::Json(std::ostream& out) : out_(out) {
}
void Json::print(const IntrospectionResult& r) {
auto begin = r.cbegin();
return print(begin, r.cend());
}
void Json::print(IntrospectionResult::const_iterator& it,
IntrospectionResult::const_iterator end) {
std::vector<std::string_view> firstTypePath = it->type_path;
const auto indent = pretty_ ? makeIndent(firstTypePath) : "";
const auto* tab = pretty_ ? " " : "";
const auto* space = pretty_ ? " " : "";
const auto* endl = pretty_ ? "\n" : "";
out_ << '[';
out_ << endl << indent;
bool first = true;
while (it != end) {
if (it->type_path.size() < firstTypePath.size()) {
// no longer a sibling, must be a sibling of the type we're printing
break;
}
if (!first)
out_ << ',' << endl << indent;
first = false;
out_ << '{' << endl << indent;
out_ << tab << "\"name\"" << space << ':' << space << "\"" << it->name
<< "\"," << endl
<< indent;
out_ << tab << "\"typePath\"" << space << ':' << space << "";
printStringList(out_, it->type_path.begin(), it->type_path.end(), pretty_);
out_ << (pretty_ ? ",\n" : ",") << indent;
out_ << tab << "\"typeNames\"" << space << ':' << space;
printStringList(out_, it->type_names.begin(), it->type_names.end(),
pretty_);
out_ << ',' << endl << indent;
out_ << tab << "\"staticSize\":" << space << it->static_size << ',' << endl
<< indent;
out_ << tab << "\"exclusiveSize\":" << space << it->exclusive_size << ','
<< endl
<< indent;
if (it->pointer.has_value()) {
out_ << tab << "\"pointer\":" << space << *(it->pointer) << ',' << endl
<< indent;
}
if (it->container_stats.has_value()) {
out_ << tab << "\"length\":" << space << it->container_stats->length
<< ',' << endl
<< indent;
out_ << tab << "\"capacity\":" << space << it->container_stats->capacity
<< ',' << endl
<< indent;
}
if (it->is_set_stats.has_value()) {
out_ << tab << "\"is_set\":" << space << it->is_set_stats->is_set << ','
<< endl
<< indent;
}
out_ << tab << "\"members\":" << space;
if ((++it)->type_path.size() > firstTypePath.size()) {
print(it, end);
} else {
out_ << "[]" << endl << indent;
}
out_ << "}";
}
out_ << endl << indent << ']' << endl;
}
} // namespace oi::exporters

View File

@ -0,0 +1,76 @@
/*
* 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 <oi/exporters/ParsedData.h>
#include <cassert>
#include <stdexcept>
#include <type_traits>
template <typename T>
constexpr bool always_false_v = false;
namespace oi::exporters {
namespace {
uint64_t parseVarint(std::vector<uint8_t>::const_iterator& it);
}
ParsedData ParsedData::parse(std::vector<uint8_t>::const_iterator& it,
types::dy::Dynamic dy) {
return std::visit(
[&it](const auto el) -> ParsedData {
auto ty = el.get();
using T = std::decay_t<decltype(ty)>;
if constexpr (std::is_same_v<T, types::dy::Unit>) {
return ParsedData::Unit{};
} else if constexpr (std::is_same_v<T, types::dy::VarInt>) {
return ParsedData::VarInt{.value = parseVarint(it)};
} else if constexpr (std::is_same_v<T, types::dy::Pair>) {
return ParsedData::Pair{
.first = Lazy{it, ty.first},
.second = Lazy{it, ty.second},
};
} else if constexpr (std::is_same_v<T, types::dy::List>) {
return ParsedData::List{
.length = parseVarint(it),
.values = {it, ty.element},
};
} else if constexpr (std::is_same_v<T, types::dy::Sum>) {
auto index = parseVarint(it);
assert(index < ty.variants.size());
return ParsedData::Sum{
.index = parseVarint(it),
.value = {it, ty.variants[index]},
};
} else {
static_assert(always_false_v<T>, "non-exhaustive visitor!");
}
},
dy);
}
namespace {
uint64_t parseVarint(std::vector<uint8_t>::const_iterator& it) {
uint64_t v = 0;
int shift = 0;
while (*it >= 0x80) {
v |= static_cast<uint64_t>(*it++ & 0x7f) << shift;
shift += 7;
}
v |= static_cast<uint64_t>(*it++ & 0x7f) << shift;
return v;
}
} // namespace
} // namespace oi::exporters

View File

@ -71,6 +71,8 @@ void NameGen::visit(Class& c) {
std::string name = c.name(); std::string name = c.name();
removeTemplateParams(name); removeTemplateParams(name);
deduplicate(name); deduplicate(name);
if (c.name().empty())
c.setInputName(name);
c.setName(name); c.setName(name);
// Deduplicate member names. Duplicates may be present after flattening. // Deduplicate member names. Duplicates may be present after flattening.
@ -151,6 +153,8 @@ void NameGen::visit(Container& c) {
void NameGen::visit(Enum& e) { void NameGen::visit(Enum& e) {
std::string name = e.name(); std::string name = e.name();
deduplicate(name); deduplicate(name);
if (e.name().empty())
e.setInputName(name);
e.setName(name); e.setName(name);
} }

View File

@ -40,7 +40,6 @@ namespace {
void print(const TypeGraph& typeGraph) { void print(const TypeGraph& typeGraph) {
if (!VLOG_IS_ON(1)) if (!VLOG_IS_ON(1))
return; return;
std::stringstream out; std::stringstream out;
Printer printer{out, typeGraph.resetTracker(), typeGraph.size()}; Printer printer{out, typeGraph.resetTracker(), typeGraph.size()};
for (const auto& type : typeGraph.rootTypes()) { for (const auto& type : typeGraph.rootTypes()) {

View File

@ -231,6 +231,10 @@ class Class : public Type {
name_ = std::move(name); name_ = std::move(name);
} }
void setInputName(std::string name) {
inputName_ = std::move(name);
}
virtual size_t size() const override { virtual size_t size() const override {
return size_; return size_;
} }
@ -376,6 +380,10 @@ class Enum : public Type {
return inputName_; return inputName_;
} }
void setInputName(std::string name) {
inputName_ = std::move(name);
}
void setName(std::string name) { void setName(std::string name) {
name_ = std::move(name); name_ = std::move(name);
} }

View File

@ -7,6 +7,9 @@ function(embed_headers output)
file(APPEND ${output} "namespace oi::detail::headers {\n") file(APPEND ${output} "namespace oi::detail::headers {\n")
set(HEADERS set(HEADERS
../include/oi/exporters/ParsedData.h
../include/oi/exporters/inst.h
../include/oi/result/Element.h
../include/oi/types/dy.h ../include/oi/types/dy.h
../include/oi/types/st.h ../include/oi/types/st.h
../oi/OITraceCode.cpp ../oi/OITraceCode.cpp

View File

@ -50,7 +50,7 @@ add_executable(integration_test_target
${INTEGRATION_TEST_TARGET_SRC} ${INTEGRATION_TEST_TARGET_SRC}
folly_shims.cpp) folly_shims.cpp)
target_compile_options(integration_test_target PRIVATE -O1) target_compile_options(integration_test_target PRIVATE -O1)
target_link_libraries(integration_test_target PRIVATE oil Boost::headers ${Boost_LIBRARIES}) target_link_libraries(integration_test_target PRIVATE oil_jit exporters_json Boost::headers ${Boost_LIBRARIES})
add_executable(integration_test_runner ${INTEGRATION_TEST_RUNNER_SRC} runner_common.cpp) add_executable(integration_test_runner ${INTEGRATION_TEST_RUNNER_SRC} runner_common.cpp)
target_include_directories(integration_test_runner PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(integration_test_runner PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -56,6 +56,12 @@ definitions = '''
{"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [ {"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1} {"typeName": "char", "staticSize": 1, "exclusiveSize": 1}
]}]}]''' ]}]}]'''
expect_json_v2 = '''[
{"staticSize": 32, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["Align16"], "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}
]}]}]'''
[cases.container_struct] [cases.container_struct]
skip = "container alignment is broken (#143)" skip = "container alignment is broken (#143)"
param_types = ["const std::optional<Align16>&"] param_types = ["const std::optional<Align16>&"]
@ -78,6 +84,16 @@ definitions = '''
{"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [ {"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1}]} {"typeName": "char", "staticSize": 1, "exclusiveSize": 1}]}
]}]}]''' ]}]}]'''
expect_json_v2 = '''[
{"staticSize": 64, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["TwoStruct"], "staticSize": 48, "exclusiveSize": 15, "members": [
{"typeNames": ["Align16"], "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}]},
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["Align16"], "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}]}
]}]}]'''
[cases.container_two_members] [cases.container_two_members]
skip = "container alignment is broken (#143)" skip = "container alignment is broken (#143)"
param_types = ["const std::optional<TwoStruct>&"] param_types = ["const std::optional<TwoStruct>&"]
@ -101,6 +117,13 @@ definitions = '''
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1}, {"typeName": "char", "staticSize": 1, "exclusiveSize": 1},
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1} {"typeName": "char", "staticSize": 1, "exclusiveSize": 1}
]}]}]''' ]}]}]'''
expect_json_v2 = '''[
{"staticSize": 96, "exclusiveSize": 31, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["MemberAlignment"], "staticSize": 64, "exclusiveSize": 62, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}
]}]}]'''
[cases.container_member_alignment] [cases.container_member_alignment]
skip = "container alignment is broken (#143)" skip = "container alignment is broken (#143)"
param_types = ["const std::optional<MemberAlignment>&"] param_types = ["const std::optional<MemberAlignment>&"]
@ -119,6 +142,11 @@ definitions = '''
{"typeName": "int8_t", "staticSize": 1, "exclusiveSize": 1}, {"typeName": "int8_t", "staticSize": 1, "exclusiveSize": 1},
{"typeName": "UnionMember", "staticSize": 32, "exclusiveSize": 32, "NOT":"members"} {"typeName": "UnionMember", "staticSize": 32, "exclusiveSize": 32, "NOT":"members"}
]}]''' ]}]'''
expect_json_v2 = '''[
{"staticSize": 64, "exclusiveSize": 31, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["UnionMember"], "staticSize": 32, "exclusiveSize": 32, "members":[]}
]}]'''
[cases.container_union_member] [cases.container_union_member]
skip = "container alignment is broken (#143)" skip = "container alignment is broken (#143)"
param_types = ["const std::optional<UnionMember>&"] param_types = ["const std::optional<UnionMember>&"]
@ -138,6 +166,14 @@ definitions = '''
{"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [ {"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1} {"typeName": "char", "staticSize": 1, "exclusiveSize": 1}
]}]}]}]''' ]}]}]}]'''
expect_json_v2 = '''[
{"staticSize": 96, "exclusiveSize": 31, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["MemberAlignmentOverriden"], "staticSize": 64, "exclusiveSize": 47, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["Align16"], "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}
]}]}]}]'''
[cases.container_member_override] [cases.container_member_override]
skip = "container alignment is broken (#143)" skip = "container alignment is broken (#143)"
param_types = ["const std::optional<MemberAlignmentOverriden>&"] param_types = ["const std::optional<MemberAlignmentOverriden>&"]
@ -159,6 +195,13 @@ definitions = '''
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1}, {"typeName": "char", "staticSize": 1, "exclusiveSize": 1},
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1} {"typeName": "char", "staticSize": 1, "exclusiveSize": 1}
]}]}]''' ]}]}]'''
expect_json_v2 = '''[
{"staticSize": 256, "exclusiveSize": 127, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["AlignedStructMemberAlignLower"], "staticSize": 128, "exclusiveSize": 126, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}
]}]}]'''
[cases.container_member_lower] [cases.container_member_lower]
skip = "container alignment is broken (#143)" skip = "container alignment is broken (#143)"
param_types = ["const std::optional<AlignedStructMemberAlignLower>&"] param_types = ["const std::optional<AlignedStructMemberAlignLower>&"]

View File

@ -192,6 +192,7 @@ definitions = '''
}]''' }]'''
[cases.anon_union] [cases.anon_union]
oil_skip = "anonymous unions are fully stubbed in the generated code" # https://github.com/facebookexperimental/object-introspection/issues/292
param_types = ["const AnonUnionContainer&"] param_types = ["const AnonUnionContainer&"]
setup = 'return AnonUnionContainer{ .a = 3 };' setup = 'return AnonUnionContainer{ .a = 3 };'
cli_options = ["-fchase-raw-pointers"] cli_options = ["-fchase-raw-pointers"]
@ -204,6 +205,15 @@ definitions = '''
{"name":"e", "staticSize":4, "dynamicSize":0, "typeName":"int"} {"name":"e", "staticSize":4, "dynamicSize":0, "typeName":"int"}
] ]
}]''' }]'''
expect_json_v2 = '''[{
"staticSize": 24,
"exclusiveSize": 10,
"members": [
{"name":"__anon_member_0", "staticSize":2, "exclusiveSize":2},
{"name":"__anon_member_1", "staticSize":8, "exclusiveSize":8},
{"name":"e", "staticSize":4, "exclusiveSize":4, "typeNames":["int32_t"]}
]
}]'''
[cases.nested_anon_struct] [cases.nested_anon_struct]
oil_disable = "oil can't chase raw pointers safely" oil_disable = "oil can't chase raw pointers safely"

View File

@ -30,7 +30,17 @@ definitions = '''
"capacity":10, "capacity":10,
"elementStaticSize":4 "elementStaticSize":4
}]}]''' }]}]'''
expect_json_v2 = '''[{
"staticSize":40,
"exclusiveSize":0,
"members":[{
"staticSize":40,
"exclusiveSize":0,
"length":10,
"capacity":10
}]}]'''
[cases.member_int0] [cases.member_int0]
oil_skip = 'zero length arrays fail codegen v2' # https://github.com/facebookexperimental/object-introspection/issues/295
# WARNING: zero-length arrays are handled differently to non-empty arrays. # WARNING: zero-length arrays are handled differently to non-empty arrays.
# They end up not being treated as containers. This should probably change # They end up not being treated as containers. This should probably change
# in the future. # in the future.
@ -43,8 +53,16 @@ definitions = '''
"staticSize":0, "staticSize":0,
"dynamicSize":0 "dynamicSize":0
}]}]''' }]}]'''
expect_json_v2 = '''[{
"staticSize":1,
"exclusiveSize":1,
"members":[{
"staticSize":0,
"exclusiveSize":0
}]}]'''
[cases.multidim_legacy] # Test for legacy behaviour. Remove with OICodeGen [cases.multidim_legacy] # Test for legacy behaviour. Remove with OICodeGen
cli_options = ["-Ftype-graph", "-Ftyped-data-segment", "-Ftree-builder-type-checking"] oil_disable = 'oil only runs on codegen v2'
cli_options = ["-Ftype-graph", "-Ftyped-data-segment", "-Ftree-builder-type-checking", "-Ftree-builder-v2"]
param_types = ["const MultiDim&"] param_types = ["const MultiDim&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -67,6 +85,12 @@ definitions = '''
{"staticSize":12, "dynamicSize":0, "exclusiveSize":12, "length":3, "capacity":3, "elementStaticSize":4}, {"staticSize":12, "dynamicSize":0, "exclusiveSize":12, "length":3, "capacity":3, "elementStaticSize":4},
{"staticSize":12, "dynamicSize":0, "exclusiveSize":12, "length":3, "capacity":3, "elementStaticSize":4}] {"staticSize":12, "dynamicSize":0, "exclusiveSize":12, "length":3, "capacity":3, "elementStaticSize":4}]
}]}]''' }]}]'''
expect_json_v2 = '''[
{"staticSize":24, "exclusiveSize":0, "members":[
{"staticSize":24, "exclusiveSize":0, "length":2, "capacity":2, "members":[
{"staticSize":12, "exclusiveSize":0, "length":3, "capacity":3},
{"staticSize":12, "exclusiveSize":0, "length":3, "capacity":3}]
}]}]'''
[cases.direct_int10] [cases.direct_int10]
skip = "Direct array arguments don't work" skip = "Direct array arguments don't work"
param_types = ["int[10]"] param_types = ["int[10]"]

View File

@ -154,6 +154,7 @@ definitions = '''
''' '''
[cases.unique_ptr] [cases.unique_ptr]
oil_skip = "cycles are broken" # https://github.com/facebookexperimental/object-introspection/issues/293
param_types = ["std::reference_wrapper<UniqueNode>&"] param_types = ["std::reference_wrapper<UniqueNode>&"]
setup = ''' setup = '''
auto first = std::make_unique<UniqueNode>(); auto first = std::make_unique<UniqueNode>();
@ -231,6 +232,7 @@ definitions = '''
''' '''
[cases.shared_ptr] [cases.shared_ptr]
oil_skip = "cycles are broken" # https://github.com/facebookexperimental/object-introspection/issues/293
param_types = ["std::reference_wrapper<SharedNode>&"] param_types = ["std::reference_wrapper<SharedNode>&"]
setup = ''' setup = '''
auto first = std::make_shared<SharedNode>(); auto first = std::make_shared<SharedNode>();

View File

@ -38,7 +38,8 @@ definitions = '''
[cases.scoped_enum_val_cast] [cases.scoped_enum_val_cast]
param_types = ["const std::array<int, static_cast<size_t>(MyNS::ScopedEnum::Two)>&"] param_types = ["const std::array<int, static_cast<size_t>(MyNS::ScopedEnum::Two)>&"]
setup = "return {};" setup = "return {};"
expect_json = '[{"staticSize":8, "dynamicSize":0, "length":2, "capacity":2, "elementStaticSize":4}]' expect_json = '[{"staticSize":8, "length":2, "capacity":2, "elementStaticSize":4}]'
expect_json_v2 = '[{"staticSize":8, "dynamicSize":0, "length":2, "capacity":2}]'
[cases.scoped_enum_val] [cases.scoped_enum_val]
param_types = ["const MyClass<MyNS::ScopedEnum::One>&"] param_types = ["const MyClass<MyNS::ScopedEnum::One>&"]
@ -59,4 +60,5 @@ definitions = '''
[cases.unscoped_enum_val_cast] [cases.unscoped_enum_val_cast]
param_types = ["const std::array<int, MyNS::ONE>&"] param_types = ["const std::array<int, MyNS::ONE>&"]
setup = "return {};" setup = "return {};"
expect_json = '[{"staticSize":4, "dynamicSize":0, "length":1, "capacity":1, "elementStaticSize":4}]' expect_json = '[{"staticSize":4, "length":1, "capacity":1, "elementStaticSize":4}]'
expect_json_v2 = '[{"staticSize":4, "length":1, "capacity":1}]'

View File

@ -1,6 +1,7 @@
includes = ["folly/FBString.h"] includes = ["folly/FBString.h"]
[cases] [cases]
[cases.empty] [cases.empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
param_types = ["folly::fbstring&"] param_types = ["folly::fbstring&"]
setup = "return {};" setup = "return {};"
expect_json = ''' expect_json = '''
@ -26,6 +27,7 @@ includes = ["folly/FBString.h"]
''' '''
[cases.inline] [cases.inline]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
param_types = ["folly::fbstring&"] param_types = ["folly::fbstring&"]
setup = 'return {"012345"};' setup = 'return {"012345"};'
expect_json = ''' expect_json = '''
@ -51,6 +53,7 @@ includes = ["folly/FBString.h"]
''' '''
[cases.heap_allocated] [cases.heap_allocated]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
param_types = ["folly::fbstring&"] param_types = ["folly::fbstring&"]
setup = 'return {"abcdefghijklmnopqrstuvwxzy"};' setup = 'return {"abcdefghijklmnopqrstuvwxzy"};'
expect_json = ''' expect_json = '''
@ -76,6 +79,7 @@ includes = ["folly/FBString.h"]
''' '''
[cases.string_pooled] [cases.string_pooled]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
param_types = ["folly::fbstring&"] param_types = ["folly::fbstring&"]
setup = "return folly::fbstring(1024, 'c');" setup = "return folly::fbstring(1024, 'c');"
expect_json = ''' expect_json = '''

View File

@ -1,23 +1,28 @@
includes = ["folly/small_vector.h", "vector"] includes = ["folly/small_vector.h", "vector"]
[cases] [cases]
[cases.int_default_empty] [cases.int_default_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<int>&"] param_types = ["const folly::small_vector<int>&"]
setup = "return {};" setup = "return {};"
expect_json = '[{"staticSize":16, "dynamicSize":0, "exclusiveSize":16, "length":0, "capacity":2, "elementStaticSize":4}]' expect_json = '[{"staticSize":16, "dynamicSize":0, "exclusiveSize":16, "length":0, "capacity":2, "elementStaticSize":4}]'
[cases.int_default_inlined] [cases.int_default_inlined]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<int>&"] param_types = ["const folly::small_vector<int>&"]
setup = "return {{1,2}};" setup = "return {{1,2}};"
expect_json = '[{"staticSize":16, "dynamicSize":0, "exclusiveSize":16, "length":2, "capacity":2, "elementStaticSize":4}]' expect_json = '[{"staticSize":16, "dynamicSize":0, "exclusiveSize":16, "length":2, "capacity":2, "elementStaticSize":4}]'
[cases.int_default_overflow] [cases.int_default_overflow]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<int>&"] param_types = ["const folly::small_vector<int>&"]
setup = "return {{1,2,3,4}};" setup = "return {{1,2,3,4}};"
expect_json = '[{"staticSize":16, "dynamicSize":24, "exclusiveSize":40, "length":4, "capacity":6, "elementStaticSize":4}]' expect_json = '[{"staticSize":16, "dynamicSize":24, "exclusiveSize":40, "length":4, "capacity":6, "elementStaticSize":4}]'
[cases.vector_3_empty] [cases.vector_3_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<std::vector<int>, 3>&"] param_types = ["const folly::small_vector<std::vector<int>, 3>&"]
setup = "return {};" setup = "return {};"
expect_json = '[{"staticSize":80, "dynamicSize":0, "exclusiveSize":80, "length":0, "capacity":3, "elementStaticSize":24}]' expect_json = '[{"staticSize":80, "dynamicSize":0, "exclusiveSize":80, "length":0, "capacity":3, "elementStaticSize":24}]'
[cases.vector_3_inlined] [cases.vector_3_inlined]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<std::vector<int>, 3>&"] param_types = ["const folly::small_vector<std::vector<int>, 3>&"]
setup = "return {{ {1,2,3}, {4}, {5,6} }};" setup = "return {{ {1,2,3}, {4}, {5,6} }};"
expect_json = '''[ expect_json = '''[
@ -27,6 +32,7 @@ includes = ["folly/small_vector.h", "vector"]
{"staticSize":24, "dynamicSize":8, "exclusiveSize":32, "length":2, "capacity":2, "elementStaticSize":4} {"staticSize":24, "dynamicSize":8, "exclusiveSize":32, "length":2, "capacity":2, "elementStaticSize":4}
]}]''' ]}]'''
[cases.vector_3_overflow] [cases.vector_3_overflow]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<std::vector<int>, 3>&"] param_types = ["const folly::small_vector<std::vector<int>, 3>&"]
setup = "return {{ {1,2,3}, {4}, {5,6}, {7} }};" setup = "return {{ {1,2,3}, {4}, {5,6}, {7} }};"
expect_json = '''[ expect_json = '''[
@ -38,6 +44,7 @@ includes = ["folly/small_vector.h", "vector"]
]}]''' ]}]'''
[cases.int_always_heap] [cases.int_always_heap]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<int, 0>&"] param_types = ["const folly::small_vector<int, 0>&"]
setup = "return {{1}};" setup = "return {{1}};"
expect_json = '[{"staticSize":16, "dynamicSize":4, "exclusiveSize":20, "length":1, "capacity":1, "elementStaticSize":4}]' expect_json = '[{"staticSize":16, "dynamicSize":4, "exclusiveSize":20, "length":1, "capacity":1, "elementStaticSize":4}]'

View File

@ -4,10 +4,39 @@ includes = ["folly/sorted_vector_types.h", "vector"]
param_types = ["const folly::sorted_vector_map<int, int>&"] param_types = ["const folly::sorted_vector_map<int, int>&"]
setup = "return {};" setup = "return {};"
expect_json = '[{"staticSize":24, "dynamicSize":0, "exclusiveSize":24, "length":0, "capacity":0, "elementStaticSize":8}]' expect_json = '[{"staticSize":24, "dynamicSize":0, "exclusiveSize":24, "length":0, "capacity":0, "elementStaticSize":8}]'
expect_json_v2 = '[{"staticSize":24, "exclusiveSize":24, "length":0, "capacity":0, "members":[]}]'
[cases.int_int_some] [cases.int_int_some]
param_types = ["const folly::sorted_vector_map<int, int>&"] param_types = ["const folly::sorted_vector_map<int, int>&"]
setup = "return {{ {1,2}, {3,4} }};" setup = "return {{ {1,2}, {3,4} }};"
expect_json = '[{"staticSize":24, "dynamicSize":16, "exclusiveSize":40, "length":2, "capacity":2, "elementStaticSize":8}]' expect_json = '[{"staticSize":24, "dynamicSize":16, "exclusiveSize":40, "length":2, "capacity":2, "elementStaticSize":8}]'
expect_json_v2 = '''[{"staticSize":24, "exclusiveSize":24, "length":2, "capacity":2, "members":[
{"staticSize":8, "exclusiveSize":0, "members": [
{"name":"key", "staticSize":4, "exclusiveSize":4},
{"name":"value", "staticSize":4, "exclusiveSize":4}
]},
{"staticSize":8, "exclusiveSize":0, "members": [
{"name":"key", "staticSize":4, "exclusiveSize":4},
{"name":"value", "staticSize":4, "exclusiveSize":4}
]}
]}]'''
[cases.int_int_reserve]
param_types = ["const folly::sorted_vector_map<int, int>&"]
setup = '''
folly::sorted_vector_map<int, int> m{{ {1,2}, {3,4} }};
m.reserve(10);
return m;
'''
expect_json = '[{"staticSize":24, "dynamicSize":80, "exclusiveSize":104, "length":2, "capacity":10, "elementStaticSize":8}]'
expect_json_v2 = '''[{"staticSize":24, "exclusiveSize":88, "length":2, "capacity":10, "members":[
{"staticSize":8, "exclusiveSize":0, "members": [
{"name":"key", "staticSize":4, "exclusiveSize":4},
{"name":"value", "staticSize":4, "exclusiveSize":4}
]},
{"staticSize":8, "exclusiveSize":0, "members": [
{"name":"key", "staticSize":4, "exclusiveSize":4},
{"name":"value", "staticSize":4, "exclusiveSize":4}
]}
]}]'''
[cases.int_vector_empty] [cases.int_vector_empty]
skip = "Wrong results" # https://github.com/facebookexperimental/object-introspection/issues/258 skip = "Wrong results" # https://github.com/facebookexperimental/object-introspection/issues/258

View File

@ -38,7 +38,8 @@ def add_headers(f, custom_headers, thrift_headers):
#include <thread> #include <thread>
#include <tuple> #include <tuple>
#include <ObjectIntrospection.h> #include <oi/oi.h>
#include <oi/exporters/Json.h>
""" """
) )
@ -83,11 +84,11 @@ def add_test_setup(f, config):
def define_traceable_func(name, params, body): def define_traceable_func(name, params, body):
return ( return (
f"\n" f"\n"
f' extern "C" {{\n' # f' extern "C" {{\n'
f" void __attribute__((noinline)) {name}({params}) {{\n" f' extern "C" void __attribute__((noinline)) {name}({params}) {{\n'
f"{body}" f"{body}"
f" }}\n" f" }}\n"
f" }}\n" # f" }}\n"
) )
cases = config["cases"] cases = config["cases"]
@ -134,22 +135,18 @@ def add_test_setup(f, config):
) )
oil_func_body = ( oil_func_body = (
f"\n" f" oi::GeneratorOptions opts{{\n"
f"ObjectIntrospection::options opts{{\n" f" .configFilePath = configFile,\n"
f" .configFilePath = configFile,\n" f' .sourceFileDumpPath = "oil_jit_code.cpp",\n'
f" .debugLevel = 3,\n" f" .debugLevel = 3,\n"
f' .sourceFileDumpPath = "oil_jit_code.cpp",\n' f" }};\n\n"
f"}};"
) )
oil_func_body += ' std::cout << "{\\"results\\": [" << std::endl;\n' oil_func_body += " auto pr = oi::exporters::Json(std::cout);\n"
oil_func_body += ' std::cout << "," << std::endl;\n'.join( oil_func_body += " pr.setPretty(true);\n"
f" size_t size{i} = 0;\n" for i in range(len(case["param_types"])):
f" auto ret{i} = ObjectIntrospection::getObjectSize(a{i}, size{i}, opts);\n" oil_func_body += f" auto ret{i} = oi::setupAndIntrospect(a{i}, opts);\n"
f' std::cout << "{{\\"ret\\": " << ret{i} << ", \\"size\\": " << size{i} << "}}" << std::endl;\n' oil_func_body += f" pr.print(*ret{i});\n"
for i in range(len(case["param_types"]))
)
oil_func_body += ' std::cout << "]}" << std::endl;\n'
f.write( f.write(
define_traceable_func( define_traceable_func(
@ -367,23 +364,14 @@ def add_oil_integration_test(f, config, case_name, case):
f' .targetArgs = "oil {case_str}",\n' f' .targetArgs = "oil {case_str}",\n'
f" }}, std::move(configPrefix), std::move(configSuffix));\n\n" f" }}, std::move(configPrefix), std::move(configSuffix));\n\n"
f" ASSERT_EQ(exit_code(target), {exit_code});\n" f" ASSERT_EQ(exit_code(target), {exit_code});\n"
f"\n"
f" bpt::ptree result_json;\n"
f" auto json_ss = std::stringstream(stdout_);\n"
f" bpt::read_json(json_ss, result_json);\n"
f" std::vector<size_t> sizes;\n"
f' for (const auto& each : result_json.get_child("results")) {{\n'
f" const auto& result = each.second;\n"
f' int oilResult = result.get<int>("ret");\n'
f' size_t oilSize = result.get<size_t>("size");\n'
f" ASSERT_EQ(oilResult, 0);\n"
f" sizes.push_back(oilSize);\n"
f" }}"
) )
if "expect_json" in case: key = "expect_json"
if "expect_json_v2" in case:
key = "expect_json_v2"
if key in case:
try: try:
json.loads(case["expect_json"]) json.loads(case[key])
except json.decoder.JSONDecodeError as error: except json.decoder.JSONDecodeError as error:
print( print(
f"\x1b[31m`expect_json` value for test case {config['suite']}.{case_name} was invalid JSON: {error}\x1b[0m", f"\x1b[31m`expect_json` value for test case {config['suite']}.{case_name} was invalid JSON: {error}\x1b[0m",
@ -394,17 +382,12 @@ def add_oil_integration_test(f, config, case_name, case):
f.write( f.write(
f"\n" f"\n"
f" std::stringstream expected_json_ss;\n" f" std::stringstream expected_json_ss;\n"
f' expected_json_ss << R"--({case["expect_json"]})--";\n' f' expected_json_ss << R"--({case[key]})--";\n'
f" bpt::ptree expected_json;\n" f" auto result_json_ss = std::stringstream(stdout_);\n"
f" bpt::ptree expected_json, actual_json;\n"
f" bpt::read_json(expected_json_ss, expected_json);\n" f" bpt::read_json(expected_json_ss, expected_json);\n"
f" auto sizes_it = sizes.begin();\n" f" bpt::read_json(result_json_ss, actual_json);\n"
f" for (auto it = expected_json.begin(); it != expected_json.end(); ++it, ++sizes_it) {{\n" f" compare_json(expected_json, actual_json);\n"
f" auto node = it->second;\n"
f' size_t expected_size = node.get<size_t>("staticSize");\n'
f' if (node.find("dynamicSize") != node.not_found())\n' # Assume dynamicSize is 0 if not set
f' expected_size += node.get<size_t>("dynamicSize");\n'
f" EXPECT_EQ(*sizes_it, expected_size);\n"
f" }}\n"
) )
f.write(f"}}\n") f.write(f"}}\n")

View File

@ -10,6 +10,7 @@ definitions = '''
[cases] [cases]
[cases.a] [cases.a]
oil_skip = 'v2 hides the member entirely when it should show it with static size' # todo: github issue
param_types = ["const Bar&"] param_types = ["const Bar&"]
setup = """ setup = """
return Bar{ return Bar{

View File

@ -1,18 +1,18 @@
definitions = ''' definitions = '''
class Base { class Base {
int base_int; int32_t base_int;
}; };
class Public : public Base { class Public : public Base {
int public_int; int32_t public_int;
}; };
class Protected : protected Base { class Protected : protected Base {
int protected_int; int32_t protected_int;
}; };
class Private : private Base { class Private : private Base {
int private_int; int32_t private_int;
}; };
''' '''
[cases] [cases]
@ -21,30 +21,27 @@ definitions = '''
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
"staticSize":8, "staticSize":8,
"dynamicSize":0,
"members":[ "members":[
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}, {"name":"base_int", "staticSize":4, "typeName": "int32_t"},
{"name":"public_int", "staticSize":4, "dynamicSize":0, "typeName": "int"} {"name":"public_int", "staticSize":4, "typeName": "int32_t"}
]}]''' ]}]'''
[cases.protected] [cases.protected]
param_types = ["const Protected&"] param_types = ["const Protected&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
"staticSize":8, "staticSize":8,
"dynamicSize":0,
"members":[ "members":[
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}, {"name":"base_int", "staticSize":4, "typeName": "int32_t"},
{"name":"protected_int", "staticSize":4, "dynamicSize":0, "typeName": "int"} {"name":"protected_int", "staticSize":4, "typeName": "int32_t"}
]}]''' ]}]'''
[cases.private] [cases.private]
param_types = ["const Private&"] param_types = ["const Private&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
"staticSize":8, "staticSize":8,
"dynamicSize":0,
"members":[ "members":[
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}, {"name":"base_int", "staticSize":4, "typeName": "int32_t"},
{"name":"private_int", "staticSize":4, "dynamicSize":0, "typeName": "int"} {"name":"private_int", "staticSize":4, "typeName": "int32_t"}
]}]''' ]}]'''
[cases.public_as_base] [cases.public_as_base]
param_types = ["const Base&"] param_types = ["const Base&"]
@ -52,7 +49,6 @@ definitions = '''
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
"staticSize":4, "staticSize":4,
"dynamicSize":0,
"members":[ "members":[
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"} {"name":"base_int", "staticSize":4, "typeName": "int32_t"}
]}]''' ]}]'''

View File

@ -34,3 +34,14 @@ definitions = '''
{"name":"e", "staticSize":4, "dynamicSize":0, "typeName": "int"}, {"name":"e", "staticSize":4, "dynamicSize":0, "typeName": "int"},
{"name":"f", "staticSize":4, "dynamicSize":0, "typeName": "int"} {"name":"f", "staticSize":4, "dynamicSize":0, "typeName": "int"}
]}]''' ]}]'''
expect_json_v2 = '''[{
"staticSize":24,
"exclusiveSize":0,
"members":[
{"name":"a", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]},
{"name":"b", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]},
{"name":"c", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]},
{"name":"d", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]},
{"name":"e", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]},
{"name":"f", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]}
]}]'''

View File

@ -82,6 +82,7 @@ definitions = '''
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4} {"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}
]}]''' ]}]'''
[cases.b_no_polymorphic] [cases.b_no_polymorphic]
oil_skip = "vptr included in output when it should probably be hidden" # https://github.com/facebookexperimental/object-introspection/issues/291
param_types = ["const B&"] param_types = ["const B&"]
arg_types = ["B"] arg_types = ["B"]
setup = ''' setup = '''
@ -157,6 +158,7 @@ definitions = '''
{"name":"int_c", "staticSize":4, "dynamicSize":0} {"name":"int_c", "staticSize":4, "dynamicSize":0}
]}]''' ]}]'''
[cases.c_no_polymorphic] [cases.c_no_polymorphic]
oil_skip = 'vptrs are included int he json output' # https://github.com/facebookexperimental/object-introspection/issues/291
param_types = ["const C&"] param_types = ["const C&"]
arg_types = ["C"] arg_types = ["C"]
setup = ''' setup = '''
@ -174,3 +176,14 @@ definitions = '''
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, {"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4},
{"name":"int_c", "staticSize":4, "dynamicSize":0} {"name":"int_c", "staticSize":4, "dynamicSize":0}
]}]''' ]}]'''
expect_json_v2 = '''[{
"typeNames": ["C"],
"staticSize": 48,
"exclusiveSize": 8,
"members": [
{"name":"int_a", "staticSize":4, "exclusiveSize":4},
{"staticSize":8, "exclusiveSize":8},
{"name":"vec_b", "staticSize":24, "exclusiveSize":24, "length":3, "capacity":3},
{"name":"int_c", "staticSize":4, "exclusiveSize":4}
]
}]'''

View File

@ -12,6 +12,7 @@ definitions = '''
[cases] [cases]
[cases.a] [cases.a]
oil_disable = 'multi-argument probing has no meaning for oil'
param_types = ["int", "double"] param_types = ["int", "double"]
args = "arg0,arg1" args = "arg0,arg1"
setup = "return {1,2.0};" setup = "return {1,2.0};"

View File

@ -16,6 +16,7 @@ definitions = '''
''' '''
[cases] [cases]
[cases.queue] [cases.queue]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<std::pair<nsA::Foo, nsB::Foo>>&"] param_types = ["const std::queue<std::pair<nsA::Foo, nsB::Foo>>&"]
setup = "return std::queue<std::pair<ns_namespaces::nsA::Foo, ns_namespaces::nsB::Foo>>({{ns_namespaces::nsA::Foo(), ns_namespaces::nsB::Foo()}});" setup = "return std::queue<std::pair<ns_namespaces::nsA::Foo, ns_namespaces::nsB::Foo>>({{ns_namespaces::nsA::Foo(), ns_namespaces::nsB::Foo()}});"
expect_json = '''[{ expect_json = '''[{
@ -23,6 +24,7 @@ definitions = '''
"staticSize": 80, "dynamicSize": 12, "length": 1, "capacity": 1, "elementStaticSize": 12 "staticSize": 80, "dynamicSize": 12, "length": 1, "capacity": 1, "elementStaticSize": 12
}]''' }]'''
[cases.stack] [cases.stack]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<std::pair<nsA::Foo, nsB::Foo>>&"] param_types = ["const std::stack<std::pair<nsA::Foo, nsB::Foo>>&"]
setup = "return std::stack<std::pair<ns_namespaces::nsA::Foo, ns_namespaces::nsB::Foo>>({{ns_namespaces::nsA::Foo(), ns_namespaces::nsB::Foo()}});" setup = "return std::stack<std::pair<ns_namespaces::nsA::Foo, ns_namespaces::nsB::Foo>>({{ns_namespaces::nsA::Foo(), ns_namespaces::nsB::Foo()}});"
expect_json = '''[{ expect_json = '''[{

View File

@ -93,6 +93,7 @@ definitions = '''
]}]''' ]}]'''
[cases.parent_padding] [cases.parent_padding]
oid_skip = 'calculating incorrectly'
param_types = ["const PaddedChild&"] param_types = ["const PaddedChild&"]
setup = "return PaddedChild{};" setup = "return PaddedChild{};"
expect_json = '''[{ expect_json = '''[{
@ -100,3 +101,7 @@ definitions = '''
"dynamicSize": 0, "dynamicSize": 0,
"paddingSavingsSize": 19 "paddingSavingsSize": 19
}]''' }]'''
expect_json_v2 = '''[{
"staticSize": 104,
"exclusiveSize": 32
}]'''

View File

@ -14,8 +14,7 @@ definitions = '''
[cases] [cases]
[cases.int] [cases.int]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19 skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
oil_disable = "oil can't chase raw pointers safely"
param_types = ["int*"] param_types = ["int*"]
setup = "return new int(1);" setup = "return new int(1);"
cli_options = ["-fchase-raw-pointers"] cli_options = ["-fchase-raw-pointers"]
@ -33,7 +32,7 @@ definitions = '''
] ]
}]''' }]'''
[cases.int_no_follow] [cases.int_no_follow]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19 skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["int*"] param_types = ["int*"]
setup = "return new int(1);" setup = "return new int(1);"
expect_json = '''[{ expect_json = '''[{
@ -44,7 +43,7 @@ definitions = '''
"NOT": "members" "NOT": "members"
}]''' }]'''
[cases.int_null] [cases.int_null]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19 skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["int*"] param_types = ["int*"]
setup = "return nullptr;" setup = "return nullptr;"
expect_json = '''[{ expect_json = '''[{
@ -57,8 +56,7 @@ definitions = '''
[cases.void] [cases.void]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19 skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
oil_disable = "oil can't chase raw pointers safely"
param_types = ["void*"] param_types = ["void*"]
setup = "return new int(1);" setup = "return new int(1);"
cli_options = ["-fchase-raw-pointers"] cli_options = ["-fchase-raw-pointers"]
@ -70,7 +68,7 @@ definitions = '''
"NOT": "members" "NOT": "members"
}]''' }]'''
[cases.void_no_follow] [cases.void_no_follow]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19 skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["void*"] param_types = ["void*"]
setup = "return new int(1);" setup = "return new int(1);"
expect_json = '''[{ expect_json = '''[{
@ -81,7 +79,7 @@ definitions = '''
"NOT": "members" "NOT": "members"
}]''' }]'''
[cases.void_null] [cases.void_null]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19 skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["void*"] param_types = ["void*"]
setup = "return nullptr;" setup = "return nullptr;"
expect_json = '''[{ expect_json = '''[{
@ -94,8 +92,7 @@ definitions = '''
[cases.vector] [cases.vector]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19 skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
oil_disable = "oil can't chase raw pointers safely"
param_types = ["std::vector<int>*"] param_types = ["std::vector<int>*"]
setup = "return new std::vector<int>{1,2,3};" setup = "return new std::vector<int>{1,2,3};"
cli_options = ["-fchase-raw-pointers"] cli_options = ["-fchase-raw-pointers"]
@ -113,7 +110,7 @@ definitions = '''
] ]
}]''' }]'''
[cases.vector_no_follow] [cases.vector_no_follow]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19 skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["std::vector<int>*"] param_types = ["std::vector<int>*"]
setup = "return new std::vector<int>{1,2,3};" setup = "return new std::vector<int>{1,2,3};"
expect_json = '''[{ expect_json = '''[{
@ -124,7 +121,7 @@ definitions = '''
"NOT": "members" "NOT": "members"
}]''' }]'''
[cases.vector_null] [cases.vector_null]
oid_skip = "BAD DATA SEGMENT!!! top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19 skip = "BAD DATA SEGMENT!!! top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["std::vector<int>*"] param_types = ["std::vector<int>*"]
setup = "return nullptr;" setup = "return nullptr;"
expect_json = '''[{ expect_json = '''[{
@ -224,7 +221,7 @@ definitions = '''
{"staticSize":8, "dynamicSize":4, "NOT": {"pointer":0}} {"staticSize":8, "dynamicSize":4, "NOT": {"pointer":0}}
]}]''' ]}]'''
[cases.vector_of_pointers_no_follow] [cases.vector_of_pointers_no_follow]
oid_skip = "pointer field is missing from results" # https://github.com/facebookexperimental/object-introspection/issues/21 skip = "pointer field is missing from results" # https://github.com/facebookexperimental/object-introspection/issues/21
param_types = ["const std::vector<int*>&"] param_types = ["const std::vector<int*>&"]
setup = "return {{new int(1), nullptr, new int(3)}};" setup = "return {{new int(1), nullptr, new int(3)}};"
expect_json = '''[{ expect_json = '''[{

View File

@ -14,7 +14,7 @@ definitions = '''
[cases] [cases]
[cases.raw] [cases.raw]
oid_skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22 skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
param_types = ["const FuncPtrStruct&"] param_types = ["const FuncPtrStruct&"]
setup = "return {{myFunction}};" setup = "return {{myFunction}};"
expect_json = '''[{ expect_json = '''[{
@ -46,7 +46,7 @@ definitions = '''
}] }]
}]''' }]'''
[cases.raw_null] [cases.raw_null]
oid_skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22 skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
param_types = ["const FuncPtrStruct&"] param_types = ["const FuncPtrStruct&"]
setup = "return {{nullptr}};" setup = "return {{nullptr}};"
expect_json = '''[{ expect_json = '''[{
@ -62,7 +62,7 @@ definitions = '''
}]''' }]'''
[cases.std_function] [cases.std_function]
oid_skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22 skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
param_types = ["std::function<void(int)> &"] param_types = ["std::function<void(int)> &"]
setup = "return myFunction;" setup = "return myFunction;"
expect_json = '''[{ expect_json = '''[{
@ -86,7 +86,7 @@ definitions = '''
"NOT": "members" "NOT": "members"
}]''' }]'''
[cases.std_function_null] [cases.std_function_null]
oid_skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22 skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
param_types = ["std::function<void(int)> &"] param_types = ["std::function<void(int)> &"]
setup = "return nullptr;" setup = "return nullptr;"
expect_json = '''[{ expect_json = '''[{

View File

@ -32,7 +32,7 @@ definitions = '''
"NOT": "members" "NOT": "members"
}]''' }]'''
[cases.raw_no_follow] [cases.raw_no_follow]
oid_skip = "oid codegen fails on this" # https://github.com/facebookexperimental/object-introspection/issues/17 skip = "oid codegen fails on this" # https://github.com/facebookexperimental/object-introspection/issues/17
param_types = ["IncompleteType*"] param_types = ["IncompleteType*"]
setup = "return static_cast<IncompleteType*>(::operator new(5));" setup = "return static_cast<IncompleteType*>(::operator new(5));"
expect_json = '''[{ expect_json = '''[{
@ -43,7 +43,7 @@ definitions = '''
"NOT": "members" "NOT": "members"
}]''' }]'''
[cases.raw_null] [cases.raw_null]
oid_skip = "oid codegen fails on this" # https://github.com/facebookexperimental/object-introspection/issues/17 skip = "oid codegen fails on this" # https://github.com/facebookexperimental/object-introspection/issues/17
param_types = ["IncompleteType*"] param_types = ["IncompleteType*"]
setup = "return nullptr;" setup = "return nullptr;"
expect_json = '''[{ expect_json = '''[{
@ -55,6 +55,7 @@ definitions = '''
}]''' }]'''
[cases.unique_ptr] [cases.unique_ptr]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["const std::unique_ptr<IncompleteType, decltype(&incomplete_type_deleter)>&"] param_types = ["const std::unique_ptr<IncompleteType, decltype(&incomplete_type_deleter)>&"]
setup = ''' setup = '''
auto raw_ptr = static_cast<IncompleteType*>(::operator new(5)); auto raw_ptr = static_cast<IncompleteType*>(::operator new(5));
@ -63,6 +64,7 @@ definitions = '''
''' '''
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]' expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'
[cases.unique_ptr_null] [cases.unique_ptr_null]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["const std::unique_ptr<IncompleteType, decltype(&incomplete_type_deleter)>&"] param_types = ["const std::unique_ptr<IncompleteType, decltype(&incomplete_type_deleter)>&"]
setup = ''' setup = '''
return std::unique_ptr<IncompleteType, decltype(&incomplete_type_deleter)>( return std::unique_ptr<IncompleteType, decltype(&incomplete_type_deleter)>(
@ -71,6 +73,7 @@ definitions = '''
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]' expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'
[cases.shared_ptr] [cases.shared_ptr]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["const std::shared_ptr<IncompleteType>&"] param_types = ["const std::shared_ptr<IncompleteType>&"]
setup = ''' setup = '''
auto raw_ptr = static_cast<IncompleteType*>(::operator new(5)); auto raw_ptr = static_cast<IncompleteType*>(::operator new(5));
@ -78,6 +81,7 @@ definitions = '''
''' '''
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]' expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'
[cases.shared_ptr_null] [cases.shared_ptr_null]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["const std::shared_ptr<IncompleteType>"] param_types = ["const std::shared_ptr<IncompleteType>"]
setup = "return nullptr;" setup = "return nullptr;"
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]' expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'

View File

@ -302,10 +302,10 @@ OidProc OidIntegration::runOidOnProcess(OidOpts opts,
}; };
} }
void OidIntegration::compare_json(const bpt::ptree& expected_json, void IntegrationBase::compare_json(const bpt::ptree& expected_json,
const bpt::ptree& actual_json, const bpt::ptree& actual_json,
const std::string& full_key, const std::string& full_key,
bool expect_eq) { bool expect_eq) {
if (expected_json.empty()) { if (expected_json.empty()) {
if (expect_eq) { if (expect_eq) {
ASSERT_EQ(expected_json.data(), actual_json.data()) ASSERT_EQ(expected_json.data(), actual_json.data())
@ -357,6 +357,21 @@ void OidIntegration::compare_json(const bpt::ptree& expected_json,
auto actual_it = actual_json.find(key); auto actual_it = actual_json.find(key);
auto curr_key = full_key + "." + key; auto curr_key = full_key + "." + key;
if (actual_it == actual_json.not_found()) { if (actual_it == actual_json.not_found()) {
// TODO: Remove these with the switch to treebuilderv2. This is a hack to
// make some old JSON output compatible with new JSON output.
if (key == "typeName") {
auto type_names = actual_json.find("typeNames");
if (type_names != actual_json.not_found()) {
if (auto name = type_names->second.rbegin();
name != type_names->second.rend()) {
compare_json(val, name->second, curr_key, expect_eq);
continue;
}
}
} else if (key == "dynamicSize" && val.get_value<size_t>() == 0) {
continue;
}
ADD_FAILURE() << "Expected key not found in output: " << curr_key; ADD_FAILURE() << "Expected key not found in output: " << curr_key;
continue; continue;
} }

View File

@ -49,16 +49,6 @@ class IntegrationBase : public ::testing::Test {
std::string stdout_; std::string stdout_;
std::string stderr_; std::string stderr_;
};
class OidIntegration : public IntegrationBase {
protected:
std::string TmpDirStr() override;
OidProc runOidOnProcess(OidOpts opts,
std::vector<std::string> extra_args,
std::string configPrefix,
std::string configSuffix);
/* /*
* compare_json * compare_json
@ -72,6 +62,16 @@ class OidIntegration : public IntegrationBase {
bool expect_eq = true); bool expect_eq = true);
}; };
class OidIntegration : public IntegrationBase {
protected:
std::string TmpDirStr() override;
OidProc runOidOnProcess(OidOpts opts,
std::vector<std::string> extra_args,
std::string configPrefix,
std::string configSuffix);
};
class OilIntegration : public IntegrationBase { class OilIntegration : public IntegrationBase {
protected: protected:
std::string TmpDirStr() override; std::string TmpDirStr() override;

View File

@ -44,6 +44,5 @@ definitions = '''
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
"staticSize":8, "staticSize":8,
"dynamicSize":0, "dynamicSize":0
"NOT":"members"
}]''' }]'''

View File

@ -5,6 +5,7 @@ definitions = '''
[cases] [cases]
[cases.no_ints] [cases.no_ints]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/320
param_types = ["const sorted_vector_set<int>&"] param_types = ["const sorted_vector_set<int>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -22,6 +23,7 @@ definitions = '''
}]}]''' }]}]'''
[cases.some_ints] [cases.some_ints]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/320
param_types = ["const sorted_vector_set<int>&"] param_types = ["const sorted_vector_set<int>&"]
setup = ''' setup = '''
sorted_vector_set<int> is; sorted_vector_set<int> is;

View File

@ -1,6 +1,7 @@
includes = ["array", "cstdint", "vector"] includes = ["array", "cstdint", "vector"]
[cases] [cases]
[cases.uint64_length_0] [cases.uint64_length_0]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/318
param_types = ["std::array<std::uint64_t, 0>&"] param_types = ["std::array<std::uint64_t, 0>&"]
setup = "return {{}};" setup = "return {{}};"
expect_json = ''' expect_json = '''
@ -16,6 +17,7 @@ includes = ["array", "cstdint", "vector"]
] ]
''' '''
[cases.uint64_length_1] [cases.uint64_length_1]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/318
param_types = ["std::array<std::uint64_t, 1>&"] param_types = ["std::array<std::uint64_t, 1>&"]
setup = "return {{1}};" setup = "return {{1}};"
expect_json = ''' expect_json = '''
@ -31,6 +33,7 @@ includes = ["array", "cstdint", "vector"]
] ]
''' '''
[cases.uint64_length_8] [cases.uint64_length_8]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/318
param_types = ["std::array<std::uint64_t, 8>&"] param_types = ["std::array<std::uint64_t, 8>&"]
setup = "return {{0,1,2,3,4,5,6,7}};" setup = "return {{0,1,2,3,4,5,6,7}};"
expect_json = ''' expect_json = '''
@ -45,6 +48,7 @@ includes = ["array", "cstdint", "vector"]
] ]
''' '''
[cases.vector_length_1] [cases.vector_length_1]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/318
param_types = ["std::array<std::vector<std::uint64_t>, 1>&"] param_types = ["std::array<std::vector<std::uint64_t>, 1>&"]
setup = "return {{std::initializer_list<std::uint64_t>({1,2,3,4,5})}};" setup = "return {{std::initializer_list<std::uint64_t>({1,2,3,4,5})}};"
expect_json = ''' expect_json = '''
@ -65,6 +69,7 @@ includes = ["array", "cstdint", "vector"]
] ]
''' '''
[cases.vector_length_2] [cases.vector_length_2]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/318
param_types = ["std::array<std::vector<std::uint64_t>, 2>&"] param_types = ["std::array<std::vector<std::uint64_t>, 2>&"]
setup = "return {{std::initializer_list<std::uint64_t>({1,2,3,4,5}), std::initializer_list<std::uint64_t>({6,7,8,9})}};" setup = "return {{std::initializer_list<std::uint64_t>({1,2,3,4,5}), std::initializer_list<std::uint64_t>({6,7,8,9})}};"
expect_json = ''' expect_json = '''

View File

@ -16,6 +16,7 @@ public:
''' '''
[cases] [cases]
[cases.a] [cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/317
param_types = ["const C&"] param_types = ["const C&"]
setup = ''' setup = '''
C foo; C foo;

View File

@ -3,18 +3,22 @@
includes = ["deque"] includes = ["deque"]
[cases] [cases]
[cases.int_empty] [cases.int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
param_types = ["const std::deque<int>&"] param_types = ["const std::deque<int>&"]
setup = "return {};" setup = "return {};"
expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}]' expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}]'
[cases.int_some] [cases.int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
param_types = ["const std::deque<int>&"] param_types = ["const std::deque<int>&"]
setup = "return {{1,2,3}};" setup = "return {{1,2,3}};"
expect_json = '[{"staticSize":80, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}]' expect_json = '[{"staticSize":80, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}]'
[cases.deque_int_empty] [cases.deque_int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
param_types = ["const std::deque<std::deque<int>>&"] param_types = ["const std::deque<std::deque<int>>&"]
setup = "return {};" setup = "return {};"
expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":80}]' expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":80}]'
[cases.deque_int_some] [cases.deque_int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
param_types = ["const std::deque<std::deque<int>>&"] param_types = ["const std::deque<std::deque<int>>&"]
setup = "return {{{1,2,3},{},{4,5}}};" setup = "return {{{1,2,3},{},{4,5}}};"
expect_json = '''[{ expect_json = '''[{

View File

@ -24,6 +24,7 @@ includes = ["deque"]
[cases] [cases]
[cases.a] [cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
param_types = ["const Foo&"] param_types = ["const Foo&"]
setup = ''' setup = '''
Foo foo; Foo foo;

View File

@ -27,6 +27,7 @@ includes = ["list"]
[cases] [cases]
[cases.a] [cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/315
param_types = ["const Foo&"] param_types = ["const Foo&"]
setup = ''' setup = '''
Foo foo; Foo foo;

View File

@ -36,6 +36,7 @@ includes = ["map", "functional"]
[cases] [cases]
[cases.a] [cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/314
param_types = ["const Foo&"] param_types = ["const Foo&"]
setup = ''' setup = '''
Foo foo; Foo foo;

View File

@ -14,6 +14,7 @@ includes = ["map"]
[cases] [cases]
[cases.a] [cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/313
param_types = ["const Foo&"] param_types = ["const Foo&"]
setup = ''' setup = '''
Foo foo; Foo foo;

View File

@ -1,6 +1,7 @@
includes = ["optional", "cstdint", "vector"] includes = ["optional", "cstdint", "vector"]
[cases] [cases]
[cases.uint64_empty] [cases.uint64_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/312
param_types = ["std::optional<std::uint64_t>&"] param_types = ["std::optional<std::uint64_t>&"]
setup = "return std::nullopt;" setup = "return std::nullopt;"
expect_json = ''' expect_json = '''
@ -16,6 +17,7 @@ includes = ["optional", "cstdint", "vector"]
] ]
''' '''
[cases.uint64_present] [cases.uint64_present]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/312
param_types = ["std::optional<std::uint64_t>&"] param_types = ["std::optional<std::uint64_t>&"]
setup = "return 64;" setup = "return 64;"
expect_json = ''' expect_json = '''
@ -31,6 +33,7 @@ includes = ["optional", "cstdint", "vector"]
] ]
''' '''
[cases.vector_empty] [cases.vector_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/312
param_types = ["std::optional<std::vector<std::uint64_t>>&"] param_types = ["std::optional<std::vector<std::uint64_t>>&"]
setup = "return std::nullopt;" setup = "return std::nullopt;"
expect_json = ''' expect_json = '''
@ -46,6 +49,7 @@ includes = ["optional", "cstdint", "vector"]
] ]
''' '''
[cases.vector_present] [cases.vector_present]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/312
param_types = ["std::optional<std::vector<std::uint64_t>>&"] param_types = ["std::optional<std::vector<std::uint64_t>>&"]
setup = "return {{{1,2,3,4,5}}};" setup = "return {{{1,2,3,4,5}}};"
expect_json = ''' expect_json = '''

View File

@ -1,6 +1,7 @@
includes = ["vector", "utility", "cstdint"] includes = ["vector", "utility", "cstdint"]
[cases] [cases]
[cases.uint64_uint64] [cases.uint64_uint64]
oil_skip = 'tests need updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/310
param_types = ["std::pair<std::uint64_t, std::uint64_t>&"] param_types = ["std::pair<std::uint64_t, std::uint64_t>&"]
setup = "return {{0, 1}};" setup = "return {{0, 1}};"
expect_json = ''' expect_json = '''
@ -14,6 +15,7 @@ includes = ["vector", "utility", "cstdint"]
] ]
''' '''
[cases.uint64_uint32] [cases.uint64_uint32]
oil_skip = 'tests need updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/310
param_types = ["std::pair<std::uint64_t, std::uint32_t>&"] param_types = ["std::pair<std::uint64_t, std::uint32_t>&"]
setup = "return {{0, 1}};" setup = "return {{0, 1}};"
# Should still have static size of 16 due to padding # Should still have static size of 16 due to padding
@ -28,6 +30,7 @@ includes = ["vector", "utility", "cstdint"]
] ]
''' '''
[cases.vector_vector] [cases.vector_vector]
oil_skip = 'tests need updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/310
param_types = ["std::pair<std::vector<std::uint64_t>, std::vector<std::uint64_t>>&"] param_types = ["std::pair<std::vector<std::uint64_t>, std::vector<std::uint64_t>>&"]
setup = "return {{std::initializer_list<std::uint64_t>({0,1,2}), std::initializer_list<std::uint64_t>({3,4,5,6})}};" setup = "return {{std::initializer_list<std::uint64_t>({0,1,2}), std::initializer_list<std::uint64_t>({3,4,5,6})}};"
expect_json = ''' expect_json = '''

View File

@ -1,6 +1,7 @@
includes = ["queue"] includes = ["queue"]
[cases] [cases]
[cases.int_empty] [cases.int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/309
param_types = ["const std::priority_queue<int>&"] param_types = ["const std::priority_queue<int>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -13,6 +14,7 @@ includes = ["queue"]
} }
]}]''' ]}]'''
[cases.int_some] [cases.int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/309
param_types = ["const std::priority_queue<int>&"] param_types = ["const std::priority_queue<int>&"]
setup = "return std::priority_queue<int>({}, {3,2,1});" setup = "return std::priority_queue<int>({}, {3,2,1});"
expect_json = '''[{ expect_json = '''[{
@ -25,6 +27,7 @@ includes = ["queue"]
} }
]}]''' ]}]'''
[cases.adapter_deque_empty] [cases.adapter_deque_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/309
param_types = ["const std::priority_queue<int, std::deque<int>>&"] param_types = ["const std::priority_queue<int, std::deque<int>>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -37,6 +40,7 @@ includes = ["queue"]
} }
]}]''' ]}]'''
[cases.adapter_deque_some] [cases.adapter_deque_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/309
param_types = ["const std::priority_queue<int, std::deque<int>>&"] param_types = ["const std::priority_queue<int, std::deque<int>>&"]
setup = "return std::priority_queue<int, std::deque<int>>({}, {3,2,1});" setup = "return std::priority_queue<int, std::deque<int>>({}, {3,2,1});"
expect_json = '''[{ expect_json = '''[{

View File

@ -1,6 +1,7 @@
includes = ["queue"] includes = ["queue"]
[cases] [cases]
[cases.int_empty] [cases.int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<int>&"] param_types = ["const std::queue<int>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -13,6 +14,7 @@ includes = ["queue"]
} }
]}]''' ]}]'''
[cases.int_some] [cases.int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<int>&"] param_types = ["const std::queue<int>&"]
setup = "return std::queue<int>({1,2,3});" setup = "return std::queue<int>({1,2,3});"
expect_json = '''[{ expect_json = '''[{
@ -25,6 +27,7 @@ includes = ["queue"]
} }
]}]''' ]}]'''
[cases.queue_int_empty] [cases.queue_int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<std::queue<int>>&"] param_types = ["const std::queue<std::queue<int>>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -35,6 +38,7 @@ includes = ["queue"]
} }
]}]''' ]}]'''
[cases.queue_int_some] [cases.queue_int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<std::queue<int>>&"] param_types = ["const std::queue<std::queue<int>>&"]
setup = ''' setup = '''
return std::queue<std::queue<int>>({ return std::queue<std::queue<int>>({
@ -83,6 +87,7 @@ includes = ["queue"]
} }
]}]''' ]}]'''
[cases.adapter_vector_empty] [cases.adapter_vector_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<int, std::vector<int>>&"] param_types = ["const std::queue<int, std::vector<int>>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -95,6 +100,7 @@ includes = ["queue"]
} }
]}]''' ]}]'''
[cases.adapter_vector_some] [cases.adapter_vector_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<int, std::vector<int>>&"] param_types = ["const std::queue<int, std::vector<int>>&"]
setup = "return std::queue<int, std::vector<int>>({1,2,3});" setup = "return std::queue<int, std::vector<int>>({1,2,3});"
expect_json = '''[{ expect_json = '''[{

View File

@ -1,10 +1,12 @@
includes = ["functional"] includes = ["functional"]
[cases] [cases]
[cases.int] [cases.int]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/307
param_types = ["const std::reference_wrapper<int>&"] param_types = ["const std::reference_wrapper<int>&"]
setup = "return std::ref(*new int(1));" setup = "return std::ref(*new int(1));"
expect_json = '[{"staticSize":8, "dynamicSize":4, "length":1, "capacity":1, "elementStaticSize":4}]' expect_json = '[{"staticSize":8, "dynamicSize":4, "length":1, "capacity":1, "elementStaticSize":4}]'
[cases.vector] [cases.vector]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/307
param_types = ["const std::vector<std::reference_wrapper<int>>&"] param_types = ["const std::vector<std::reference_wrapper<int>>&"]
setup = "return {{std::ref(*new int(1)), std::ref(*new int(2)), std::ref(*new int(3))}};" setup = "return {{std::ref(*new int(1)), std::ref(*new int(2)), std::ref(*new int(3))}};"
expect_json = '''[{ expect_json = '''[{

View File

@ -31,6 +31,7 @@ includes = ["set", "functional"]
[cases] [cases]
[cases.a] [cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/306
param_types = ["const Foo&"] param_types = ["const Foo&"]
setup = ''' setup = '''
Foo foo; Foo foo;

View File

@ -7,6 +7,7 @@ definitions = '''
''' '''
[cases] [cases]
[cases.unique_ptr_uint64_empty] [cases.unique_ptr_uint64_empty]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<std::uint64_t>&"] param_types = ["std::unique_ptr<std::uint64_t>&"]
setup = "return {nullptr};" setup = "return {nullptr};"
expect_json = ''' expect_json = '''
@ -22,6 +23,7 @@ definitions = '''
] ]
''' '''
[cases.unique_ptr_uint64_present] [cases.unique_ptr_uint64_present]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<std::uint64_t>&"] param_types = ["std::unique_ptr<std::uint64_t>&"]
setup = "return {std::make_unique<std::uint64_t>(64)};" setup = "return {std::make_unique<std::uint64_t>(64)};"
expect_json = ''' expect_json = '''
@ -36,6 +38,7 @@ definitions = '''
] ]
''' '''
[cases.unique_ptr_vector_empty] [cases.unique_ptr_vector_empty]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<std::vector<std::uint64_t>>&"] param_types = ["std::unique_ptr<std::vector<std::uint64_t>>&"]
setup = "return {nullptr};" setup = "return {nullptr};"
expect_json = ''' expect_json = '''
@ -50,6 +53,7 @@ definitions = '''
] ]
''' '''
[cases.unique_ptr_vector_present] [cases.unique_ptr_vector_present]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<std::vector<std::uint64_t>>&"] param_types = ["std::unique_ptr<std::vector<std::uint64_t>>&"]
setup = "return {std::make_unique<std::vector<std::uint64_t>>(std::initializer_list<std::uint64_t>({1,2,3,4,5}))};" setup = "return {std::make_unique<std::vector<std::uint64_t>>(std::initializer_list<std::uint64_t>({1,2,3,4,5}))};"
expect_json = ''' expect_json = '''
@ -72,6 +76,7 @@ definitions = '''
] ]
''' '''
[cases.unique_ptr_void_empty] [cases.unique_ptr_void_empty]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<void, decltype(&void_int_deleter)>&"] param_types = ["std::unique_ptr<void, decltype(&void_int_deleter)>&"]
setup = "return {std::unique_ptr<void, decltype(&void_int_deleter)>(nullptr, &void_int_deleter)};" setup = "return {std::unique_ptr<void, decltype(&void_int_deleter)>(nullptr, &void_int_deleter)};"
expect_json = ''' expect_json = '''
@ -83,6 +88,7 @@ definitions = '''
] ]
''' '''
[cases.unique_ptr_void_present] [cases.unique_ptr_void_present]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<void, decltype(&void_int_deleter)>&"] param_types = ["std::unique_ptr<void, decltype(&void_int_deleter)>&"]
setup = "return {std::unique_ptr<void, decltype(&void_int_deleter)>(new int, &void_int_deleter)};" setup = "return {std::unique_ptr<void, decltype(&void_int_deleter)>(new int, &void_int_deleter)};"
expect_json = ''' expect_json = '''
@ -94,6 +100,7 @@ definitions = '''
] ]
''' '''
[cases.shared_ptr_uint64_empty] [cases.shared_ptr_uint64_empty]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<std::uint64_t>&"] param_types = ["std::shared_ptr<std::uint64_t>&"]
setup = "return {nullptr};" setup = "return {nullptr};"
expect_json = ''' expect_json = '''
@ -108,6 +115,7 @@ definitions = '''
] ]
''' '''
[cases.shared_ptr_uint64_present] [cases.shared_ptr_uint64_present]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<std::uint64_t>&"] param_types = ["std::shared_ptr<std::uint64_t>&"]
setup = "return std::make_shared<std::uint64_t>(64);" setup = "return std::make_shared<std::uint64_t>(64);"
expect_json = ''' expect_json = '''
@ -122,6 +130,7 @@ definitions = '''
] ]
''' '''
[cases.shared_ptr_vector_empty] [cases.shared_ptr_vector_empty]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<std::vector<std::uint64_t>>&"] param_types = ["std::shared_ptr<std::vector<std::uint64_t>>&"]
setup = "return {nullptr};" setup = "return {nullptr};"
expect_json = ''' expect_json = '''
@ -136,6 +145,7 @@ definitions = '''
] ]
''' '''
[cases.shared_ptr_vector_present] [cases.shared_ptr_vector_present]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<std::vector<std::uint64_t>>&"] param_types = ["std::shared_ptr<std::vector<std::uint64_t>>&"]
setup = "return std::make_shared<std::vector<std::uint64_t>>(std::initializer_list<std::uint64_t>({1,2,3,4,5}));" setup = "return std::make_shared<std::vector<std::uint64_t>>(std::initializer_list<std::uint64_t>({1,2,3,4,5}));"
expect_json = ''' expect_json = '''
@ -156,6 +166,7 @@ definitions = '''
] ]
''' '''
[cases.shared_ptr_void_empty] [cases.shared_ptr_void_empty]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<void>&"] param_types = ["std::shared_ptr<void>&"]
setup = "return {nullptr};" setup = "return {nullptr};"
expect_json = ''' expect_json = '''
@ -168,6 +179,7 @@ definitions = '''
] ]
''' '''
[cases.shared_ptr_void_present] [cases.shared_ptr_void_present]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<void>&"] param_types = ["std::shared_ptr<void>&"]
setup = "return {std::shared_ptr<void>(new int)};" setup = "return {std::shared_ptr<void>(new int)};"
expect_json = ''' expect_json = '''
@ -192,6 +204,7 @@ definitions = '''
} }
] ]
''' '''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
[cases.weak_ptr_int64_void_empty] [cases.weak_ptr_int64_void_empty]
param_types = ["std::weak_ptr<void>&"] param_types = ["std::weak_ptr<void>&"]
setup = "return std::weak_ptr<void>();" setup = "return std::weak_ptr<void>();"
@ -205,6 +218,7 @@ definitions = '''
} }
] ]
''' '''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
[cases.weak_ptr_int64_present] [cases.weak_ptr_int64_present]
param_types = ["std::weak_ptr<std::uint64_t>&"] param_types = ["std::weak_ptr<std::uint64_t>&"]
setup = ''' setup = '''
@ -222,6 +236,7 @@ definitions = '''
} }
] ]
''' '''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
[cases.weak_ptr_int64_expired] [cases.weak_ptr_int64_expired]
param_types = ["std::weak_ptr<std::uint64_t>&"] param_types = ["std::weak_ptr<std::uint64_t>&"]
setup = ''' setup = '''
@ -239,6 +254,7 @@ definitions = '''
} }
] ]
''' '''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
[cases.weak_ptr_int64_present_chase] [cases.weak_ptr_int64_present_chase]
param_types = ["std::weak_ptr<std::uint64_t>&"] param_types = ["std::weak_ptr<std::uint64_t>&"]
cli_options = ["-fchase-raw-pointers"] cli_options = ["-fchase-raw-pointers"]
@ -257,6 +273,7 @@ definitions = '''
} }
] ]
''' '''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
[cases.weak_ptr_int64_expired_chase] [cases.weak_ptr_int64_expired_chase]
param_types = ["std::weak_ptr<std::uint64_t>&"] param_types = ["std::weak_ptr<std::uint64_t>&"]
cli_options = ["-fchase-raw-pointers"] cli_options = ["-fchase-raw-pointers"]
@ -273,3 +290,4 @@ definitions = '''
} }
] ]
''' '''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''

View File

@ -1,6 +1,7 @@
includes = ["stack", "vector"] includes = ["stack", "vector"]
[cases] [cases]
[cases.int_empty] [cases.int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<int>&"] param_types = ["const std::stack<int>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -13,6 +14,7 @@ includes = ["stack", "vector"]
} }
]}]''' ]}]'''
[cases.int_some] [cases.int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<int>&"] param_types = ["const std::stack<int>&"]
setup = "return std::stack<int>({1,2,3});" setup = "return std::stack<int>({1,2,3});"
expect_json = '''[{ expect_json = '''[{
@ -25,6 +27,7 @@ includes = ["stack", "vector"]
} }
]}]''' ]}]'''
[cases.stack_int_empty] [cases.stack_int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<std::stack<int>>&"] param_types = ["const std::stack<std::stack<int>>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -35,6 +38,7 @@ includes = ["stack", "vector"]
} }
]}]''' ]}]'''
[cases.stack_int_some] [cases.stack_int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<std::stack<int>>&"] param_types = ["const std::stack<std::stack<int>>&"]
setup = ''' setup = '''
return std::stack<std::stack<int>>({ return std::stack<std::stack<int>>({
@ -83,6 +87,7 @@ includes = ["stack", "vector"]
} }
]}]''' ]}]'''
[cases.adapter_vector_empty] [cases.adapter_vector_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<int, std::vector<int>>&"] param_types = ["const std::stack<int, std::vector<int>>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -95,6 +100,7 @@ includes = ["stack", "vector"]
} }
]}]''' ]}]'''
[cases.adapter_vector_some] [cases.adapter_vector_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<int, std::vector<int>>&"] param_types = ["const std::stack<int, std::vector<int>>&"]
setup = "return std::stack<int, std::vector<int>>({1,2,3});" setup = "return std::stack<int, std::vector<int>>({1,2,3});"
expect_json = '''[{ expect_json = '''[{

View File

@ -1,6 +1,7 @@
includes = ["string"] includes = ["string"]
[cases] [cases]
[cases.empty] [cases.empty]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311
param_types = ["std::string&"] param_types = ["std::string&"]
setup = "return {};" setup = "return {};"
expect_json = ''' expect_json = '''
@ -25,6 +26,7 @@ includes = ["string"]
] ]
''' '''
[cases.sso] [cases.sso]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311
param_types = ["std::string&"] param_types = ["std::string&"]
setup = 'return {"012345"};' setup = 'return {"012345"};'
expect_json = ''' expect_json = '''
@ -49,6 +51,7 @@ includes = ["string"]
] ]
''' '''
[cases.heap_allocated] [cases.heap_allocated]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311
param_types = ["std::string&"] param_types = ["std::string&"]
setup = 'return {"abcdefghijklmnopqrstuvwxzy"};' setup = 'return {"abcdefghijklmnopqrstuvwxzy"};'
expect_json = ''' expect_json = '''

View File

@ -9,6 +9,7 @@ struct Bar {
''' '''
[cases] [cases]
[cases.uint64_uint64] [cases.uint64_uint64]
oil_skip = "std::tuple is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/304
param_types = ["Bar&"] param_types = ["Bar&"]
setup = ''' setup = '''
Foo f; Foo f;

View File

@ -33,6 +33,7 @@ includes = ["unordered_map"]
[cases] [cases]
[cases.a] [cases.a]
oil_skip = "std::unordered_map is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/303
param_types = ["const Foo&"] param_types = ["const Foo&"]
setup = ''' setup = '''
Foo foo; Foo foo;

View File

@ -33,6 +33,7 @@ includes = ["unordered_set"]
[cases] [cases]
[cases.a] [cases.a]
oil_skip = "std::unordered_set is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/302
param_types = ["const Foo&"] param_types = ["const Foo&"]
setup = ''' setup = '''
Foo foo; Foo foo;

View File

@ -9,6 +9,7 @@ definitions = '''
[cases] [cases]
[cases.char_int64_1] [cases.char_int64_1]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<char, int64_t>&"] param_types = ["const std::variant<char, int64_t>&"]
setup = "return 'a';" setup = "return 'a';"
expect_json = '''[{ expect_json = '''[{
@ -22,6 +23,7 @@ definitions = '''
{"typeName":"char", "staticSize":1, "exclusiveSize":1, "dynamicSize":0} {"typeName":"char", "staticSize":1, "exclusiveSize":1, "dynamicSize":0}
]}]''' ]}]'''
[cases.char_int64_2] [cases.char_int64_2]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<char, int64_t>&"] param_types = ["const std::variant<char, int64_t>&"]
setup = "return 1234;" setup = "return 1234;"
expect_json = '''[{ expect_json = '''[{
@ -36,6 +38,7 @@ definitions = '''
]}]''' ]}]'''
[cases.vector_int_1] [cases.vector_int_1]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<std::vector<int>, int>&"] param_types = ["const std::variant<std::vector<int>, int>&"]
setup = "return std::vector<int>{1,2,3};" setup = "return std::vector<int>{1,2,3};"
expect_json = '''[{ expect_json = '''[{
@ -57,6 +60,7 @@ definitions = '''
} }
]}]''' ]}]'''
[cases.vector_int_2] [cases.vector_int_2]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<std::vector<int>, int>&"] param_types = ["const std::variant<std::vector<int>, int>&"]
setup = "return 123;" setup = "return 123;"
expect_json = '''[{ expect_json = '''[{
@ -74,6 +78,7 @@ definitions = '''
]}]''' ]}]'''
[cases.optional] [cases.optional]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
# This test case ensures that the alignment of std::variant is set # This test case ensures that the alignment of std::variant is set
# correctly, as otherwise the size of the std::optional would be wrong # correctly, as otherwise the size of the std::optional would be wrong
param_types = ["const std::optional<std::variant<char, int64_t>>&"] param_types = ["const std::optional<std::variant<char, int64_t>>&"]
@ -100,6 +105,7 @@ definitions = '''
]}]''' ]}]'''
[cases.empty] [cases.empty]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
# https://en.cppreference.com/w/cpp/utility/variant/valueless_by_exception # https://en.cppreference.com/w/cpp/utility/variant/valueless_by_exception
param_types = ["const std::variant<int, Thrower>&"] param_types = ["const std::variant<int, Thrower>&"]
setup = ''' setup = '''
@ -127,6 +133,7 @@ definitions = '''
# 0xff can be a valid index if there are at least 256 parameters, and that # 0xff can be a valid index if there are at least 256 parameters, and that
# the invalid index value is raised to 0xffff. # the invalid index value is raised to 0xffff.
[cases.256_params_256] [cases.256_params_256]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<Thrower,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,char>&"] param_types = ["const std::variant<Thrower,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,char>&"]
setup = "return 'a';" setup = "return 'a';"
expect_json = '''[{ expect_json = '''[{
@ -140,6 +147,7 @@ definitions = '''
{"typeName":"char", "staticSize":1, "exclusiveSize":1, "dynamicSize":0} {"typeName":"char", "staticSize":1, "exclusiveSize":1, "dynamicSize":0}
]}]''' ]}]'''
[cases.256_params_empty] [cases.256_params_empty]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<Thrower,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,char>&"] param_types = ["const std::variant<Thrower,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,char>&"]
setup = ''' setup = '''
std::variant<Thrower,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,char> var{'a'}; std::variant<Thrower,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,char> var{'a'};

View File

@ -4,10 +4,16 @@ includes = ["vector"]
param_types = ["const std::vector<int>&"] param_types = ["const std::vector<int>&"]
setup = "return {};" setup = "return {};"
expect_json = '[{"staticSize":24, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}]' expect_json = '[{"staticSize":24, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}]'
expect_json_v2 = '[{"staticSize":24, "exclusiveSize":24, "length":0, "capacity":0, "members":[]}]'
[cases.int_some] [cases.int_some]
param_types = ["const std::vector<int>&"] param_types = ["const std::vector<int>&"]
setup = "return {{1,2,3}};" setup = "return {{1,2,3}};"
expect_json = '[{"staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}]' expect_json = '[{"staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}]'
expect_json_v2 = '''[{"staticSize":24, "exclusiveSize":24, "length":3, "capacity":3, "members":[
{"staticSize":4, "exclusiveSize":4},
{"staticSize":4, "exclusiveSize":4},
{"staticSize":4, "exclusiveSize":4}
]}]'''
[cases.bool_empty] [cases.bool_empty]
skip = true # https://github.com/facebookexperimental/object-introspection/issues/14 skip = true # https://github.com/facebookexperimental/object-introspection/issues/14
param_types = ["const std::vector<bool>&"] param_types = ["const std::vector<bool>&"]
@ -22,6 +28,7 @@ includes = ["vector"]
param_types = ["const std::vector<std::vector<int>>&"] param_types = ["const std::vector<std::vector<int>>&"]
setup = "return {};" setup = "return {};"
expect_json = '[{"staticSize":24, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":24}]' expect_json = '[{"staticSize":24, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":24}]'
expect_json_v2 = '[{"staticSize":24, "exclusiveSize":24, "length":0, "capacity":0, "members":[]}]'
[cases.vector_int_some] [cases.vector_int_some]
param_types = ["const std::vector<std::vector<int>>&"] param_types = ["const std::vector<std::vector<int>>&"]
setup = "return {{{1,2,3},{4},{5,6}}};" setup = "return {{{1,2,3},{4},{5,6}}};"
@ -37,6 +44,11 @@ includes = ["vector"]
{"staticSize":24, "dynamicSize":4, "exclusiveSize":28, "length":1, "capacity":1, "elementStaticSize":4}, {"staticSize":24, "dynamicSize":4, "exclusiveSize":28, "length":1, "capacity":1, "elementStaticSize":4},
{"staticSize":24, "dynamicSize":8, "exclusiveSize":32, "length":2, "capacity":2, "elementStaticSize":4} {"staticSize":24, "dynamicSize":8, "exclusiveSize":32, "length":2, "capacity":2, "elementStaticSize":4}
]}]''' ]}]'''
expect_json_v2 = '''[{"staticSize":24, "exclusiveSize":24, "length":3, "capacity": 3, "members":[
{"staticSize":24, "exclusiveSize":24, "length":3, "capacity": 3, "members":[]},
{"staticSize":24, "exclusiveSize":24, "length":1, "capacity": 1, "members":[]},
{"staticSize":24, "exclusiveSize":24, "length":2, "capacity": 2, "members":[]}
]}]'''
[cases.reserve] [cases.reserve]
param_types = ["const std::vector<int>&"] param_types = ["const std::vector<int>&"]
setup = ''' setup = '''
@ -45,3 +57,8 @@ includes = ["vector"]
return ret; return ret;
''' '''
expect_json = '[{"staticSize":24, "dynamicSize":40, "exclusiveSize":64, "length":3, "capacity":10, "elementStaticSize":4}]' expect_json = '[{"staticSize":24, "dynamicSize":40, "exclusiveSize":64, "length":3, "capacity":10, "elementStaticSize":4}]'
expect_json_v2 = '''[{"staticSize":24, "exclusiveSize":52, "length":3, "capacity":10, "members":[
{"staticSize":4, "exclusiveSize":4},
{"staticSize":4, "exclusiveSize":4},
{"staticSize":4, "exclusiveSize":4}
]}]'''

View File

@ -27,6 +27,7 @@ includes = ["vector"]
[cases] [cases]
[cases.a] [cases.a]
oil_skip = "oil gets the exclusive size of vector subfields wrong" # https://github.com/facebookexperimental/object-introspection/issues/301
param_types = ["const Foo&"] param_types = ["const Foo&"]
setup = ''' setup = '''
Foo foo; Foo foo;
@ -57,3 +58,22 @@ includes = ["vector"]
]} ]}
]} ]}
]}]''' ]}]'''
expect_json_v2 = '''[{
"staticSize":48,
"exclusiveSize":0,
"members":[
{"name":"v1", "staticSize":24, "exclusiveSize":24, "length":1, "capacity":1, "members":[
{"name":"[]", "staticSize":4, "exclusiveSize":0, "members":[
{"name":"a", "staticSize":4, "exclusiveSize":4, "members":[]}
]}
]},
{"name":"v2", "staticSize":24, "exclusiveSize":24, "length":2, "capacity":2, "members":[
{"name":"[]", "staticSize":4, "exclusiveSize":0, "members":[
{"name":"b", "staticSize":4, "exclusiveSize":4, "members":[]}
]},
{"name":"[]", "staticSize":4, "exclusiveSize":0, "members":[
{"name":"b", "staticSize":4, "exclusiveSize":4, "members":[]}
]}
]}
]
}]'''

View File

@ -24,6 +24,7 @@ definitions = '''
[cases] [cases]
[cases.int] [cases.int]
oil_skip = "primitives are named differently in treebuilderv2" # https://github.com/facebookexperimental/object-introspection/issues/286
param_types = ["const TemplatedClass1<int>&"] param_types = ["const TemplatedClass1<int>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -48,7 +49,19 @@ definitions = '''
"capacity":0, "capacity":0,
"elementStaticSize":4 "elementStaticSize":4
}]}]''' }]}]'''
expect_json_v2 = '''[{
"typeName":"TemplatedClass1<std::vector<int, std::allocator<int> > >",
"staticSize":24,
"exclusiveSize":0,
"members":[{
"typeName":"std::vector<int32_t, std::allocator<int32_t>>",
"staticSize":24,
"exclusiveSize":24,
"length":0,
"capacity":0
}]}]'''
[cases.two] [cases.two]
oil_skip = "OIL returns better primitive names"
param_types = ["const TemplatedClass2<Foo, int>&"] param_types = ["const TemplatedClass2<Foo, int>&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -73,3 +86,13 @@ definitions = '''
"capacity":3, "capacity":3,
"elementStaticSize":4 "elementStaticSize":4
}]}]''' }]}]'''
expect_json_v2 = '''[{
"typeName":"TemplatedClassVal<3>",
"staticSize":12,
"exclusiveSize":0,
"members":[{
"staticSize":12,
"exclusiveSize":0,
"length":3,
"capacity":3
}]}]'''

View File

@ -77,6 +77,7 @@ namespace cpp2 {
[cases] [cases]
[cases.unpacked] [cases.unpacked]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructUnpacked&"] param_types = ["const cpp2::MyThriftStructUnpacked&"]
setup = ''' setup = '''
cpp2::MyThriftStructUnpacked ret; cpp2::MyThriftStructUnpacked ret;
@ -96,6 +97,7 @@ namespace cpp2 {
]}]''' ]}]'''
[cases.packed] [cases.packed]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructPacked&"] param_types = ["const cpp2::MyThriftStructPacked&"]
setup = ''' setup = '''
cpp2::MyThriftStructPacked ret; cpp2::MyThriftStructPacked ret;
@ -126,6 +128,7 @@ namespace cpp2 {
]}]''' ]}]'''
[cases.packed_non_atomic] [cases.packed_non_atomic]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructPackedNonAtomic&"] param_types = ["const cpp2::MyThriftStructPackedNonAtomic&"]
setup = ''' setup = '''
cpp2::MyThriftStructPackedNonAtomic ret; cpp2::MyThriftStructPackedNonAtomic ret;
@ -146,6 +149,7 @@ namespace cpp2 {
]}]''' ]}]'''
[cases.out_of_order] [cases.out_of_order]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructOutOfOrder&"] param_types = ["const cpp2::MyThriftStructOutOfOrder&"]
setup = ''' setup = '''
cpp2::MyThriftStructOutOfOrder ret; cpp2::MyThriftStructOutOfOrder ret;
@ -164,6 +168,7 @@ namespace cpp2 {
]}]''' ]}]'''
[cases.required] [cases.required]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructRequired&"] param_types = ["const cpp2::MyThriftStructRequired&"]
setup = ''' setup = '''
cpp2::MyThriftStructRequired ret; cpp2::MyThriftStructRequired ret;
@ -186,6 +191,7 @@ namespace cpp2 {
]}]''' ]}]'''
[cases.box] [cases.box]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructBoxed&"] param_types = ["const cpp2::MyThriftStructBoxed&"]
setup = ''' setup = '''
cpp2::MyThriftStructBoxed ret; cpp2::MyThriftStructBoxed ret;
@ -207,6 +213,7 @@ namespace cpp2 {
]}]''' ]}]'''
[cases.no_capture] [cases.no_capture]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructBoxed&"] param_types = ["const cpp2::MyThriftStructBoxed&"]
setup = ''' setup = '''
cpp2::MyThriftStructBoxed ret; cpp2::MyThriftStructBoxed ret;

View File

@ -58,6 +58,7 @@ raw_definitions = '''
''' '''
[cases] [cases]
[cases.present] [cases.present]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const FakeThriftWithData&"] param_types = ["const FakeThriftWithData&"]
setup = ''' setup = '''
FakeThriftWithData ret; FakeThriftWithData ret;
@ -78,6 +79,7 @@ raw_definitions = '''
{"name":"__isset", "staticSize":3} {"name":"__isset", "staticSize":3}
]}]''' ]}]'''
[cases.missing] [cases.missing]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const FakeThriftWithoutData&"] param_types = ["const FakeThriftWithoutData&"]
setup = ''' setup = '''
FakeThriftWithoutData ret; FakeThriftWithoutData ret;
@ -98,6 +100,7 @@ raw_definitions = '''
{"name":"__isset", "staticSize":3} {"name":"__isset", "staticSize":3}
]}]''' ]}]'''
[cases.mixed] [cases.mixed]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const Mixed&"] param_types = ["const Mixed&"]
setup = ''' setup = '''
Mixed ret; Mixed ret;

View File

@ -10,6 +10,7 @@ thrift_definitions = '''
[cases] [cases]
[cases.a] [cases.a]
oil_skip = 'enum type template arguments do not match' # https://github.com/facebookexperimental/object-introspection/issues/297
param_types = ["const namespaceA::namespaceB::TTTTT&"] param_types = ["const namespaceA::namespaceB::TTTTT&"]
setup = ''' setup = '''
namespaceA::namespaceB::TTTTT ret; namespaceA::namespaceB::TTTTT ret;

View File

@ -33,6 +33,13 @@ namespace cpp2 {
{"typeName":"storage_type", "name":"value_", "staticSize":4}, {"typeName":"storage_type", "name":"value_", "staticSize":4},
{"typeName":"underlying_type_t<cpp2::StaticUnion::Type>", "name":"type_", "staticSize":4} {"typeName":"underlying_type_t<cpp2::StaticUnion::Type>", "name":"type_", "staticSize":4}
]}]''' ]}]'''
expect_json_v2 = '''[{
"staticSize":8,
"exclusiveSize":0,
"members":[
{"typeNames":["storage_type"], "name":"value_", "staticSize":4, "exclusiveSize":4},
{"typeNames":["underlying_type_t<cpp2::StaticUnion::Type>", "type", "int32_t"], "name":"type_", "staticSize":4, "exclusiveSize":4}
]}]'''
[cases.dynamic_int] [cases.dynamic_int]
param_types = ["const cpp2::DynamicUnion&"] param_types = ["const cpp2::DynamicUnion&"]
setup = ''' setup = '''
@ -47,6 +54,13 @@ namespace cpp2 {
{"typeName":"storage_type", "name":"value_", "staticSize":24}, {"typeName":"storage_type", "name":"value_", "staticSize":24},
{"typeName":"underlying_type_t<cpp2::DynamicUnion::Type>", "name":"type_", "staticSize":4} {"typeName":"underlying_type_t<cpp2::DynamicUnion::Type>", "name":"type_", "staticSize":4}
]}]''' ]}]'''
expect_json_v2 = '''[{
"staticSize":32,
"exclusiveSize":4,
"members":[
{"typeNames":["storage_type"], "name":"value_", "staticSize":24, "exclusiveSize":24},
{"typeNames":["underlying_type_t<cpp2::DynamicUnion::Type>", "type", "int32_t"], "name":"type_", "staticSize":4, "exclusiveSize":4}
]}]'''
[cases.dynamic_vec] [cases.dynamic_vec]
param_types = ["const cpp2::DynamicUnion&"] param_types = ["const cpp2::DynamicUnion&"]
setup = ''' setup = '''
@ -61,3 +75,10 @@ namespace cpp2 {
{"typeName":"storage_type", "name":"value_", "staticSize":24}, {"typeName":"storage_type", "name":"value_", "staticSize":24},
{"typeName":"underlying_type_t<cpp2::DynamicUnion::Type>", "name":"type_", "staticSize":4} {"typeName":"underlying_type_t<cpp2::DynamicUnion::Type>", "name":"type_", "staticSize":4}
]}]''' ]}]'''
expect_json_v2 = '''[{
"staticSize":32,
"exclusiveSize":4,
"members":[
{"typeNames":["storage_type"], "name":"value_", "staticSize":24, "exclusiveSize":24},
{"typeNames":["underlying_type_t<cpp2::DynamicUnion::Type>", "type", "int32_t"], "name":"type_", "staticSize":4, "exclusiveSize":4}
]}]'''

View File

@ -27,6 +27,13 @@ definitions = '''
{"name":"a", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3}, {"name":"a", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3},
{"name":"b", "staticSize":24, "dynamicSize":20, "length":5, "capacity":5} {"name":"b", "staticSize":24, "dynamicSize":20, "length":5, "capacity":5}
]}]''' ]}]'''
expect_json_v2 = '''[{
"staticSize":48,
"exclusiveSize":0,
"members":[
{"name":"a", "staticSize":24, "exclusiveSize":24, "length":3, "capacity":3},
{"name":"b", "staticSize":24, "exclusiveSize":24, "length":5, "capacity":5}
]}]'''
[cases.multilevel_typedef_parent] [cases.multilevel_typedef_parent]
param_types = ["const Bar_2&"] param_types = ["const Bar_2&"]
setup = ''' setup = '''
@ -40,3 +47,10 @@ definitions = '''
{"name":"a", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3}, {"name":"a", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3},
{"name":"c", "staticSize":24, "dynamicSize":20, "length":5, "capacity":5} {"name":"c", "staticSize":24, "dynamicSize":20, "length":5, "capacity":5}
]}]''' ]}]'''
expect_json_v2 = '''[{
"staticSize":48,
"exclusiveSize":0,
"members":[
{"name":"a", "staticSize":24, "exclusiveSize":24, "length":3, "capacity":3},
{"name":"c", "staticSize":24, "exclusiveSize":24, "length":5, "capacity":5}
]}]'''

View File

@ -4,9 +4,15 @@ definitions = '''
using UsingUInt64 = uint64_t; using UsingUInt64 = uint64_t;
using IntVector = std::vector<int>; using IntVector = std::vector<int>;
using Anonymous = struct { int n; }; using Anonymous = struct { int n; };
template <typename T>
struct Proxy {
T t;
};
''' '''
[cases] [cases]
[cases.c_style] [cases.c_style]
oil_disable = "oil can't pick up top level typedefs"
param_types = ["TdUInt64"] param_types = ["TdUInt64"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -24,6 +30,7 @@ definitions = '''
} }
]}]''' ]}]'''
[cases.using] [cases.using]
oil_disable = "oil can't pick up top level typedefs"
param_types = ["UsingUInt64"] param_types = ["UsingUInt64"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -41,6 +48,7 @@ definitions = '''
} }
]}]''' ]}]'''
[cases.container] [cases.container]
oil_disable = "oil can't pick up top level typedefs"
param_types = ["const IntVector&"] param_types = ["const IntVector&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -61,7 +69,7 @@ definitions = '''
} }
]}]''' ]}]'''
[cases.anonymous] [cases.anonymous]
oil_skip = "TreeBuilder crashes" # https://github.com/facebookexperimental/object-introspection/issues/232 oil_disable = "oil can't pick up top level typedefs"
param_types = ["const Anonymous&"] param_types = ["const Anonymous&"]
setup = "return {};" setup = "return {};"
expect_json = '''[{ expect_json = '''[{
@ -86,3 +94,4 @@ definitions = '''
} }
]} ]}
]}]''' ]}]'''

View File

@ -45,14 +45,17 @@ definitions = '''
param_types = ["const MyUnion&"] param_types = ["const MyUnion&"]
setup = "return 123;" setup = "return 123;"
expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]' expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]'
expect_json_v2 = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}]'
[cases.vector] [cases.vector]
param_types = ["const MyUnion&"] param_types = ["const MyUnion&"]
setup = "return std::vector{1,2,3};" setup = "return std::vector{1,2,3};"
expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]' expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]'
expect_json_v2 = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}]'
[cases.unordered_map] [cases.unordered_map]
param_types = ["const MyUnion&"] param_types = ["const MyUnion&"]
setup = 'return std::unordered_map<std::string, std::string>{{"a", "b"}, {"c","d"}};' setup = 'return std::unordered_map<std::string, std::string>{{"a", "b"}, {"c","d"}};'
expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]' expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]'
expect_json_v2 = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}]'
[cases.alignment] [cases.alignment]
# Wrap the union in a pair as a way of inferring its alignment # Wrap the union in a pair as a way of inferring its alignment
@ -63,6 +66,11 @@ definitions = '''
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1}, {"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"} {"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}
]}]''' ]}]'''
expect_json_v2 = '''[
{"staticSize":64, "dynamicSize":0, "exclusiveSize":7, "members":[
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}
]}]'''
[cases.tagged_int] [cases.tagged_int]
param_types = ["const TaggedUnion&"] param_types = ["const TaggedUnion&"]
@ -72,6 +80,11 @@ definitions = '''
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}, {"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "NOT":"members"} {"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "NOT":"members"}
]}]''' ]}]'''
expect_json_v2 = '''[
{"staticSize":64, "dynamicSize":0, "exclusiveSize":7, "members":[
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "members":[]}
]}]'''
[cases.tagged_vector] [cases.tagged_vector]
param_types = ["const TaggedUnion&"] param_types = ["const TaggedUnion&"]
setup = "return TaggedUnion{MyUnion{std::vector{1,2,3}}, 1};" setup = "return TaggedUnion{MyUnion{std::vector{1,2,3}}, 1};"
@ -80,6 +93,11 @@ definitions = '''
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}, {"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "NOT":"members"} {"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "NOT":"members"}
]}]''' ]}]'''
expect_json_v2 = '''[
{"staticSize":64, "dynamicSize":0, "exclusiveSize":7, "members":[
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "members":[]}
]}]'''
[cases.tagged_unordered_map] [cases.tagged_unordered_map]
param_types = ["const TaggedUnion&"] param_types = ["const TaggedUnion&"]
setup = 'return TaggedUnion{MyUnion{std::unordered_map<std::string, std::string>{{"a", "b"}, {"c","d"}}}, 2};' setup = 'return TaggedUnion{MyUnion{std::unordered_map<std::string, std::string>{{"a", "b"}, {"c","d"}}}, 2};'
@ -88,3 +106,8 @@ definitions = '''
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}, {"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "NOT":"members"} {"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "NOT":"members"}
]}]''' ]}]'''
expect_json_v2 = '''[
{"staticSize":64, "dynamicSize":0, "exclusiveSize":7, "members":[
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "members":[]}
]}]'''

View File

@ -50,14 +50,16 @@ TEST_F(AddChildrenTest, SimpleStruct) {
TEST_F(AddChildrenTest, InheritanceStatic) { TEST_F(AddChildrenTest, InheritanceStatic) {
// Should do nothing // Should do nothing
test("oid_test_case_inheritance_access_public", R"( test("oid_test_case_inheritance_access_public", R"(
[2] Pointer [4] Pointer
[0] Class: Public (size: 8) [0] Class: Public (size: 8)
Parent (offset: 0) Parent (offset: 0)
[1] Class: Base (size: 4) [1] Class: Base (size: 4)
Member: base_int (offset: 0) Member: base_int (offset: 0)
Primitive: int32_t [3] Typedef: int32_t
[2] Typedef: __int32_t
Primitive: int32_t
Member: public_int (offset: 4) Member: public_int (offset: 4)
Primitive: int32_t [3]
)"); )");
} }

View File

@ -144,14 +144,16 @@ TEST_F(DrgnParserTest, SimpleUnion) {
TEST_F(DrgnParserTest, Inheritance) { TEST_F(DrgnParserTest, Inheritance) {
test("oid_test_case_inheritance_access_public", R"( test("oid_test_case_inheritance_access_public", R"(
[2] Pointer [4] Pointer
[0] Class: Public (size: 8) [0] Class: Public (size: 8)
Parent (offset: 0) Parent (offset: 0)
[1] Class: Base (size: 4) [1] Class: Base (size: 4)
Member: base_int (offset: 0) Member: base_int (offset: 0)
Primitive: int32_t [3] Typedef: int32_t
[2] Typedef: __int32_t
Primitive: int32_t
Member: public_int (offset: 4) Member: public_int (offset: 4)
Primitive: int32_t [3]
)"); )");
} }

View File

@ -446,7 +446,7 @@ TEST(NameGenTest, AnonymousTypes) {
EXPECT_EQ(myenum.name(), "__oi_anon_1"); EXPECT_EQ(myenum.name(), "__oi_anon_1");
EXPECT_EQ(mytypedef.name(), "__oi_anon_2"); EXPECT_EQ(mytypedef.name(), "__oi_anon_2");
EXPECT_EQ(myclass.inputName(), ""); EXPECT_EQ(myclass.inputName(), "__oi_anon_0");
EXPECT_EQ(myenum.inputName(), ""); EXPECT_EQ(myenum.inputName(), "__oi_anon_1");
EXPECT_EQ(mytypedef.inputName(), ""); EXPECT_EQ(mytypedef.inputName(), "");
} }

View File

@ -8,10 +8,6 @@ This document describes the format of the container definition files contained i
The fully-qualified name of the container type. This is used to match against The fully-qualified name of the container type. This is used to match against
the names of types contained in an executable's debug information. the names of types contained in an executable's debug information.
- `ctype`
A reference to the enum `ContainerTypeEnum` in OI's source code.
- `header` - `header`
The name of the C++ header file in which this container is defined. The name of the C++ header file in which this container is defined.
@ -35,42 +31,32 @@ This document describes the format of the container definition files contained i
C++ code for the definition of a `getSizeType` function for this container. C++ code for the definition of a `getSizeType` function for this container.
- `handler` - `processor.type`
C++ code for the definition of a `TypeHandler` class for this container. See The static type that will be filled in by this particular processor. Multiple
further down for a description. processors are allowed and recommended.
- `processor.func`
C++ code for the function body of a function with this signature:
`void(result::Element& el, std::stack<Inst>& ins, ParsedData d)` which is
supplied with the data of the paired static type.
- `traversal_func`
C++ code for the function body of a function with this signature:
`static types::st::Unit<DB> getSizeType(const T& container, ST returnArg)`
where `ST` defines the combined static type of each supplied processor.
## Changes introduced with Typed Data Segment ## Changes introduced with Typed Data Segment
- `decl` and `func` fields are ignored when using `-ftyped-data-segment` and the - `decl` and `func` fields are ignored when using `-ftyped-data-segment` and the
`handler` field is used instead. `handler` field is used instead.
### TypeHandler Classes ## Changes introduced with TreeBuilder V2
A `TypeHandler` class describes both what a type will write into the data segment - `decl`, `func`, and `handler` fields are ignored when using `-ftree-builder-v2`.
and how to write it. It consists of two major parts: The `TypeHandler` is instead constructed from `traversal_func` and `processor`
- `using type = ...;` - describe what it will write into the data segment. entries.
- `static types::st::Unit<DB> getSizeType(...)` - a function which takes a
const reference to a container and a `::type` by value and fills in the type.
Example:
```cpp
template <typename DB, typename T0>
struct TypeHandler<DB, std::string<T0>> {
using type =
types::st::Pair<DB, types::st::VarInt<DB>, types::st::VarInt<DB>>;
static types::st::Unit<DB> getSizeType(
const std::string<T0> & container,
typename TypeHandler<DB, std::string<T0>>::type returnArg) {
bool sso = ((uintptr_t)container.data() <
(uintptr_t)(&container + sizeof(std::string<T0>))) &&
((uintptr_t)container.data() >= (uintptr_t)&container);
return returnArg.write(container.capacity()).write(container.size());
}
};
```
## Changes introduced with TypeGraph ## Changes introduced with TypeGraph
- `typeName` and `matcher` fields have been merged into the single field `type_name`. - `typeName` and `matcher` fields have been merged into the single field `type_name`.
@ -81,6 +67,16 @@ struct TypeHandler<DB, std::string<T0>> {
- There is one option to specify template parameters which should not be measured: `stub_template_params`. The type graph code will automatically determine what type of stubbing to apply to each parameter in this list. - There is one option to specify template parameters which should not be measured: `stub_template_params`. The type graph code will automatically determine what type of stubbing to apply to each parameter in this list.
### Deprecated Options ### Deprecated Options
- `ctype`
A reference to the enum `ContainerTypeEnum` in OI's source code. This is no
longer required with Tree Builder v2.
- `handler`
C++ code for the definition of a `TypeHandler` class for this container. Now
generated from `traversal_func` and `processor` entries.
- `numTemplateParams` - `numTemplateParams`
The first `numTemplateParams` template parameters for this container represent The first `numTemplateParams` template parameters for this container represent

View File

@ -49,3 +49,34 @@ struct TypeHandler<DB, %1%<T0, N>> {
} }
}; };
""" """
traversal_func = """
auto tail = returnArg.write(container.size());
for (auto & it: container) {
tail = tail.delegate([&it](auto ret) {
return TypeHandler<DB, T0>::getSizeType(it, ret);
});
}
return tail.finish();
"""
[[codegen.processor]]
type = "types::st::List<DB, typename TypeHandler<DB, T0>::type>"
func = """
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr inst::Field childField{
sizeof(T0),
"[]",
names,
TypeHandler<DB, T0>::fields,
TypeHandler<DB, T0>::processors,
};
size_t size = std::get<ParsedData::List>(d.val).length;
el.exclusive_size = N0 == 0 ? 1 : 0;
el.container_stats.emplace(result::Element::ContainerStats{ .capacity = size, .length = size });
for (size_t i = 0; i < size; i++)
ins.emplace(childField);
"""

View File

@ -53,3 +53,34 @@ struct TypeHandler<DB, %1% <T0>> {
} }
}; };
""" """
traversal_func = """
bool sso = ((uintptr_t)container.data() <
(uintptr_t)(&container + sizeof(std::__cxx11::basic_string<T0>))) &&
((uintptr_t)container.data() >= (uintptr_t)&container);
return returnArg.write(container.capacity())
.write(sso)
.write(container.size());
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
uint64_t capacity = std::get<ParsedData::VarInt>(d.val).value;
el.container_stats.emplace(result::Element::ContainerStats { .capacity = capacity });
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
bool sso = std::get<ParsedData::VarInt>(d.val).value;
if (!sso)
el.exclusive_size += el.container_stats->capacity * sizeof(T0);
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
el.container_stats->length = std::get<ParsedData::VarInt>(d.val).value;
"""

View File

@ -66,3 +66,73 @@ struct TypeHandler<DB, %1%<T0, T1, T2, T3, T4, T5>> {
} }
}; };
""" """
traversal_func = '''
auto tail = returnArg.write((uintptr_t)&container)
.write(container.capacity())
.write(container.size());
for (const auto& kv : container) {
tail = tail.delegate([&kv](auto ret) {
auto next = ret.delegate([&kv](typename TypeHandler<DB, T0>::type ret) {
return OIInternal::getSizeType<DB>(kv.first, ret);
});
return OIInternal::getSizeType<DB>(kv.second, next);
});
}
return tail.finish();
'''
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = "el.pointer = std::get<ParsedData::VarInt>(d.val).value;"
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = '''
el.container_stats.emplace(result::Element::ContainerStats{ .capacity = std::get<ParsedData::VarInt>(d.val).value });
'''
[[codegen.processor]]
type = '''types::st::List<DB, types::st::Pair<DB,
typename TypeHandler<DB, T0>::type,
typename TypeHandler<DB, T1>::type
>>'''
func = '''
using element_type = std::pair<T0, T1>;
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr std::array<inst::Field, 2> entryFields{
inst::Field{
sizeof(T0),
"key",
names,
TypeHandler<DB, T0>::fields,
TypeHandler<DB, T0>::processors,
},
inst::Field{
sizeof(T1),
"value",
names,
TypeHandler<DB, T1>::fields,
TypeHandler<DB, T1>::processors,
},
};
static constexpr auto entryField = inst::Field {
sizeof(element_type),
sizeof(element_type) - sizeof(T0) - sizeof(T1),
"[]",
names,
entryFields,
std::array<inst::ProcessorInst, 0>{},
};
auto list = std::get<ParsedData::List>(d.val);
el.container_stats->length = list.length;
el.exclusive_size += (el.container_stats->capacity - el.container_stats->length) * sizeof(element_type);
for (size_t i = 0; i < list.length; i++)
ins.emplace(entryField);
'''

View File

@ -45,3 +45,36 @@ struct TypeHandler<DB, %1%<T0, T1>> {
} }
}; };
""" """
traversal_func = """
return OIInternal::getSizeType<DB>(
container.second,
returnArg.delegate([&container](auto ret) {
return OIInternal::getSizeType<DB>(container.first, ret);
})
);
"""
[[codegen.processor]]
type = "types::st::Pair<DB, typename TypeHandler<DB, T0>::type, typename TypeHandler<DB, T1>::type>"
func = """
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr auto firstField = inst::Field{
sizeof(T0),
"first",
names,
TypeHandler<DB, T0>::fields,
TypeHandler<DB, T0>::processors,
};
static constexpr auto secondField = inst::Field{
sizeof(T1),
"second",
names,
TypeHandler<DB, T1>::fields,
TypeHandler<DB, T1>::processors,
};
el.exclusive_size = sizeof(std::pair<T0, T1>) - sizeof(T0) - sizeof(T1);
ins.emplace(secondField);
ins.emplace(firstField);
"""

View File

@ -1,10 +1,10 @@
[info] [info]
type_name = "std::vector" type_name = "std::vector"
stub_template_params = [1] stub_template_params = [1]
ctype = "SEQ_TYPE"
header = "vector" header = "vector"
# Old: # Old:
ctype = "SEQ_TYPE"
typeName = "std::vector<" typeName = "std::vector<"
ns = ["namespace std"] ns = ["namespace std"]
numTemplateParams = 1 numTemplateParams = 1
@ -63,3 +63,51 @@ struct TypeHandler<DB, %1%<T0, T1>> {
} }
}; };
""" """
traversal_func = """
auto tail = returnArg.write((uintptr_t)&container)
.write(container.capacity())
.write(container.size());
// The double ampersand is needed otherwise this loop doesn't work with
// vector<bool>
for (auto&& it : container) {
tail = tail.delegate([&it](auto ret) {
return OIInternal::getSizeType<DB>(it, ret);
});
}
return tail.finish();
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
el.pointer = std::get<ParsedData::VarInt>(d.val).value;
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
el.container_stats.emplace(result::Element::ContainerStats{ .capacity = std::get<ParsedData::VarInt>(d.val).value });
"""
[[codegen.processor]]
type = "types::st::List<DB, typename TypeHandler<DB, T0>::type>"
func = """
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr auto childField = inst::Field{
sizeof(T0),
"[]",
names,
TypeHandler<DB, T0>::fields,
TypeHandler<DB, T0>::processors,
};
auto list = std::get<ParsedData::List>(d.val);
el.container_stats->length = list.length;
el.exclusive_size += (el.container_stats->capacity - el.container_stats->length) * sizeof(T0);
for (size_t i = 0; i < list.length; i++)
ins.emplace(childField);
"""

Some files were not shown because too many files have changed in this diff Show More