mirror of
https://github.com/JakeHillion/object-introspection.git
synced 2024-12-22 05:23:05 +00:00
oil v2
This commit is contained in:
parent
57210cf06c
commit
5071519e45
@ -334,9 +334,18 @@ target_link_libraries(treebuilder
|
||||
|
||||
## OI Outputs
|
||||
### 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_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)
|
||||
add_executable(oilgen
|
||||
|
78
include/oi/IntrospectionResult-inl.h
Normal file
78
include/oi/IntrospectionResult-inl.h
Normal 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
|
70
include/oi/IntrospectionResult.h
Normal file
70
include/oi/IntrospectionResult.h
Normal 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
|
44
include/oi/exporters/Json.h
Normal file
44
include/oi/exporters/Json.h
Normal 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
|
80
include/oi/exporters/ParsedData.h
Normal file
80
include/oi/exporters/ParsedData.h
Normal 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
|
90
include/oi/exporters/inst.h
Normal file
90
include/oi/exporters/inst.h
Normal 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
91
include/oi/oi-jit-inl.h
Normal 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
82
include/oi/oi-jit.h
Normal 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
47
include/oi/oi.h
Normal 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
|
49
include/oi/result/Element.h
Normal file
49
include/oi/result/Element.h
Normal 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
|
@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef OI_TYPES_DY_H
|
||||
#define OI_TYPES_DY_H 1
|
||||
#ifndef INCLUDED_OI_TYPES_DY_H
|
||||
#define INCLUDED_OI_TYPES_DY_H 1
|
||||
|
||||
/*
|
||||
* Dynamic Types
|
||||
@ -45,11 +45,11 @@
|
||||
|
||||
namespace oi::types::dy {
|
||||
|
||||
class Unit;
|
||||
class VarInt;
|
||||
class Pair;
|
||||
class Sum;
|
||||
class List;
|
||||
struct Unit;
|
||||
struct VarInt;
|
||||
struct Pair;
|
||||
struct Sum;
|
||||
struct List;
|
||||
|
||||
/*
|
||||
* Dynamic
|
||||
@ -63,11 +63,10 @@ using Dynamic = std::variant<std::reference_wrapper<const Unit>,
|
||||
std::reference_wrapper<const Sum>,
|
||||
std::reference_wrapper<const List> >;
|
||||
|
||||
class Unit {};
|
||||
class VarInt {};
|
||||
struct Unit {};
|
||||
struct VarInt {};
|
||||
|
||||
class Pair {
|
||||
public:
|
||||
struct Pair {
|
||||
constexpr Pair(Dynamic first_, Dynamic second_)
|
||||
: first(first_), second(second_) {
|
||||
}
|
||||
@ -76,8 +75,7 @@ class Pair {
|
||||
Dynamic second;
|
||||
};
|
||||
|
||||
class Sum {
|
||||
public:
|
||||
struct Sum {
|
||||
template <size_t N>
|
||||
constexpr Sum(const std::array<Dynamic, N>& variants_) : variants(variants_) {
|
||||
}
|
||||
@ -85,8 +83,7 @@ class Sum {
|
||||
std::span<const Dynamic> variants;
|
||||
};
|
||||
|
||||
class List {
|
||||
public:
|
||||
struct List {
|
||||
constexpr List(Dynamic element_) : element(element_) {
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef OI_TYPES_ST_H
|
||||
#define OI_TYPES_ST_H 1
|
||||
#ifndef INCLUDED_OI_TYPES_ST_H
|
||||
#define INCLUDED_OI_TYPES_ST_H 1
|
||||
|
||||
/*
|
||||
* Static Types
|
||||
@ -123,7 +123,7 @@ class VarInt {
|
||||
}
|
||||
|
||||
Unit<DataBuffer> write(uint64_t val) {
|
||||
while (val >= 128) {
|
||||
while (val >= 0x80) {
|
||||
_buf.write_byte(0x80 | (val & 0x7f));
|
||||
val >>= 7;
|
||||
}
|
||||
|
@ -52,4 +52,10 @@ target_link_libraries(codegen
|
||||
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)
|
||||
|
316
oi/CodeGen.cpp
316
oi/CodeGen.cpp
@ -19,6 +19,7 @@
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
|
||||
@ -54,9 +55,11 @@ using type_graph::Enum;
|
||||
using type_graph::Flattener;
|
||||
using type_graph::Member;
|
||||
using type_graph::NameGen;
|
||||
using type_graph::Primitive;
|
||||
using type_graph::Prune;
|
||||
using type_graph::RemoveMembers;
|
||||
using type_graph::RemoveTopLevelPointer;
|
||||
using type_graph::TemplateParam;
|
||||
using type_graph::TopoSorter;
|
||||
using type_graph::Type;
|
||||
using type_graph::Typedef;
|
||||
@ -67,6 +70,18 @@ template <typename T>
|
||||
using ref = std::reference_wrapper<T>;
|
||||
|
||||
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) {
|
||||
if (true /* TODO: config.useDataSegment*/) {
|
||||
code += R"(
|
||||
@ -93,6 +108,23 @@ struct OIArray {
|
||||
void defineJitLog(FeatureSet features, std::string& code) {
|
||||
if (features[Feature::JitLogging]) {
|
||||
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) \
|
||||
do { \
|
||||
if (__builtin_expect(logFile, 0)) { \
|
||||
@ -128,6 +160,10 @@ void addIncludes(const TypeGraph& typeGraph,
|
||||
|
||||
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]) {
|
||||
includes.emplace("chrono");
|
||||
}
|
||||
@ -569,38 +605,6 @@ void CodeGen::addGetSizeFuncDefs(const TypeGraph& typeGraph,
|
||||
|
||||
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.
|
||||
size_t getLastNonPaddingMemberIndex(const std::vector<Member>& members) {
|
||||
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) {
|
||||
std::string helpers;
|
||||
|
||||
@ -756,30 +824,159 @@ void CodeGen::genClassTypeHandler(const Class& c, std::string& code) {
|
||||
code += " using type = ";
|
||||
genClassStaticType(c, code);
|
||||
code += ";\n";
|
||||
if (config_.features[Feature::TreeBuilderV2])
|
||||
genClassTreeBuilderInstructions(c, code);
|
||||
genClassTraversalFunction(c, code);
|
||||
code += "};\n";
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void getContainerTypeHandler(std::unordered_set<const ContainerInfo*>& used,
|
||||
const Container& c,
|
||||
void genContainerTypeHandler(FeatureSet features,
|
||||
std::unordered_set<const ContainerInfo*>& used,
|
||||
const ContainerInfo& c,
|
||||
std::span<const TemplateParam> templateParams,
|
||||
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;
|
||||
}
|
||||
|
||||
const auto& handler = c.containerInfo_.codegen.handler;
|
||||
// TODO: Move this check into the ContainerInfo parsing once always enabled.
|
||||
if (handler.empty()) {
|
||||
LOG(ERROR) << "`codegen.handler` must be specified for all containers "
|
||||
"under \"-ftyped-data-segment\", not specified for \"" +
|
||||
c.containerInfo_.typeName + "\"";
|
||||
throw std::runtime_error("missing `codegen.handler`");
|
||||
const auto& func = c.codegen.traversalFunc;
|
||||
const auto& processors = c.codegen.processors;
|
||||
|
||||
if (func.empty()) {
|
||||
LOG(ERROR)
|
||||
<< "`codegen.traversal_func` must be specified for all containers "
|
||||
"under \"-ftree-builder-v2\", not specified for \"" +
|
||||
c.typeName + "\"";
|
||||
throw std::runtime_error("missing `codegen.traversal_func`");
|
||||
}
|
||||
auto fmt = boost::format(c.containerInfo_.codegen.handler) %
|
||||
c.containerInfo_.typeName;
|
||||
code += fmt.str();
|
||||
|
||||
std::string containerWithTypes = c.typeName;
|
||||
if (!templateParams.empty())
|
||||
containerWithTypes += '<';
|
||||
size_t types = 0, values = 0;
|
||||
for (const auto& p : templateParams) {
|
||||
if (types > 0 || values > 0)
|
||||
containerWithTypes += ", ";
|
||||
if (p.value) {
|
||||
containerWithTypes += "N" + std::to_string(values++);
|
||||
} else {
|
||||
containerWithTypes += "T" + std::to_string(types++);
|
||||
}
|
||||
}
|
||||
if (!templateParams.empty())
|
||||
containerWithTypes += '>';
|
||||
|
||||
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
|
||||
@ -789,7 +986,8 @@ void CodeGen::addTypeHandlers(const TypeGraph& typeGraph, std::string& code) {
|
||||
if (const auto* c = dynamic_cast<const Class*>(&t)) {
|
||||
genClassTypeHandler(*c, code);
|
||||
} 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 */
|
||||
) {
|
||||
code = headers::oi_OITraceCode_cpp;
|
||||
if (!config_.features[Feature::Library]) {
|
||||
FuncGen::DeclareExterns(code);
|
||||
}
|
||||
if (!config_.features[Feature::TypedDataSegment]) {
|
||||
defineMacros(code);
|
||||
}
|
||||
@ -894,12 +1095,19 @@ void CodeGen::generate(
|
||||
defineJitLog(config_.features, code);
|
||||
|
||||
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::detail;\n";
|
||||
|
||||
if (config_.features[Feature::TreeBuilderV2]) {
|
||||
code += "using oi::exporters::ParsedData;\n";
|
||||
code += "using namespace oi::exporters;\n";
|
||||
}
|
||||
code += "namespace OIInternal {\nnamespace {\n";
|
||||
FuncGen::DefineBasicTypeHandlers(code);
|
||||
FuncGen::DefineBasicTypeHandlers(code, config_.features);
|
||||
code += "} // namespace\n} // namespace OIInternal\n";
|
||||
}
|
||||
|
||||
@ -930,7 +1138,7 @@ void CodeGen::generate(
|
||||
genStaticAsserts(typeGraph, code);
|
||||
|
||||
if (config_.features[Feature::TypedDataSegment]) {
|
||||
addStandardTypeHandlers(code);
|
||||
addStandardTypeHandlers(typeGraph, config_.features, code);
|
||||
addTypeHandlers(typeGraph, code);
|
||||
} else {
|
||||
addStandardGetSizeFuncDecls(code);
|
||||
@ -946,13 +1154,19 @@ void CodeGen::generate(
|
||||
code += "} // namespace\n} // namespace OIInternal\n";
|
||||
|
||||
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);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,8 @@ class CodeGen {
|
||||
void genClassTypeHandler(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 genClassTreeBuilderInstructions(const type_graph::Class& c,
|
||||
std::string& code);
|
||||
};
|
||||
|
||||
} // namespace oi::detail
|
||||
|
@ -265,6 +265,38 @@ ContainerInfo::ContainerInfo(const fs::path& path) {
|
||||
codegenToml["handler"].value<std::string>()) {
|
||||
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_,
|
||||
@ -275,5 +307,5 @@ ContainerInfo::ContainerInfo(std::string typeName_,
|
||||
ctype(ctype_),
|
||||
header(std::move(header_)),
|
||||
codegen(Codegen{"// DummyDecl %1%\n", "// DummyFunc %1%\n",
|
||||
"// DummyHandler %1%\n"}) {
|
||||
"// DummyHandler %1%\n", "// DummyFunc\n"}) {
|
||||
}
|
||||
|
@ -27,10 +27,17 @@ ContainerTypeEnum containerTypeEnumFromStr(std::string& str);
|
||||
const char* containerTypeEnumToStr(ContainerTypeEnum ty);
|
||||
|
||||
struct ContainerInfo {
|
||||
struct Processor {
|
||||
std::string type;
|
||||
std::string func;
|
||||
};
|
||||
|
||||
struct Codegen {
|
||||
std::string decl;
|
||||
std::string func;
|
||||
std::string handler = "";
|
||||
std::string traversalFunc = "";
|
||||
std::vector<Processor> processors{};
|
||||
};
|
||||
|
||||
explicit ContainerInfo(const std::filesystem::path& path); // Throws
|
||||
|
@ -45,6 +45,8 @@ std::optional<std::string_view> featureHelp(Feature f) {
|
||||
case Feature::TreeBuilderTypeChecking:
|
||||
return "Use Typed Data Segment to perform runtime Type Checking in "
|
||||
"TreeBuilder.";
|
||||
case Feature::Library:
|
||||
return std::nullopt; // Hide in OID help
|
||||
case Feature::TreeBuilderV2:
|
||||
return "Use Tree Builder v2 for reading the data segment";
|
||||
case Feature::GenJitDebug:
|
||||
@ -72,6 +74,20 @@ std::span<const Feature> requirements(Feature f) {
|
||||
case Feature::TreeBuilderV2:
|
||||
static constexpr std::array tb2 = {Feature::TreeBuilderTypeChecking};
|
||||
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:
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
X(PruneTypeGraph, "prune-type-graph") \
|
||||
X(TypedDataSegment, "typed-data-segment") \
|
||||
X(TreeBuilderTypeChecking, "tree-builder-type-checking") \
|
||||
X(Library, "library") \
|
||||
X(TreeBuilderV2, "tree-builder-v2") \
|
||||
X(GenJitDebug, "gen-jit-debug") \
|
||||
X(JitLogging, "jit-logging") \
|
||||
|
242
oi/FuncGen.cpp
242
oi/FuncGen.cpp
@ -147,6 +147,16 @@ void FuncGen::DeclareTopLevelGetSize(std::string& testCode,
|
||||
boost::format fmt = boost::format("void getSizeType(const %1% &t);\n") % type;
|
||||
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) {
|
||||
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());
|
||||
}
|
||||
|
||||
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,
|
||||
const std::string& rawType,
|
||||
FeatureSet features) {
|
||||
@ -373,15 +412,58 @@ void FuncGen::DefineOutputType(std::string& code, const std::string& rawType) {
|
||||
#pragma GCC diagnostic ignored "-Wunknown-attributes"
|
||||
/* RawType: %1% */
|
||||
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
|
||||
)";
|
||||
)";
|
||||
|
||||
boost::format fmt =
|
||||
boost::format(func) % rawType % std::hash<std::string>{}(rawType);
|
||||
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,
|
||||
const std::string& rawType) {
|
||||
std::string func = R"(
|
||||
@ -564,6 +646,34 @@ void FuncGen::DefineDataSegmentDataBuffer(std::string& testCode) {
|
||||
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
|
||||
*
|
||||
@ -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
|
||||
* is of type Unit and always stores nothing.
|
||||
*/
|
||||
void FuncGen::DefineBasicTypeHandlers(std::string& testCode) {
|
||||
constexpr std::string_view tHandler = R"(
|
||||
void FuncGen::DefineBasicTypeHandlers(std::string& code, FeatureSet features) {
|
||||
code += R"(
|
||||
template <typename DB, typename T>
|
||||
struct TypeHandler {
|
||||
private:
|
||||
@ -582,10 +692,8 @@ void FuncGen::DefineBasicTypeHandlers(std::string& testCode) {
|
||||
if constexpr(std::is_pointer_v<T>) {
|
||||
return std::type_identity<types::st::Pair<DB,
|
||||
types::st::VarInt<DB>,
|
||||
types::st::Sum<DB,
|
||||
types::st::Unit<DB>,
|
||||
typename TypeHandler<DB, std::remove_pointer_t<T>>::type
|
||||
>>>();
|
||||
types::st::Sum<DB, types::st::Unit<DB>, typename TypeHandler<DB, std::remove_pointer_t<T>>::type>
|
||||
>>();
|
||||
} else {
|
||||
return std::type_identity<types::st::Unit<DB>>();
|
||||
}
|
||||
@ -593,7 +701,56 @@ void FuncGen::DefineBasicTypeHandlers(std::string& testCode) {
|
||||
|
||||
public:
|
||||
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(
|
||||
const T& t,
|
||||
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>
|
||||
class TypeHandler<DB, void> {
|
||||
public:
|
||||
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);
|
||||
testCode.append(voidHandler);
|
||||
ContainerInfo FuncGen::GetOiArrayContainerInfo() {
|
||||
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
|
||||
|
15
oi/FuncGen.h
15
oi/FuncGen.h
@ -17,6 +17,7 @@
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include "oi/ContainerInfo.h"
|
||||
@ -28,6 +29,8 @@ namespace oi::detail {
|
||||
|
||||
class FuncGen {
|
||||
public:
|
||||
static void DeclareExterns(std::string& code);
|
||||
|
||||
static void DeclareStoreData(std::string& testCode);
|
||||
static void DefineStoreData(std::string& testCode);
|
||||
|
||||
@ -56,6 +59,8 @@ class FuncGen {
|
||||
static void DefineTopLevelGetObjectSize(std::string& testCode,
|
||||
const std::string& type,
|
||||
const std::string& linkageName);
|
||||
static void DefineTopLevelIntrospect(std::string& code,
|
||||
const std::string& type);
|
||||
|
||||
static void DefineTopLevelGetSizeRef(std::string& testCode,
|
||||
const std::string& rawType,
|
||||
@ -65,6 +70,11 @@ class FuncGen {
|
||||
FeatureSet features);
|
||||
static void DefineOutputType(std::string& testCode,
|
||||
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,
|
||||
const std::string& type);
|
||||
@ -77,7 +87,10 @@ class FuncGen {
|
||||
const std::string& ctype);
|
||||
|
||||
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
|
||||
|
@ -19,7 +19,10 @@ namespace oi::detail::headers {
|
||||
|
||||
// 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_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_st_h;
|
||||
|
||||
} // namespace oi::detail::headers
|
||||
|
80
oi/IntrospectionResult.cpp
Normal file
80
oi/IntrospectionResult.cpp
Normal 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
|
@ -2983,6 +2983,7 @@ void OICodeGen::declareThriftStruct(std::string& code, std::string_view name) {
|
||||
}
|
||||
|
||||
bool OICodeGen::generateJitCode(std::string& code) {
|
||||
FuncGen::DeclareExterns(code);
|
||||
// Include relevant headers
|
||||
code.append("// relevant header includes -----\n");
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
#include <llvm/Support/raw_os_ostream.h>
|
||||
|
||||
#include <array>
|
||||
#include <boost/range/combine.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);
|
||||
}
|
||||
|
||||
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(
|
||||
"/synthetic/headers/oi/types/st.h",
|
||||
MemoryBuffer::getMemBuffer(headers::oi_types_st_h).release());
|
||||
headerSearchOptions.AddPath(
|
||||
"/synthetic/headers", clang::frontend::IncludeDirGroup::IndexHeaderMap,
|
||||
false, false);
|
||||
std::string{"/synthetic/headers/"} + v.second,
|
||||
MemoryBuffer::getMemBuffer(v.first).release());
|
||||
}
|
||||
if (config.features[Feature::TreeBuilderTypeChecking]) {
|
||||
compInv->getPreprocessorOpts().addRemappedFile(
|
||||
"/synthetic/headers/oi/types/dy.h",
|
||||
MemoryBuffer::getMemBuffer(headers::oi_types_dy_h).release());
|
||||
headerSearchOptions.AddPath(
|
||||
"/synthetic/headers", clang::frontend::IncludeDirGroup::IndexHeaderMap,
|
||||
false, false);
|
||||
for (const auto& [k, _] : syntheticHeaders) {
|
||||
if (config.features[k]) {
|
||||
headerSearchOptions.AddPath(
|
||||
"/synthetic/headers",
|
||||
clang::frontend::IncludeDirGroup::IndexHeaderMap, false, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
compInv->getFrontendOpts().OutputFile = objectPath;
|
||||
|
@ -14,49 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "oi/OILibraryImpl.h"
|
||||
#include "oi/oi-jit.h"
|
||||
|
||||
bool debug = false;
|
||||
namespace oi {
|
||||
|
||||
namespace ObjectIntrospection {
|
||||
|
||||
bool operator==(const options& lhs, const options& rhs) {
|
||||
return lhs.configFilePath == rhs.configFilePath &&
|
||||
lhs.debugFilePath == rhs.debugFilePath &&
|
||||
lhs.debugLevel == rhs.debugLevel &&
|
||||
lhs.chaseRawPointers == rhs.chaseRawPointers;
|
||||
OILibrary::OILibrary(void* atomicHole,
|
||||
std::unordered_set<Feature> fs,
|
||||
GeneratorOptions opts)
|
||||
: pimpl_{std::make_unique<detail::OILibraryImpl>(
|
||||
atomicHole, std::move(fs), std::move(opts))} {
|
||||
}
|
||||
|
||||
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() {
|
||||
delete pimpl_;
|
||||
}
|
||||
|
||||
int OILibrary::init() {
|
||||
if (!pimpl_->processConfigFile()) {
|
||||
return Response::OIL_BAD_CONFIG_FILE;
|
||||
}
|
||||
|
||||
if (!pimpl_->mapSegment()) {
|
||||
return Response::OIL_SEGMENT_INIT_FAIL;
|
||||
}
|
||||
|
||||
pimpl_->initCompiler();
|
||||
return pimpl_->compileCode();
|
||||
std::pair<void*, const exporters::inst::Inst&> OILibrary::init() {
|
||||
return pimpl_->init();
|
||||
}
|
||||
|
||||
int OILibrary::getObjectSize(void* ObjectAddr, size_t& size) {
|
||||
if (fp == nullptr) {
|
||||
return Response::OIL_UNINITIALISED;
|
||||
}
|
||||
|
||||
size = (*fp)(ObjectAddr);
|
||||
return Response::OIL_SUCCESS;
|
||||
}
|
||||
} // namespace ObjectIntrospection
|
||||
} // namespace oi
|
||||
|
@ -13,208 +13,241 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "oi/OILibraryImpl.h"
|
||||
#include "OILibraryImpl.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <glog/logging.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boost/core/demangle.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "oi/DrgnUtils.h"
|
||||
#include "oi/Headers.h"
|
||||
#include "oi/OIParser.h"
|
||||
#include "oi/OIUtils.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libelf.h>
|
||||
namespace oi::detail {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
PLOG_IF(ERROR, munmap(data_.data(), data_.size()) != 0)
|
||||
<< "segment unmap failed";
|
||||
}
|
||||
|
||||
OILibraryImpl::~OILibraryImpl() {
|
||||
unmapSegment();
|
||||
OILibraryImpl::MemoryFile::MemoryFile(const char* name) {
|
||||
fd_ = memfd_create(name, 0);
|
||||
if (fd_ == -1)
|
||||
throw std::runtime_error(std::string("memfd creation failed: ") +
|
||||
std::strerror(errno));
|
||||
}
|
||||
|
||||
bool OILibraryImpl::mapSegment() {
|
||||
void* textSeg =
|
||||
mmap(NULL, segConfig.textSegSize, PROT_EXEC | PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (textSeg == MAP_FAILED) {
|
||||
PLOG(ERROR) << "error mapping text segment";
|
||||
return false;
|
||||
}
|
||||
segConfig.textSegBase = textSeg;
|
||||
OILibraryImpl::MemoryFile::~MemoryFile() {
|
||||
if (fd_ == -1)
|
||||
return;
|
||||
|
||||
return true;
|
||||
PLOG_IF(ERROR, close(fd_) == -1) << "memfd close failed";
|
||||
}
|
||||
|
||||
bool OILibraryImpl::unmapSegment() {
|
||||
if (segConfig.textSegBase != nullptr &&
|
||||
munmap(segConfig.textSegBase, segConfig.textSegSize) != 0) {
|
||||
PLOG(ERROR) << "error unmapping text segment";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
std::filesystem::path OILibraryImpl::MemoryFile::path() {
|
||||
return {(boost::format("/dev/fd/%1%") % fd_).str()};
|
||||
}
|
||||
|
||||
void OILibraryImpl::initCompiler() {
|
||||
symbols = std::make_shared<SymbolService>(getpid());
|
||||
|
||||
generatorConfig.useDataSegment = false;
|
||||
OILibraryImpl::OILibraryImpl(void* atomicHole,
|
||||
std::unordered_set<oi::Feature> fs,
|
||||
GeneratorOptions opts)
|
||||
: atomicHole_(atomicHole),
|
||||
requestedFeatures_(convertFeatures(std::move(fs))),
|
||||
opts_(std::move(opts)) {
|
||||
}
|
||||
|
||||
bool OILibraryImpl::processConfigFile() {
|
||||
auto features = utils::processConfigFile(
|
||||
_self->opts.configFilePath,
|
||||
{
|
||||
{Feature::ChaseRawPointers, _self->opts.chaseRawPointers},
|
||||
{Feature::PackStructs, true},
|
||||
{Feature::PruneTypeGraph, true},
|
||||
{Feature::GenJitDebug, _self->opts.generateJitDebugInfo},
|
||||
},
|
||||
compilerConfig, generatorConfig);
|
||||
if (!features) {
|
||||
return false;
|
||||
}
|
||||
generatorConfig.features = *features;
|
||||
compilerConfig.features = *features;
|
||||
return true;
|
||||
std::pair<void*, const exporters::inst::Inst&> OILibraryImpl::init() {
|
||||
processConfigFile();
|
||||
|
||||
constexpr size_t TextSegSize = 1u << 22;
|
||||
textSeg = {TextSegSize};
|
||||
|
||||
return compileCode();
|
||||
}
|
||||
|
||||
template <class T, class F>
|
||||
class Cleanup {
|
||||
T resource;
|
||||
F cleanupFunc;
|
||||
void OILibraryImpl::processConfigFile() {
|
||||
auto features =
|
||||
utils::processConfigFile(opts_.configFilePath, requestedFeatures_,
|
||||
compilerConfig_, generatorConfig_);
|
||||
if (!features)
|
||||
throw std::runtime_error("failed to process configuration");
|
||||
|
||||
public:
|
||||
Cleanup(T _resource, F _cleanupFunc)
|
||||
: resource{_resource}, cleanupFunc{_cleanupFunc} {};
|
||||
~Cleanup() {
|
||||
cleanupFunc(resource);
|
||||
}
|
||||
};
|
||||
|
||||
void close_file(std::FILE* fp) {
|
||||
std::fclose(fp);
|
||||
generatorConfig_.features = *features;
|
||||
compilerConfig_.features = *features;
|
||||
}
|
||||
|
||||
int OILibraryImpl::compileCode() {
|
||||
OICompiler compiler{symbols, compilerConfig};
|
||||
std::pair<void*, const exporters::inst::Inst&> OILibraryImpl::compileCode() {
|
||||
auto symbols = std::make_shared<SymbolService>(getpid());
|
||||
|
||||
int objectMemfd = memfd_create("oil_object_code", 0);
|
||||
if (!objectMemfd) {
|
||||
PLOG(ERROR) << "failed to create memfd for object code";
|
||||
return Response::OIL_COMPILATION_FAILURE;
|
||||
}
|
||||
auto* prog = symbols->getDrgnProgram();
|
||||
CHECK(prog != nullptr) << "does this check need to exist?";
|
||||
|
||||
using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>;
|
||||
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());
|
||||
auto rootType = getTypeFromAtomicHole(symbols->getDrgnProgram(), atomicHole_);
|
||||
|
||||
struct drgn_program* prog = symbols->getDrgnProgram();
|
||||
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);
|
||||
CodeGen codegen{generatorConfig_, *symbols};
|
||||
|
||||
// TODO: change this to the new drgn interface from symbol -> type
|
||||
auto rootType = symbols->getRootType(irequest{"entry", name, "arg0"});
|
||||
if (!rootType.has_value()) {
|
||||
LOG(ERROR) << "Failed to get type of probe argument";
|
||||
return Response::OIL_COMPILATION_FAILURE;
|
||||
}
|
||||
std::string code;
|
||||
if (!codegen.codegenFromDrgn(rootType.type, code))
|
||||
throw std::runtime_error("oil jit codegen failed!");
|
||||
|
||||
std::string code(headers::oi_OITraceCode_cpp);
|
||||
|
||||
auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols);
|
||||
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";
|
||||
std::string sourcePath = opts_.sourceFileDumpPath;
|
||||
if (sourcePath.empty()) {
|
||||
sourcePath = "oil_jit.cpp"; // fake path for JIT debug info
|
||||
} else {
|
||||
std::ofstream outputFile(sourcePath);
|
||||
outputFile << code;
|
||||
}
|
||||
|
||||
if (!compiler.compile(code, sourcePath, objectPath)) {
|
||||
return Response::OIL_COMPILATION_FAILURE;
|
||||
}
|
||||
auto object = MemoryFile("oil_object_code");
|
||||
OICompiler compiler{symbols, compilerConfig_};
|
||||
if (!compiler.compile(code, sourcePath, object.path()))
|
||||
throw std::runtime_error("oil jit compilation failed!");
|
||||
|
||||
auto relocRes = compiler.applyRelocs(
|
||||
reinterpret_cast<uint64_t>(segConfig.textSegBase), {objectPath}, {});
|
||||
if (!relocRes.has_value()) {
|
||||
return Response::OIL_RELOCATION_FAILURE;
|
||||
}
|
||||
reinterpret_cast<uint64_t>(textSeg.data().data()), {object.path()}, {});
|
||||
if (!relocRes)
|
||||
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
|
||||
_self->fp = nullptr;
|
||||
std::string nameHash =
|
||||
(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) {
|
||||
if (symName.starts_with("_Z7getSize")) {
|
||||
_self->fp = (size_t(*)(const void*))symAddr;
|
||||
break;
|
||||
if (fp == nullptr && symName.starts_with(functionSymbolPrefix)) {
|
||||
fp = reinterpret_cast<void*>(symAddr);
|
||||
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
|
||||
for (const auto& [BaseAddr, RelocAddr, Size] : segments)
|
||||
memcpy((void*)RelocAddr, (void*)BaseAddr, Size);
|
||||
CHECK(fp != nullptr && ty != nullptr)
|
||||
<< "failed to find always present symbols!";
|
||||
|
||||
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
|
||||
|
@ -14,38 +14,76 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
#pragma once
|
||||
#include <oi/oi.h>
|
||||
|
||||
#include "ObjectIntrospection.h"
|
||||
#include "oi/OICodeGen.h"
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "oi/CodeGen.h"
|
||||
#include "oi/Features.h"
|
||||
#include "oi/OICompiler.h"
|
||||
#include "oi/SymbolService.h"
|
||||
|
||||
namespace ObjectIntrospection {
|
||||
namespace oi::detail {
|
||||
|
||||
class OILibraryImpl {
|
||||
public:
|
||||
OILibraryImpl(OILibrary*, void*);
|
||||
~OILibraryImpl();
|
||||
private:
|
||||
class LocalTextSegment {
|
||||
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();
|
||||
bool unmapSegment();
|
||||
void initCompiler();
|
||||
int compileCode();
|
||||
bool processConfigFile();
|
||||
void enableLayoutAnalysis();
|
||||
std::span<uint8_t> data() {
|
||||
return data_;
|
||||
}
|
||||
void release() {
|
||||
data_ = {};
|
||||
}
|
||||
|
||||
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:
|
||||
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{};
|
||||
oi::detail::OICodeGen::Config generatorConfig{};
|
||||
std::shared_ptr<oi::detail::SymbolService> symbols{};
|
||||
LocalTextSegment textSeg;
|
||||
|
||||
struct c {
|
||||
void* textSegBase = nullptr;
|
||||
size_t textSegSize = 1u << 22;
|
||||
} segConfig;
|
||||
void processConfigFile();
|
||||
std::pair<void*, const exporters::inst::Inst&> compileCode();
|
||||
};
|
||||
} // namespace ObjectIntrospection
|
||||
|
||||
} // namespace oi::detail
|
||||
|
@ -34,12 +34,6 @@
|
||||
|
||||
#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;
|
||||
|
||||
#include <array>
|
||||
@ -89,21 +83,6 @@ class {
|
||||
}
|
||||
} 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
|
||||
|
||||
// alignas(0) is ignored according to docs so can be default
|
||||
|
@ -298,7 +298,7 @@ bool SymbolService::loadModules() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::optional<drgn_qualified_type> findTypeOfSymbol(
|
||||
std::optional<drgn_qualified_type> SymbolService::findTypeOfSymbol(
|
||||
drgn_program* prog, const std::string& symbolName) {
|
||||
drgn_symbol* sym;
|
||||
if (auto* err =
|
||||
@ -313,6 +313,16 @@ static std::optional<drgn_qualified_type> findTypeOfSymbol(
|
||||
uint64_t addr = drgn_symbol_address(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_init(&obj, prog);
|
||||
|
||||
@ -320,7 +330,7 @@ static std::optional<drgn_qualified_type> findTypeOfSymbol(
|
||||
if (auto* err =
|
||||
drgn_program_find_function_by_address(prog, addr, &name, &obj);
|
||||
err != nullptr) {
|
||||
LOG(ERROR) << "Failed to lookup function '" << symbolName
|
||||
LOG(ERROR) << "Failed to lookup function '" << reinterpret_cast<void*>(addr)
|
||||
<< "': " << err->code << " " << err->message;
|
||||
drgn_error_destroy(err);
|
||||
return std::nullopt;
|
||||
@ -598,7 +608,7 @@ static std::optional<std::shared_ptr<FuncDesc>> createFuncDesc(
|
||||
struct drgn_program* prog, const irequest& request) {
|
||||
VLOG(1) << "Creating function description for: " << request.func;
|
||||
|
||||
auto ft = findTypeOfSymbol(prog, request.func);
|
||||
auto ft = SymbolService::findTypeOfSymbol(prog, request.func);
|
||||
if (!ft) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -56,6 +56,11 @@ class SymbolService {
|
||||
static std::string getTypeName(struct drgn_type*);
|
||||
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<GlobalDesc>> globalDescs;
|
||||
|
||||
|
125
oi/exporters/Json.cpp
Normal file
125
oi/exporters/Json.cpp
Normal 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
|
76
oi/exporters/ParsedData.cpp
Normal file
76
oi/exporters/ParsedData.cpp
Normal 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
|
@ -71,6 +71,8 @@ void NameGen::visit(Class& c) {
|
||||
std::string name = c.name();
|
||||
removeTemplateParams(name);
|
||||
deduplicate(name);
|
||||
if (c.name().empty())
|
||||
c.setInputName(name);
|
||||
c.setName(name);
|
||||
|
||||
// Deduplicate member names. Duplicates may be present after flattening.
|
||||
@ -151,6 +153,8 @@ void NameGen::visit(Container& c) {
|
||||
void NameGen::visit(Enum& e) {
|
||||
std::string name = e.name();
|
||||
deduplicate(name);
|
||||
if (e.name().empty())
|
||||
e.setInputName(name);
|
||||
e.setName(name);
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,6 @@ namespace {
|
||||
void print(const TypeGraph& typeGraph) {
|
||||
if (!VLOG_IS_ON(1))
|
||||
return;
|
||||
|
||||
std::stringstream out;
|
||||
Printer printer{out, typeGraph.resetTracker(), typeGraph.size()};
|
||||
for (const auto& type : typeGraph.rootTypes()) {
|
||||
|
@ -231,6 +231,10 @@ class Class : public Type {
|
||||
name_ = std::move(name);
|
||||
}
|
||||
|
||||
void setInputName(std::string name) {
|
||||
inputName_ = std::move(name);
|
||||
}
|
||||
|
||||
virtual size_t size() const override {
|
||||
return size_;
|
||||
}
|
||||
@ -376,6 +380,10 @@ class Enum : public Type {
|
||||
return inputName_;
|
||||
}
|
||||
|
||||
void setInputName(std::string name) {
|
||||
inputName_ = std::move(name);
|
||||
}
|
||||
|
||||
void setName(std::string name) {
|
||||
name_ = std::move(name);
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ function(embed_headers output)
|
||||
file(APPEND ${output} "namespace oi::detail::headers {\n")
|
||||
|
||||
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/st.h
|
||||
../oi/OITraceCode.cpp
|
||||
|
@ -50,7 +50,7 @@ add_executable(integration_test_target
|
||||
${INTEGRATION_TEST_TARGET_SRC}
|
||||
folly_shims.cpp)
|
||||
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)
|
||||
target_include_directories(integration_test_runner PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
@ -56,6 +56,12 @@ definitions = '''
|
||||
{"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [
|
||||
{"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]
|
||||
skip = "container alignment is broken (#143)"
|
||||
param_types = ["const std::optional<Align16>&"]
|
||||
@ -78,6 +84,16 @@ definitions = '''
|
||||
{"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [
|
||||
{"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]
|
||||
skip = "container alignment is broken (#143)"
|
||||
param_types = ["const std::optional<TwoStruct>&"]
|
||||
@ -101,6 +117,13 @@ definitions = '''
|
||||
{"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]
|
||||
skip = "container alignment is broken (#143)"
|
||||
param_types = ["const std::optional<MemberAlignment>&"]
|
||||
@ -119,6 +142,11 @@ definitions = '''
|
||||
{"typeName": "int8_t", "staticSize": 1, "exclusiveSize": 1},
|
||||
{"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]
|
||||
skip = "container alignment is broken (#143)"
|
||||
param_types = ["const std::optional<UnionMember>&"]
|
||||
@ -138,6 +166,14 @@ definitions = '''
|
||||
{"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [
|
||||
{"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]
|
||||
skip = "container alignment is broken (#143)"
|
||||
param_types = ["const std::optional<MemberAlignmentOverriden>&"]
|
||||
@ -159,6 +195,13 @@ definitions = '''
|
||||
{"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]
|
||||
skip = "container alignment is broken (#143)"
|
||||
param_types = ["const std::optional<AlignedStructMemberAlignLower>&"]
|
||||
|
@ -192,6 +192,7 @@ definitions = '''
|
||||
}]'''
|
||||
|
||||
[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&"]
|
||||
setup = 'return AnonUnionContainer{ .a = 3 };'
|
||||
cli_options = ["-fchase-raw-pointers"]
|
||||
@ -204,6 +205,15 @@ definitions = '''
|
||||
{"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]
|
||||
oil_disable = "oil can't chase raw pointers safely"
|
||||
|
@ -30,7 +30,17 @@ definitions = '''
|
||||
"capacity":10,
|
||||
"elementStaticSize":4
|
||||
}]}]'''
|
||||
expect_json_v2 = '''[{
|
||||
"staticSize":40,
|
||||
"exclusiveSize":0,
|
||||
"members":[{
|
||||
"staticSize":40,
|
||||
"exclusiveSize":0,
|
||||
"length":10,
|
||||
"capacity":10
|
||||
}]}]'''
|
||||
[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.
|
||||
# They end up not being treated as containers. This should probably change
|
||||
# in the future.
|
||||
@ -43,8 +53,16 @@ definitions = '''
|
||||
"staticSize":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
|
||||
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&"]
|
||||
setup = "return {};"
|
||||
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}]
|
||||
}]}]'''
|
||||
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]
|
||||
skip = "Direct array arguments don't work"
|
||||
param_types = ["int[10]"]
|
||||
|
@ -154,6 +154,7 @@ definitions = '''
|
||||
'''
|
||||
|
||||
[cases.unique_ptr]
|
||||
oil_skip = "cycles are broken" # https://github.com/facebookexperimental/object-introspection/issues/293
|
||||
param_types = ["std::reference_wrapper<UniqueNode>&"]
|
||||
setup = '''
|
||||
auto first = std::make_unique<UniqueNode>();
|
||||
@ -231,6 +232,7 @@ definitions = '''
|
||||
'''
|
||||
|
||||
[cases.shared_ptr]
|
||||
oil_skip = "cycles are broken" # https://github.com/facebookexperimental/object-introspection/issues/293
|
||||
param_types = ["std::reference_wrapper<SharedNode>&"]
|
||||
setup = '''
|
||||
auto first = std::make_shared<SharedNode>();
|
||||
|
@ -38,7 +38,8 @@ definitions = '''
|
||||
[cases.scoped_enum_val_cast]
|
||||
param_types = ["const std::array<int, static_cast<size_t>(MyNS::ScopedEnum::Two)>&"]
|
||||
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]
|
||||
param_types = ["const MyClass<MyNS::ScopedEnum::One>&"]
|
||||
@ -59,4 +60,5 @@ definitions = '''
|
||||
[cases.unscoped_enum_val_cast]
|
||||
param_types = ["const std::array<int, MyNS::ONE>&"]
|
||||
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}]'
|
||||
|
@ -1,6 +1,7 @@
|
||||
includes = ["folly/FBString.h"]
|
||||
[cases]
|
||||
[cases.empty]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
|
||||
param_types = ["folly::fbstring&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''
|
||||
@ -26,6 +27,7 @@ includes = ["folly/FBString.h"]
|
||||
'''
|
||||
|
||||
[cases.inline]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
|
||||
param_types = ["folly::fbstring&"]
|
||||
setup = 'return {"012345"};'
|
||||
expect_json = '''
|
||||
@ -51,6 +53,7 @@ includes = ["folly/FBString.h"]
|
||||
'''
|
||||
|
||||
[cases.heap_allocated]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
|
||||
param_types = ["folly::fbstring&"]
|
||||
setup = 'return {"abcdefghijklmnopqrstuvwxzy"};'
|
||||
expect_json = '''
|
||||
@ -76,6 +79,7 @@ includes = ["folly/FBString.h"]
|
||||
'''
|
||||
|
||||
[cases.string_pooled]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
|
||||
param_types = ["folly::fbstring&"]
|
||||
setup = "return folly::fbstring(1024, 'c');"
|
||||
expect_json = '''
|
||||
|
@ -1,23 +1,28 @@
|
||||
includes = ["folly/small_vector.h", "vector"]
|
||||
[cases]
|
||||
[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>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '[{"staticSize":16, "dynamicSize":0, "exclusiveSize":16, "length":0, "capacity":2, "elementStaticSize":4}]'
|
||||
[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>&"]
|
||||
setup = "return {{1,2}};"
|
||||
expect_json = '[{"staticSize":16, "dynamicSize":0, "exclusiveSize":16, "length":2, "capacity":2, "elementStaticSize":4}]'
|
||||
[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>&"]
|
||||
setup = "return {{1,2,3,4}};"
|
||||
expect_json = '[{"staticSize":16, "dynamicSize":24, "exclusiveSize":40, "length":4, "capacity":6, "elementStaticSize":4}]'
|
||||
|
||||
[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>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '[{"staticSize":80, "dynamicSize":0, "exclusiveSize":80, "length":0, "capacity":3, "elementStaticSize":24}]'
|
||||
[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>&"]
|
||||
setup = "return {{ {1,2,3}, {4}, {5,6} }};"
|
||||
expect_json = '''[
|
||||
@ -27,6 +32,7 @@ includes = ["folly/small_vector.h", "vector"]
|
||||
{"staticSize":24, "dynamicSize":8, "exclusiveSize":32, "length":2, "capacity":2, "elementStaticSize":4}
|
||||
]}]'''
|
||||
[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>&"]
|
||||
setup = "return {{ {1,2,3}, {4}, {5,6}, {7} }};"
|
||||
expect_json = '''[
|
||||
@ -38,6 +44,7 @@ includes = ["folly/small_vector.h", "vector"]
|
||||
]}]'''
|
||||
|
||||
[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>&"]
|
||||
setup = "return {{1}};"
|
||||
expect_json = '[{"staticSize":16, "dynamicSize":4, "exclusiveSize":20, "length":1, "capacity":1, "elementStaticSize":4}]'
|
||||
|
@ -4,10 +4,39 @@ includes = ["folly/sorted_vector_types.h", "vector"]
|
||||
param_types = ["const folly::sorted_vector_map<int, int>&"]
|
||||
setup = "return {};"
|
||||
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]
|
||||
param_types = ["const folly::sorted_vector_map<int, int>&"]
|
||||
setup = "return {{ {1,2}, {3,4} }};"
|
||||
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]
|
||||
skip = "Wrong results" # https://github.com/facebookexperimental/object-introspection/issues/258
|
||||
|
@ -38,7 +38,8 @@ def add_headers(f, custom_headers, thrift_headers):
|
||||
#include <thread>
|
||||
#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):
|
||||
return (
|
||||
f"\n"
|
||||
f' extern "C" {{\n'
|
||||
f" void __attribute__((noinline)) {name}({params}) {{\n"
|
||||
# f' extern "C" {{\n'
|
||||
f' extern "C" void __attribute__((noinline)) {name}({params}) {{\n'
|
||||
f"{body}"
|
||||
f" }}\n"
|
||||
f" }}\n"
|
||||
# f" }}\n"
|
||||
)
|
||||
|
||||
cases = config["cases"]
|
||||
@ -134,22 +135,18 @@ def add_test_setup(f, config):
|
||||
)
|
||||
|
||||
oil_func_body = (
|
||||
f"\n"
|
||||
f"ObjectIntrospection::options opts{{\n"
|
||||
f" .configFilePath = configFile,\n"
|
||||
f" .debugLevel = 3,\n"
|
||||
f' .sourceFileDumpPath = "oil_jit_code.cpp",\n'
|
||||
f"}};"
|
||||
f" oi::GeneratorOptions opts{{\n"
|
||||
f" .configFilePath = configFile,\n"
|
||||
f' .sourceFileDumpPath = "oil_jit_code.cpp",\n'
|
||||
f" .debugLevel = 3,\n"
|
||||
f" }};\n\n"
|
||||
)
|
||||
|
||||
oil_func_body += ' std::cout << "{\\"results\\": [" << std::endl;\n'
|
||||
oil_func_body += ' std::cout << "," << std::endl;\n'.join(
|
||||
f" size_t size{i} = 0;\n"
|
||||
f" auto ret{i} = ObjectIntrospection::getObjectSize(a{i}, size{i}, opts);\n"
|
||||
f' std::cout << "{{\\"ret\\": " << ret{i} << ", \\"size\\": " << size{i} << "}}" << std::endl;\n'
|
||||
for i in range(len(case["param_types"]))
|
||||
)
|
||||
oil_func_body += ' std::cout << "]}" << std::endl;\n'
|
||||
oil_func_body += " auto pr = oi::exporters::Json(std::cout);\n"
|
||||
oil_func_body += " pr.setPretty(true);\n"
|
||||
for i in range(len(case["param_types"])):
|
||||
oil_func_body += f" auto ret{i} = oi::setupAndIntrospect(a{i}, opts);\n"
|
||||
oil_func_body += f" pr.print(*ret{i});\n"
|
||||
|
||||
f.write(
|
||||
define_traceable_func(
|
||||
@ -367,23 +364,14 @@ def add_oil_integration_test(f, config, case_name, case):
|
||||
f' .targetArgs = "oil {case_str}",\n'
|
||||
f" }}, std::move(configPrefix), std::move(configSuffix));\n\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:
|
||||
json.loads(case["expect_json"])
|
||||
json.loads(case[key])
|
||||
except json.decoder.JSONDecodeError as error:
|
||||
print(
|
||||
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"\n"
|
||||
f" std::stringstream expected_json_ss;\n"
|
||||
f' expected_json_ss << R"--({case["expect_json"]})--";\n'
|
||||
f" bpt::ptree expected_json;\n"
|
||||
f' expected_json_ss << R"--({case[key]})--";\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" auto sizes_it = sizes.begin();\n"
|
||||
f" for (auto it = expected_json.begin(); it != expected_json.end(); ++it, ++sizes_it) {{\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" bpt::read_json(result_json_ss, actual_json);\n"
|
||||
f" compare_json(expected_json, actual_json);\n"
|
||||
)
|
||||
|
||||
f.write(f"}}\n")
|
||||
|
@ -10,6 +10,7 @@ definitions = '''
|
||||
|
||||
[cases]
|
||||
[cases.a]
|
||||
oil_skip = 'v2 hides the member entirely when it should show it with static size' # todo: github issue
|
||||
param_types = ["const Bar&"]
|
||||
setup = """
|
||||
return Bar{
|
||||
|
@ -1,18 +1,18 @@
|
||||
definitions = '''
|
||||
class Base {
|
||||
int base_int;
|
||||
int32_t base_int;
|
||||
};
|
||||
|
||||
class Public : public Base {
|
||||
int public_int;
|
||||
int32_t public_int;
|
||||
};
|
||||
|
||||
class Protected : protected Base {
|
||||
int protected_int;
|
||||
int32_t protected_int;
|
||||
};
|
||||
|
||||
class Private : private Base {
|
||||
int private_int;
|
||||
int32_t private_int;
|
||||
};
|
||||
'''
|
||||
[cases]
|
||||
@ -21,30 +21,27 @@ definitions = '''
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
"staticSize":8,
|
||||
"dynamicSize":0,
|
||||
"members":[
|
||||
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"},
|
||||
{"name":"public_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}
|
||||
{"name":"base_int", "staticSize":4, "typeName": "int32_t"},
|
||||
{"name":"public_int", "staticSize":4, "typeName": "int32_t"}
|
||||
]}]'''
|
||||
[cases.protected]
|
||||
param_types = ["const Protected&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
"staticSize":8,
|
||||
"dynamicSize":0,
|
||||
"members":[
|
||||
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"},
|
||||
{"name":"protected_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}
|
||||
{"name":"base_int", "staticSize":4, "typeName": "int32_t"},
|
||||
{"name":"protected_int", "staticSize":4, "typeName": "int32_t"}
|
||||
]}]'''
|
||||
[cases.private]
|
||||
param_types = ["const Private&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
"staticSize":8,
|
||||
"dynamicSize":0,
|
||||
"members":[
|
||||
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"},
|
||||
{"name":"private_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}
|
||||
{"name":"base_int", "staticSize":4, "typeName": "int32_t"},
|
||||
{"name":"private_int", "staticSize":4, "typeName": "int32_t"}
|
||||
]}]'''
|
||||
[cases.public_as_base]
|
||||
param_types = ["const Base&"]
|
||||
@ -52,7 +49,6 @@ definitions = '''
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
"staticSize":4,
|
||||
"dynamicSize":0,
|
||||
"members":[
|
||||
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}
|
||||
{"name":"base_int", "staticSize":4, "typeName": "int32_t"}
|
||||
]}]'''
|
||||
|
@ -34,3 +34,14 @@ definitions = '''
|
||||
{"name":"e", "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"]}
|
||||
]}]'''
|
||||
|
@ -82,6 +82,7 @@ definitions = '''
|
||||
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}
|
||||
]}]'''
|
||||
[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&"]
|
||||
arg_types = ["B"]
|
||||
setup = '''
|
||||
@ -157,6 +158,7 @@ definitions = '''
|
||||
{"name":"int_c", "staticSize":4, "dynamicSize":0}
|
||||
]}]'''
|
||||
[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&"]
|
||||
arg_types = ["C"]
|
||||
setup = '''
|
||||
@ -174,3 +176,14 @@ definitions = '''
|
||||
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4},
|
||||
{"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}
|
||||
]
|
||||
}]'''
|
||||
|
@ -12,6 +12,7 @@ definitions = '''
|
||||
|
||||
[cases]
|
||||
[cases.a]
|
||||
oil_disable = 'multi-argument probing has no meaning for oil'
|
||||
param_types = ["int", "double"]
|
||||
args = "arg0,arg1"
|
||||
setup = "return {1,2.0};"
|
||||
|
@ -16,6 +16,7 @@ definitions = '''
|
||||
'''
|
||||
[cases]
|
||||
[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>>&"]
|
||||
setup = "return std::queue<std::pair<ns_namespaces::nsA::Foo, ns_namespaces::nsB::Foo>>({{ns_namespaces::nsA::Foo(), ns_namespaces::nsB::Foo()}});"
|
||||
expect_json = '''[{
|
||||
@ -23,6 +24,7 @@ definitions = '''
|
||||
"staticSize": 80, "dynamicSize": 12, "length": 1, "capacity": 1, "elementStaticSize": 12
|
||||
}]'''
|
||||
[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>>&"]
|
||||
setup = "return std::stack<std::pair<ns_namespaces::nsA::Foo, ns_namespaces::nsB::Foo>>({{ns_namespaces::nsA::Foo(), ns_namespaces::nsB::Foo()}});"
|
||||
expect_json = '''[{
|
||||
|
@ -93,6 +93,7 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[cases.parent_padding]
|
||||
oid_skip = 'calculating incorrectly'
|
||||
param_types = ["const PaddedChild&"]
|
||||
setup = "return PaddedChild{};"
|
||||
expect_json = '''[{
|
||||
@ -100,3 +101,7 @@ definitions = '''
|
||||
"dynamicSize": 0,
|
||||
"paddingSavingsSize": 19
|
||||
}]'''
|
||||
expect_json_v2 = '''[{
|
||||
"staticSize": 104,
|
||||
"exclusiveSize": 32
|
||||
}]'''
|
||||
|
@ -14,8 +14,7 @@ definitions = '''
|
||||
|
||||
[cases]
|
||||
[cases.int]
|
||||
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
|
||||
oil_disable = "oil can't chase raw pointers safely"
|
||||
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
|
||||
param_types = ["int*"]
|
||||
setup = "return new int(1);"
|
||||
cli_options = ["-fchase-raw-pointers"]
|
||||
@ -33,7 +32,7 @@ definitions = '''
|
||||
]
|
||||
}]'''
|
||||
[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*"]
|
||||
setup = "return new int(1);"
|
||||
expect_json = '''[{
|
||||
@ -44,7 +43,7 @@ definitions = '''
|
||||
"NOT": "members"
|
||||
}]'''
|
||||
[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*"]
|
||||
setup = "return nullptr;"
|
||||
expect_json = '''[{
|
||||
@ -57,8 +56,7 @@ definitions = '''
|
||||
|
||||
|
||||
[cases.void]
|
||||
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
|
||||
oil_disable = "oil can't chase raw pointers safely"
|
||||
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
|
||||
param_types = ["void*"]
|
||||
setup = "return new int(1);"
|
||||
cli_options = ["-fchase-raw-pointers"]
|
||||
@ -70,7 +68,7 @@ definitions = '''
|
||||
"NOT": "members"
|
||||
}]'''
|
||||
[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*"]
|
||||
setup = "return new int(1);"
|
||||
expect_json = '''[{
|
||||
@ -81,7 +79,7 @@ definitions = '''
|
||||
"NOT": "members"
|
||||
}]'''
|
||||
[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*"]
|
||||
setup = "return nullptr;"
|
||||
expect_json = '''[{
|
||||
@ -94,8 +92,7 @@ definitions = '''
|
||||
|
||||
|
||||
[cases.vector]
|
||||
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
|
||||
oil_disable = "oil can't chase raw pointers safely"
|
||||
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
|
||||
param_types = ["std::vector<int>*"]
|
||||
setup = "return new std::vector<int>{1,2,3};"
|
||||
cli_options = ["-fchase-raw-pointers"]
|
||||
@ -113,7 +110,7 @@ definitions = '''
|
||||
]
|
||||
}]'''
|
||||
[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>*"]
|
||||
setup = "return new std::vector<int>{1,2,3};"
|
||||
expect_json = '''[{
|
||||
@ -124,7 +121,7 @@ definitions = '''
|
||||
"NOT": "members"
|
||||
}]'''
|
||||
[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>*"]
|
||||
setup = "return nullptr;"
|
||||
expect_json = '''[{
|
||||
@ -224,7 +221,7 @@ definitions = '''
|
||||
{"staticSize":8, "dynamicSize":4, "NOT": {"pointer":0}}
|
||||
]}]'''
|
||||
[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*>&"]
|
||||
setup = "return {{new int(1), nullptr, new int(3)}};"
|
||||
expect_json = '''[{
|
||||
|
@ -14,7 +14,7 @@ definitions = '''
|
||||
|
||||
[cases]
|
||||
[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&"]
|
||||
setup = "return {{myFunction}};"
|
||||
expect_json = '''[{
|
||||
@ -46,7 +46,7 @@ definitions = '''
|
||||
}]
|
||||
}]'''
|
||||
[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&"]
|
||||
setup = "return {{nullptr}};"
|
||||
expect_json = '''[{
|
||||
@ -62,7 +62,7 @@ definitions = '''
|
||||
}]'''
|
||||
|
||||
[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)> &"]
|
||||
setup = "return myFunction;"
|
||||
expect_json = '''[{
|
||||
@ -86,7 +86,7 @@ definitions = '''
|
||||
"NOT": "members"
|
||||
}]'''
|
||||
[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)> &"]
|
||||
setup = "return nullptr;"
|
||||
expect_json = '''[{
|
||||
|
@ -32,7 +32,7 @@ definitions = '''
|
||||
"NOT": "members"
|
||||
}]'''
|
||||
[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*"]
|
||||
setup = "return static_cast<IncompleteType*>(::operator new(5));"
|
||||
expect_json = '''[{
|
||||
@ -43,7 +43,7 @@ definitions = '''
|
||||
"NOT": "members"
|
||||
}]'''
|
||||
[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*"]
|
||||
setup = "return nullptr;"
|
||||
expect_json = '''[{
|
||||
@ -55,6 +55,7 @@ definitions = '''
|
||||
}]'''
|
||||
|
||||
[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)>&"]
|
||||
setup = '''
|
||||
auto raw_ptr = static_cast<IncompleteType*>(::operator new(5));
|
||||
@ -63,6 +64,7 @@ definitions = '''
|
||||
'''
|
||||
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'
|
||||
[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)>&"]
|
||||
setup = '''
|
||||
return std::unique_ptr<IncompleteType, decltype(&incomplete_type_deleter)>(
|
||||
@ -71,6 +73,7 @@ definitions = '''
|
||||
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'
|
||||
|
||||
[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>&"]
|
||||
setup = '''
|
||||
auto raw_ptr = static_cast<IncompleteType*>(::operator new(5));
|
||||
@ -78,6 +81,7 @@ definitions = '''
|
||||
'''
|
||||
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'
|
||||
[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>"]
|
||||
setup = "return nullptr;"
|
||||
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'
|
||||
|
@ -302,10 +302,10 @@ OidProc OidIntegration::runOidOnProcess(OidOpts opts,
|
||||
};
|
||||
}
|
||||
|
||||
void OidIntegration::compare_json(const bpt::ptree& expected_json,
|
||||
const bpt::ptree& actual_json,
|
||||
const std::string& full_key,
|
||||
bool expect_eq) {
|
||||
void IntegrationBase::compare_json(const bpt::ptree& expected_json,
|
||||
const bpt::ptree& actual_json,
|
||||
const std::string& full_key,
|
||||
bool expect_eq) {
|
||||
if (expected_json.empty()) {
|
||||
if (expect_eq) {
|
||||
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 curr_key = full_key + "." + key;
|
||||
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;
|
||||
continue;
|
||||
}
|
||||
|
@ -49,16 +49,6 @@ class IntegrationBase : public ::testing::Test {
|
||||
|
||||
std::string stdout_;
|
||||
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
|
||||
@ -72,6 +62,16 @@ class OidIntegration : public IntegrationBase {
|
||||
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 {
|
||||
protected:
|
||||
std::string TmpDirStr() override;
|
||||
|
@ -44,6 +44,5 @@ definitions = '''
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
"staticSize":8,
|
||||
"dynamicSize":0,
|
||||
"NOT":"members"
|
||||
"dynamicSize":0
|
||||
}]'''
|
||||
|
@ -5,6 +5,7 @@ definitions = '''
|
||||
|
||||
[cases]
|
||||
[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>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -22,6 +23,7 @@ definitions = '''
|
||||
}]}]'''
|
||||
|
||||
[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>&"]
|
||||
setup = '''
|
||||
sorted_vector_set<int> is;
|
||||
|
@ -1,6 +1,7 @@
|
||||
includes = ["array", "cstdint", "vector"]
|
||||
[cases]
|
||||
[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>&"]
|
||||
setup = "return {{}};"
|
||||
expect_json = '''
|
||||
@ -16,6 +17,7 @@ includes = ["array", "cstdint", "vector"]
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
setup = "return {{1}};"
|
||||
expect_json = '''
|
||||
@ -31,6 +33,7 @@ includes = ["array", "cstdint", "vector"]
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
setup = "return {{0,1,2,3,4,5,6,7}};"
|
||||
expect_json = '''
|
||||
@ -45,6 +48,7 @@ includes = ["array", "cstdint", "vector"]
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
setup = "return {{std::initializer_list<std::uint64_t>({1,2,3,4,5})}};"
|
||||
expect_json = '''
|
||||
@ -65,6 +69,7 @@ includes = ["array", "cstdint", "vector"]
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
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 = '''
|
||||
|
@ -16,6 +16,7 @@ public:
|
||||
'''
|
||||
[cases]
|
||||
[cases.a]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/317
|
||||
param_types = ["const C&"]
|
||||
setup = '''
|
||||
C foo;
|
||||
|
@ -3,18 +3,22 @@
|
||||
includes = ["deque"]
|
||||
[cases]
|
||||
[cases.int_empty]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
|
||||
param_types = ["const std::deque<int>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}]'
|
||||
[cases.int_some]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
|
||||
param_types = ["const std::deque<int>&"]
|
||||
setup = "return {{1,2,3}};"
|
||||
expect_json = '[{"staticSize":80, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}]'
|
||||
[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>>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":80}]'
|
||||
[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>>&"]
|
||||
setup = "return {{{1,2,3},{},{4,5}}};"
|
||||
expect_json = '''[{
|
||||
|
@ -24,6 +24,7 @@ includes = ["deque"]
|
||||
|
||||
[cases]
|
||||
[cases.a]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
|
||||
param_types = ["const Foo&"]
|
||||
setup = '''
|
||||
Foo foo;
|
||||
|
@ -27,6 +27,7 @@ includes = ["list"]
|
||||
|
||||
[cases]
|
||||
[cases.a]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/315
|
||||
param_types = ["const Foo&"]
|
||||
setup = '''
|
||||
Foo foo;
|
||||
|
@ -36,6 +36,7 @@ includes = ["map", "functional"]
|
||||
|
||||
[cases]
|
||||
[cases.a]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/314
|
||||
param_types = ["const Foo&"]
|
||||
setup = '''
|
||||
Foo foo;
|
||||
|
@ -14,6 +14,7 @@ includes = ["map"]
|
||||
|
||||
[cases]
|
||||
[cases.a]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/313
|
||||
param_types = ["const Foo&"]
|
||||
setup = '''
|
||||
Foo foo;
|
||||
|
@ -1,6 +1,7 @@
|
||||
includes = ["optional", "cstdint", "vector"]
|
||||
[cases]
|
||||
[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>&"]
|
||||
setup = "return std::nullopt;"
|
||||
expect_json = '''
|
||||
@ -16,6 +17,7 @@ includes = ["optional", "cstdint", "vector"]
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
setup = "return 64;"
|
||||
expect_json = '''
|
||||
@ -31,6 +33,7 @@ includes = ["optional", "cstdint", "vector"]
|
||||
]
|
||||
'''
|
||||
[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>>&"]
|
||||
setup = "return std::nullopt;"
|
||||
expect_json = '''
|
||||
@ -46,6 +49,7 @@ includes = ["optional", "cstdint", "vector"]
|
||||
]
|
||||
'''
|
||||
[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>>&"]
|
||||
setup = "return {{{1,2,3,4,5}}};"
|
||||
expect_json = '''
|
||||
|
@ -1,6 +1,7 @@
|
||||
includes = ["vector", "utility", "cstdint"]
|
||||
[cases]
|
||||
[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>&"]
|
||||
setup = "return {{0, 1}};"
|
||||
expect_json = '''
|
||||
@ -14,6 +15,7 @@ includes = ["vector", "utility", "cstdint"]
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
setup = "return {{0, 1}};"
|
||||
# Should still have static size of 16 due to padding
|
||||
@ -28,6 +30,7 @@ includes = ["vector", "utility", "cstdint"]
|
||||
]
|
||||
'''
|
||||
[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>>&"]
|
||||
setup = "return {{std::initializer_list<std::uint64_t>({0,1,2}), std::initializer_list<std::uint64_t>({3,4,5,6})}};"
|
||||
expect_json = '''
|
||||
|
@ -1,6 +1,7 @@
|
||||
includes = ["queue"]
|
||||
[cases]
|
||||
[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>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -13,6 +14,7 @@ includes = ["queue"]
|
||||
}
|
||||
]}]'''
|
||||
[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>&"]
|
||||
setup = "return std::priority_queue<int>({}, {3,2,1});"
|
||||
expect_json = '''[{
|
||||
@ -25,6 +27,7 @@ includes = ["queue"]
|
||||
}
|
||||
]}]'''
|
||||
[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>>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -37,6 +40,7 @@ includes = ["queue"]
|
||||
}
|
||||
]}]'''
|
||||
[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>>&"]
|
||||
setup = "return std::priority_queue<int, std::deque<int>>({}, {3,2,1});"
|
||||
expect_json = '''[{
|
||||
|
@ -1,6 +1,7 @@
|
||||
includes = ["queue"]
|
||||
[cases]
|
||||
[cases.int_empty]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
|
||||
param_types = ["const std::queue<int>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -13,6 +14,7 @@ includes = ["queue"]
|
||||
}
|
||||
]}]'''
|
||||
[cases.int_some]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
|
||||
param_types = ["const std::queue<int>&"]
|
||||
setup = "return std::queue<int>({1,2,3});"
|
||||
expect_json = '''[{
|
||||
@ -25,6 +27,7 @@ includes = ["queue"]
|
||||
}
|
||||
]}]'''
|
||||
[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>>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -35,6 +38,7 @@ includes = ["queue"]
|
||||
}
|
||||
]}]'''
|
||||
[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>>&"]
|
||||
setup = '''
|
||||
return std::queue<std::queue<int>>({
|
||||
@ -83,6 +87,7 @@ includes = ["queue"]
|
||||
}
|
||||
]}]'''
|
||||
[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>>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -95,6 +100,7 @@ includes = ["queue"]
|
||||
}
|
||||
]}]'''
|
||||
[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>>&"]
|
||||
setup = "return std::queue<int, std::vector<int>>({1,2,3});"
|
||||
expect_json = '''[{
|
||||
|
@ -1,10 +1,12 @@
|
||||
includes = ["functional"]
|
||||
[cases]
|
||||
[cases.int]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/307
|
||||
param_types = ["const std::reference_wrapper<int>&"]
|
||||
setup = "return std::ref(*new int(1));"
|
||||
expect_json = '[{"staticSize":8, "dynamicSize":4, "length":1, "capacity":1, "elementStaticSize":4}]'
|
||||
[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>>&"]
|
||||
setup = "return {{std::ref(*new int(1)), std::ref(*new int(2)), std::ref(*new int(3))}};"
|
||||
expect_json = '''[{
|
||||
|
@ -31,6 +31,7 @@ includes = ["set", "functional"]
|
||||
|
||||
[cases]
|
||||
[cases.a]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/306
|
||||
param_types = ["const Foo&"]
|
||||
setup = '''
|
||||
Foo foo;
|
||||
|
@ -7,6 +7,7 @@ definitions = '''
|
||||
'''
|
||||
[cases]
|
||||
[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>&"]
|
||||
setup = "return {nullptr};"
|
||||
expect_json = '''
|
||||
@ -22,6 +23,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
setup = "return {std::make_unique<std::uint64_t>(64)};"
|
||||
expect_json = '''
|
||||
@ -36,6 +38,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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>>&"]
|
||||
setup = "return {nullptr};"
|
||||
expect_json = '''
|
||||
@ -50,6 +53,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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>>&"]
|
||||
setup = "return {std::make_unique<std::vector<std::uint64_t>>(std::initializer_list<std::uint64_t>({1,2,3,4,5}))};"
|
||||
expect_json = '''
|
||||
@ -72,6 +76,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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)>&"]
|
||||
setup = "return {std::unique_ptr<void, decltype(&void_int_deleter)>(nullptr, &void_int_deleter)};"
|
||||
expect_json = '''
|
||||
@ -83,6 +88,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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)>&"]
|
||||
setup = "return {std::unique_ptr<void, decltype(&void_int_deleter)>(new int, &void_int_deleter)};"
|
||||
expect_json = '''
|
||||
@ -94,6 +100,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
setup = "return {nullptr};"
|
||||
expect_json = '''
|
||||
@ -108,6 +115,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
setup = "return std::make_shared<std::uint64_t>(64);"
|
||||
expect_json = '''
|
||||
@ -122,6 +130,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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>>&"]
|
||||
setup = "return {nullptr};"
|
||||
expect_json = '''
|
||||
@ -136,6 +145,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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>>&"]
|
||||
setup = "return std::make_shared<std::vector<std::uint64_t>>(std::initializer_list<std::uint64_t>({1,2,3,4,5}));"
|
||||
expect_json = '''
|
||||
@ -156,6 +166,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
setup = "return {nullptr};"
|
||||
expect_json = '''
|
||||
@ -168,6 +179,7 @@ definitions = '''
|
||||
]
|
||||
'''
|
||||
[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>&"]
|
||||
setup = "return {std::shared_ptr<void>(new int)};"
|
||||
expect_json = '''
|
||||
@ -192,6 +204,7 @@ definitions = '''
|
||||
}
|
||||
]
|
||||
'''
|
||||
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
|
||||
[cases.weak_ptr_int64_void_empty]
|
||||
param_types = ["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]
|
||||
param_types = ["std::weak_ptr<std::uint64_t>&"]
|
||||
setup = '''
|
||||
@ -222,6 +236,7 @@ definitions = '''
|
||||
}
|
||||
]
|
||||
'''
|
||||
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
|
||||
[cases.weak_ptr_int64_expired]
|
||||
param_types = ["std::weak_ptr<std::uint64_t>&"]
|
||||
setup = '''
|
||||
@ -239,6 +254,7 @@ definitions = '''
|
||||
}
|
||||
]
|
||||
'''
|
||||
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
|
||||
[cases.weak_ptr_int64_present_chase]
|
||||
param_types = ["std::weak_ptr<std::uint64_t>&"]
|
||||
cli_options = ["-fchase-raw-pointers"]
|
||||
@ -257,6 +273,7 @@ definitions = '''
|
||||
}
|
||||
]
|
||||
'''
|
||||
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
|
||||
[cases.weak_ptr_int64_expired_chase]
|
||||
param_types = ["std::weak_ptr<std::uint64_t>&"]
|
||||
cli_options = ["-fchase-raw-pointers"]
|
||||
@ -273,3 +290,4 @@ definitions = '''
|
||||
}
|
||||
]
|
||||
'''
|
||||
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
|
||||
|
@ -1,6 +1,7 @@
|
||||
includes = ["stack", "vector"]
|
||||
[cases]
|
||||
[cases.int_empty]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
|
||||
param_types = ["const std::stack<int>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -13,6 +14,7 @@ includes = ["stack", "vector"]
|
||||
}
|
||||
]}]'''
|
||||
[cases.int_some]
|
||||
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
|
||||
param_types = ["const std::stack<int>&"]
|
||||
setup = "return std::stack<int>({1,2,3});"
|
||||
expect_json = '''[{
|
||||
@ -25,6 +27,7 @@ includes = ["stack", "vector"]
|
||||
}
|
||||
]}]'''
|
||||
[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>>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -35,6 +38,7 @@ includes = ["stack", "vector"]
|
||||
}
|
||||
]}]'''
|
||||
[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>>&"]
|
||||
setup = '''
|
||||
return std::stack<std::stack<int>>({
|
||||
@ -83,6 +87,7 @@ includes = ["stack", "vector"]
|
||||
}
|
||||
]}]'''
|
||||
[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>>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -95,6 +100,7 @@ includes = ["stack", "vector"]
|
||||
}
|
||||
]}]'''
|
||||
[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>>&"]
|
||||
setup = "return std::stack<int, std::vector<int>>({1,2,3});"
|
||||
expect_json = '''[{
|
||||
|
@ -1,6 +1,7 @@
|
||||
includes = ["string"]
|
||||
[cases]
|
||||
[cases.empty]
|
||||
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311
|
||||
param_types = ["std::string&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''
|
||||
@ -25,6 +26,7 @@ includes = ["string"]
|
||||
]
|
||||
'''
|
||||
[cases.sso]
|
||||
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311
|
||||
param_types = ["std::string&"]
|
||||
setup = 'return {"012345"};'
|
||||
expect_json = '''
|
||||
@ -49,6 +51,7 @@ includes = ["string"]
|
||||
]
|
||||
'''
|
||||
[cases.heap_allocated]
|
||||
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311
|
||||
param_types = ["std::string&"]
|
||||
setup = 'return {"abcdefghijklmnopqrstuvwxzy"};'
|
||||
expect_json = '''
|
||||
|
@ -9,6 +9,7 @@ struct Bar {
|
||||
'''
|
||||
[cases]
|
||||
[cases.uint64_uint64]
|
||||
oil_skip = "std::tuple is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/304
|
||||
param_types = ["Bar&"]
|
||||
setup = '''
|
||||
Foo f;
|
||||
|
@ -33,6 +33,7 @@ includes = ["unordered_map"]
|
||||
|
||||
[cases]
|
||||
[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&"]
|
||||
setup = '''
|
||||
Foo foo;
|
||||
|
@ -33,6 +33,7 @@ includes = ["unordered_set"]
|
||||
|
||||
[cases]
|
||||
[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&"]
|
||||
setup = '''
|
||||
Foo foo;
|
||||
|
@ -9,6 +9,7 @@ definitions = '''
|
||||
|
||||
[cases]
|
||||
[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>&"]
|
||||
setup = "return 'a';"
|
||||
expect_json = '''[{
|
||||
@ -22,6 +23,7 @@ definitions = '''
|
||||
{"typeName":"char", "staticSize":1, "exclusiveSize":1, "dynamicSize":0}
|
||||
]}]'''
|
||||
[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>&"]
|
||||
setup = "return 1234;"
|
||||
expect_json = '''[{
|
||||
@ -36,6 +38,7 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[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>&"]
|
||||
setup = "return std::vector<int>{1,2,3};"
|
||||
expect_json = '''[{
|
||||
@ -57,6 +60,7 @@ definitions = '''
|
||||
}
|
||||
]}]'''
|
||||
[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>&"]
|
||||
setup = "return 123;"
|
||||
expect_json = '''[{
|
||||
@ -74,6 +78,7 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[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
|
||||
# correctly, as otherwise the size of the std::optional would be wrong
|
||||
param_types = ["const std::optional<std::variant<char, int64_t>>&"]
|
||||
@ -100,6 +105,7 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[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
|
||||
param_types = ["const std::variant<int, Thrower>&"]
|
||||
setup = '''
|
||||
@ -127,6 +133,7 @@ definitions = '''
|
||||
# 0xff can be a valid index if there are at least 256 parameters, and that
|
||||
# the invalid index value is raised to 0xffff.
|
||||
[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>&"]
|
||||
setup = "return 'a';"
|
||||
expect_json = '''[{
|
||||
@ -140,6 +147,7 @@ definitions = '''
|
||||
{"typeName":"char", "staticSize":1, "exclusiveSize":1, "dynamicSize":0}
|
||||
]}]'''
|
||||
[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>&"]
|
||||
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'};
|
||||
|
@ -4,10 +4,16 @@ includes = ["vector"]
|
||||
param_types = ["const std::vector<int>&"]
|
||||
setup = "return {};"
|
||||
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]
|
||||
param_types = ["const std::vector<int>&"]
|
||||
setup = "return {{1,2,3}};"
|
||||
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]
|
||||
skip = true # https://github.com/facebookexperimental/object-introspection/issues/14
|
||||
param_types = ["const std::vector<bool>&"]
|
||||
@ -22,6 +28,7 @@ includes = ["vector"]
|
||||
param_types = ["const std::vector<std::vector<int>>&"]
|
||||
setup = "return {};"
|
||||
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]
|
||||
param_types = ["const std::vector<std::vector<int>>&"]
|
||||
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":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]
|
||||
param_types = ["const std::vector<int>&"]
|
||||
setup = '''
|
||||
@ -45,3 +57,8 @@ includes = ["vector"]
|
||||
return ret;
|
||||
'''
|
||||
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}
|
||||
]}]'''
|
||||
|
@ -27,6 +27,7 @@ includes = ["vector"]
|
||||
|
||||
[cases]
|
||||
[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&"]
|
||||
setup = '''
|
||||
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":[]}
|
||||
]}
|
||||
]}
|
||||
]
|
||||
}]'''
|
||||
|
@ -24,6 +24,7 @@ definitions = '''
|
||||
|
||||
[cases]
|
||||
[cases.int]
|
||||
oil_skip = "primitives are named differently in treebuilderv2" # https://github.com/facebookexperimental/object-introspection/issues/286
|
||||
param_types = ["const TemplatedClass1<int>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -48,7 +49,19 @@ definitions = '''
|
||||
"capacity":0,
|
||||
"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]
|
||||
oil_skip = "OIL returns better primitive names"
|
||||
param_types = ["const TemplatedClass2<Foo, int>&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -73,3 +86,13 @@ definitions = '''
|
||||
"capacity":3,
|
||||
"elementStaticSize":4
|
||||
}]}]'''
|
||||
expect_json_v2 = '''[{
|
||||
"typeName":"TemplatedClassVal<3>",
|
||||
"staticSize":12,
|
||||
"exclusiveSize":0,
|
||||
"members":[{
|
||||
"staticSize":12,
|
||||
"exclusiveSize":0,
|
||||
"length":3,
|
||||
"capacity":3
|
||||
}]}]'''
|
||||
|
@ -77,6 +77,7 @@ namespace cpp2 {
|
||||
|
||||
[cases]
|
||||
[cases.unpacked]
|
||||
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
|
||||
param_types = ["const cpp2::MyThriftStructUnpacked&"]
|
||||
setup = '''
|
||||
cpp2::MyThriftStructUnpacked ret;
|
||||
@ -96,6 +97,7 @@ namespace cpp2 {
|
||||
]}]'''
|
||||
|
||||
[cases.packed]
|
||||
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
|
||||
param_types = ["const cpp2::MyThriftStructPacked&"]
|
||||
setup = '''
|
||||
cpp2::MyThriftStructPacked ret;
|
||||
@ -126,6 +128,7 @@ namespace cpp2 {
|
||||
]}]'''
|
||||
|
||||
[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&"]
|
||||
setup = '''
|
||||
cpp2::MyThriftStructPackedNonAtomic ret;
|
||||
@ -146,6 +149,7 @@ namespace cpp2 {
|
||||
]}]'''
|
||||
|
||||
[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&"]
|
||||
setup = '''
|
||||
cpp2::MyThriftStructOutOfOrder ret;
|
||||
@ -164,6 +168,7 @@ namespace cpp2 {
|
||||
]}]'''
|
||||
|
||||
[cases.required]
|
||||
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
|
||||
param_types = ["const cpp2::MyThriftStructRequired&"]
|
||||
setup = '''
|
||||
cpp2::MyThriftStructRequired ret;
|
||||
@ -186,6 +191,7 @@ namespace cpp2 {
|
||||
]}]'''
|
||||
|
||||
[cases.box]
|
||||
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
|
||||
param_types = ["const cpp2::MyThriftStructBoxed&"]
|
||||
setup = '''
|
||||
cpp2::MyThriftStructBoxed ret;
|
||||
@ -207,6 +213,7 @@ namespace cpp2 {
|
||||
]}]'''
|
||||
|
||||
[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&"]
|
||||
setup = '''
|
||||
cpp2::MyThriftStructBoxed ret;
|
||||
|
@ -58,6 +58,7 @@ raw_definitions = '''
|
||||
'''
|
||||
[cases]
|
||||
[cases.present]
|
||||
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
|
||||
param_types = ["const FakeThriftWithData&"]
|
||||
setup = '''
|
||||
FakeThriftWithData ret;
|
||||
@ -78,6 +79,7 @@ raw_definitions = '''
|
||||
{"name":"__isset", "staticSize":3}
|
||||
]}]'''
|
||||
[cases.missing]
|
||||
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
|
||||
param_types = ["const FakeThriftWithoutData&"]
|
||||
setup = '''
|
||||
FakeThriftWithoutData ret;
|
||||
@ -98,6 +100,7 @@ raw_definitions = '''
|
||||
{"name":"__isset", "staticSize":3}
|
||||
]}]'''
|
||||
[cases.mixed]
|
||||
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
|
||||
param_types = ["const Mixed&"]
|
||||
setup = '''
|
||||
Mixed ret;
|
||||
|
@ -10,6 +10,7 @@ thrift_definitions = '''
|
||||
|
||||
[cases]
|
||||
[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&"]
|
||||
setup = '''
|
||||
namespaceA::namespaceB::TTTTT ret;
|
||||
|
@ -33,6 +33,13 @@ namespace cpp2 {
|
||||
{"typeName":"storage_type", "name":"value_", "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]
|
||||
param_types = ["const cpp2::DynamicUnion&"]
|
||||
setup = '''
|
||||
@ -47,6 +54,13 @@ namespace cpp2 {
|
||||
{"typeName":"storage_type", "name":"value_", "staticSize":24},
|
||||
{"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]
|
||||
param_types = ["const cpp2::DynamicUnion&"]
|
||||
setup = '''
|
||||
@ -61,3 +75,10 @@ namespace cpp2 {
|
||||
{"typeName":"storage_type", "name":"value_", "staticSize":24},
|
||||
{"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}
|
||||
]}]'''
|
||||
|
@ -27,6 +27,13 @@ definitions = '''
|
||||
{"name":"a", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3},
|
||||
{"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]
|
||||
param_types = ["const Bar_2&"]
|
||||
setup = '''
|
||||
@ -40,3 +47,10 @@ definitions = '''
|
||||
{"name":"a", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3},
|
||||
{"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}
|
||||
]}]'''
|
||||
|
@ -4,9 +4,15 @@ definitions = '''
|
||||
using UsingUInt64 = uint64_t;
|
||||
using IntVector = std::vector<int>;
|
||||
using Anonymous = struct { int n; };
|
||||
|
||||
template <typename T>
|
||||
struct Proxy {
|
||||
T t;
|
||||
};
|
||||
'''
|
||||
[cases]
|
||||
[cases.c_style]
|
||||
oil_disable = "oil can't pick up top level typedefs"
|
||||
param_types = ["TdUInt64"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -24,6 +30,7 @@ definitions = '''
|
||||
}
|
||||
]}]'''
|
||||
[cases.using]
|
||||
oil_disable = "oil can't pick up top level typedefs"
|
||||
param_types = ["UsingUInt64"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -41,6 +48,7 @@ definitions = '''
|
||||
}
|
||||
]}]'''
|
||||
[cases.container]
|
||||
oil_disable = "oil can't pick up top level typedefs"
|
||||
param_types = ["const IntVector&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -61,7 +69,7 @@ definitions = '''
|
||||
}
|
||||
]}]'''
|
||||
[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&"]
|
||||
setup = "return {};"
|
||||
expect_json = '''[{
|
||||
@ -86,3 +94,4 @@ definitions = '''
|
||||
}
|
||||
]}
|
||||
]}]'''
|
||||
|
||||
|
@ -45,14 +45,17 @@ definitions = '''
|
||||
param_types = ["const MyUnion&"]
|
||||
setup = "return 123;"
|
||||
expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]'
|
||||
expect_json_v2 = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}]'
|
||||
[cases.vector]
|
||||
param_types = ["const MyUnion&"]
|
||||
setup = "return std::vector{1,2,3};"
|
||||
expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]'
|
||||
expect_json_v2 = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}]'
|
||||
[cases.unordered_map]
|
||||
param_types = ["const MyUnion&"]
|
||||
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_v2 = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}]'
|
||||
|
||||
[cases.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":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]
|
||||
param_types = ["const TaggedUnion&"]
|
||||
@ -72,6 +80,11 @@ definitions = '''
|
||||
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "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]
|
||||
param_types = ["const TaggedUnion&"]
|
||||
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":"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]
|
||||
param_types = ["const TaggedUnion&"]
|
||||
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":"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":[]}
|
||||
]}]'''
|
||||
|
@ -50,14 +50,16 @@ TEST_F(AddChildrenTest, SimpleStruct) {
|
||||
TEST_F(AddChildrenTest, InheritanceStatic) {
|
||||
// Should do nothing
|
||||
test("oid_test_case_inheritance_access_public", R"(
|
||||
[2] Pointer
|
||||
[4] Pointer
|
||||
[0] Class: Public (size: 8)
|
||||
Parent (offset: 0)
|
||||
[1] Class: Base (size: 4)
|
||||
Member: base_int (offset: 0)
|
||||
Primitive: int32_t
|
||||
[3] Typedef: int32_t
|
||||
[2] Typedef: __int32_t
|
||||
Primitive: int32_t
|
||||
Member: public_int (offset: 4)
|
||||
Primitive: int32_t
|
||||
[3]
|
||||
)");
|
||||
}
|
||||
|
||||
|
@ -144,14 +144,16 @@ TEST_F(DrgnParserTest, SimpleUnion) {
|
||||
|
||||
TEST_F(DrgnParserTest, Inheritance) {
|
||||
test("oid_test_case_inheritance_access_public", R"(
|
||||
[2] Pointer
|
||||
[4] Pointer
|
||||
[0] Class: Public (size: 8)
|
||||
Parent (offset: 0)
|
||||
[1] Class: Base (size: 4)
|
||||
Member: base_int (offset: 0)
|
||||
Primitive: int32_t
|
||||
[3] Typedef: int32_t
|
||||
[2] Typedef: __int32_t
|
||||
Primitive: int32_t
|
||||
Member: public_int (offset: 4)
|
||||
Primitive: int32_t
|
||||
[3]
|
||||
)");
|
||||
}
|
||||
|
||||
|
@ -446,7 +446,7 @@ TEST(NameGenTest, AnonymousTypes) {
|
||||
EXPECT_EQ(myenum.name(), "__oi_anon_1");
|
||||
EXPECT_EQ(mytypedef.name(), "__oi_anon_2");
|
||||
|
||||
EXPECT_EQ(myclass.inputName(), "");
|
||||
EXPECT_EQ(myenum.inputName(), "");
|
||||
EXPECT_EQ(myclass.inputName(), "__oi_anon_0");
|
||||
EXPECT_EQ(myenum.inputName(), "__oi_anon_1");
|
||||
EXPECT_EQ(mytypedef.inputName(), "");
|
||||
}
|
||||
|
@ -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 names of types contained in an executable's debug information.
|
||||
|
||||
- `ctype`
|
||||
|
||||
A reference to the enum `ContainerTypeEnum` in OI's source code.
|
||||
|
||||
- `header`
|
||||
|
||||
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.
|
||||
|
||||
- `handler`
|
||||
- `processor.type`
|
||||
|
||||
C++ code for the definition of a `TypeHandler` class for this container. See
|
||||
further down for a description.
|
||||
The static type that will be filled in by this particular processor. Multiple
|
||||
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
|
||||
- `decl` and `func` fields are ignored when using `-ftyped-data-segment` and the
|
||||
`handler` field is used instead.
|
||||
|
||||
### TypeHandler Classes
|
||||
A `TypeHandler` class describes both what a type will write into the data segment
|
||||
and how to write it. It consists of two major parts:
|
||||
- `using type = ...;` - describe what it will write into the data segment.
|
||||
- `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 TreeBuilder V2
|
||||
- `decl`, `func`, and `handler` fields are ignored when using `-ftree-builder-v2`.
|
||||
The `TypeHandler` is instead constructed from `traversal_func` and `processor`
|
||||
entries.
|
||||
|
||||
## Changes introduced with TypeGraph
|
||||
- `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.
|
||||
|
||||
### 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`
|
||||
|
||||
The first `numTemplateParams` template parameters for this container represent
|
||||
|
@ -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);
|
||||
"""
|
||||
|
@ -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;
|
||||
"""
|
||||
|
@ -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);
|
||||
'''
|
||||
|
@ -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);
|
||||
"""
|
||||
|
@ -1,10 +1,10 @@
|
||||
[info]
|
||||
type_name = "std::vector"
|
||||
stub_template_params = [1]
|
||||
ctype = "SEQ_TYPE"
|
||||
header = "vector"
|
||||
|
||||
# Old:
|
||||
ctype = "SEQ_TYPE"
|
||||
typeName = "std::vector<"
|
||||
ns = ["namespace std"]
|
||||
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
Loading…
Reference in New Issue
Block a user