blob: 1756c6b6bd70ef7345551ec7560e8520e4454293 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TOOLS_FIDLCAT_LIB_WIRE_TYPES_H_
#define TOOLS_FIDLCAT_LIB_WIRE_TYPES_H_
#include <lib/fit/function.h>
#if defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define le16toh(x) OSSwapLittleToHostInt16(x)
#define le32toh(x) OSSwapLittleToHostInt32(x)
#define le64toh(x) OSSwapLittleToHostInt64(x)
#else
#include <endian.h>
#endif
#ifdef __Fuchsia__
#include <zircon/types.h>
#else
typedef uint32_t zx_handle_t;
#endif
#include <sstream>
#include <string>
#include <vector>
#include "rapidjson/document.h"
#include "tools/fidlcat/lib/library_loader.h"
// This file contains classes that help parse FIDL messages as they come in off
// the wire.
namespace fidlcat {
// Indicates a position in the FIDL message, which is a combination of current
// location in the byte part |byte_pos| and current location in the handle part
// |handle_pos|.
class Marker {
public:
Marker() = delete;
Marker(const uint8_t* bytes, const zx_handle_t* handles)
: byte_pos_(bytes),
handle_pos_(handles),
end_byte_pos_(nullptr),
end_handle_pos_(nullptr) {}
Marker(const uint8_t* bytes, const zx_handle_t* handles, const Marker& end)
: byte_pos_(bytes),
handle_pos_(handles),
end_byte_pos_(end.byte_pos()),
end_handle_pos_(end.handle_pos()) {}
const uint8_t* byte_pos() const { return byte_pos_; }
const zx_handle_t* handle_pos() const { return handle_pos_; }
bool is_valid() const;
// Advances the bytes in the given |marker| by the given |amount| (or to the
// given |pos|). Sets marker.is_valid to false if the new position would be
// off the end of the tracked message.
//
// TODO(DX-1260): Consider folding all marker tracking into this class, so
// that Markers and ObjectTrackers don't have to be passed around everywhere.
void AdvanceBytesBy(size_t amount);
void AdvanceBytesTo(const uint8_t* pos);
void AdvanceHandlesBy(size_t amount);
void AdvanceHandlesTo(const zx_handle_t* pos);
private:
const uint8_t* byte_pos_;
const zx_handle_t* handle_pos_;
const uint8_t* end_byte_pos_;
const zx_handle_t* end_handle_pos_;
};
class ObjectTracker;
// A function that, given the location |marker| of an object (in-line or
// out-of-line), generates a JSON representation into |value| using |allocator|.
//
// The |marker| is set to the out-of-line size of the object (which may be
// 0).
typedef fit::function<void(ObjectTracker*, Marker& marker,
rapidjson::Value& value,
rapidjson::Document::AllocatorType& allocator)>
ValueGeneratingCallback;
// Encapsulates state when parsing wire format encoded FIDL objects.
//
// For each element a print function encounters on its walk through a
// fixed-length FIDL object, it enqueues a callback to be executed when the end
// of that element is reached. If the element is an out-of-line object, it will
// parse the out-of-line object and return the value, or simply return the
// (captured) in line value.
class ObjectTracker {
public:
// Creates a tracker for the given array of bytes.
ObjectTracker(const Marker& end) : end_(end) {}
// Executes all of the callbacks, starting at bytes (as passed to the
// constructor) + the given offset.
bool RunCallbacksFrom(Marker& marker);
// Enqueues a callback to be executed when running RunCallbacksFrom.
// |key| is the JSON key it will construct.
// |callback| is the callback to execute to construct the value.
// |target_object| is the place to put the key, value pair.
// |allocator| is the correct allocator for that object.
void ObjectEnqueue(const std::string& key, ValueGeneratingCallback&& callback,
rapidjson::Value& target_object,
rapidjson::Document::AllocatorType& allocator);
// Enqueues a callback to be executed when running RunCallbacksFrom.
// |callback| is the callback to execute to construct the value.
// |target_array| is the array in which to insert the value.
// |allocator| is the correct allocator for that object.
void ArrayEnqueue(ValueGeneratingCallback&& callback,
rapidjson::Value& target_array,
rapidjson::Document::AllocatorType& allocator);
ObjectTracker(const ObjectTracker&) = delete;
ObjectTracker& operator=(const ObjectTracker&) = delete;
const Marker& end() { return end_; }
private:
const Marker end_;
std::vector<fit::function<void(Marker& marker)>> callbacks_;
};
// A FIDL type. Provides methods for generating instances of this type.
//
// TODO: This may be misnamed. Right now, it's mostly only useful for
// generating JSON values and comparing instances for equality. Consider
// removing it from this file or making it more generic (less tied to writing
// out JSON).
class Type {
friend class InterfaceMethodParameter;
friend class Library;
public:
Type() {}
virtual ~Type() {}
// Takes a Marker |marker| and length of the data part |length|, and
// tells |tracker| to invoke |callback| on their representation given this
// type.
//
// A callback may outlive the Type that provided it. They should therefore
// not refer to anything in the containing type, as that might get deleted.
//
// Returns a Marker pointing to the next element inline in this type.
// marker.is_valid is set to false and otherwise unchanged from |marker| if
// there was something wrong with the data (e.g., this method would have had
// to read off the end of the data to parse it).
virtual Marker GetValueCallback(Marker marker, size_t length,
ObjectTracker* tracker,
ValueGeneratingCallback& callback) const = 0;
// Takes a Marker |marker| and length of the data part |length|, and
// returns whether that is equal to the Value represented by |value| according
// to this type.
virtual bool ValueEquals(Marker marker, size_t length,
const rapidjson::Value& value) const;
// Returns the size of this type when embedded in another object.
virtual size_t InlineSize() const;
// Gets a Type object representing the |type|. |type| is a JSON object a
// field "kind" that states the type (e.g., "array", "vector", "foo.bar/Baz").
// |loader| is the set of libraries to use to find types that need to be given
// by identifier (e.g., "foo.bar/Baz").
static std::unique_ptr<Type> GetType(const LibraryLoader& loader,
const rapidjson::Value& type);
// Gets a Type object representing the |type|. |type| is a JSON object with a
// "subtype" field that represents a scalar type (e.g., "float64", "uint32")
static std::unique_ptr<Type> TypeFromPrimitive(const rapidjson::Value& type);
// Gets a Type object representing the |type_name|. |type| is a string that
// represents a scalar type (e.g., "float64", "uint32").
static std::unique_ptr<Type> ScalarTypeFromName(const std::string& type_name);
// Gets a Type object representing the |type|. |type| is a JSON object a
// field "kind" that states the type. "kind" is an identifier
// (e.g.,"foo.bar/Baz"). |loader| is the set of libraries to use to lookup
// that identifier.
static std::unique_ptr<Type> TypeFromIdentifier(const LibraryLoader& loader,
const rapidjson::Value& type);
static std::unique_ptr<Type> get_illegal();
Type& operator=(const Type& other) = default;
Type(const Type& other) = default;
};
// An instance of this class is created when the system can't determine the real
// class (e.g., in cases of corrupted metadata). The ValueGeneratingCallback
// spits back hex pairs.
class UnknownType : public Type {
public:
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override;
};
class StringType : public Type {
public:
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override;
virtual size_t InlineSize() const override {
return sizeof(uint64_t) + sizeof(uint64_t);
}
};
namespace internal {
// These are convenience functions for reading little endian (i.e., FIDL wire
// format encoded) bits.
template <typename T>
class LeToHost {
public:
static T le_to_host(const T* ts);
};
template <typename T>
T LeToHost<T>::le_to_host(const T* bytes) {
if constexpr (std::is_same<T, uint8_t>::value) {
return *bytes;
} else if constexpr (std::is_same<T, uint16_t>::value) {
return le16toh(*bytes);
} else if constexpr (std::is_same<T, uint32_t>::value) {
return le32toh(*bytes);
} else if constexpr (std::is_same<T, uint64_t>::value) {
return le64toh(*bytes);
} else if constexpr (std::is_same<T, uintptr_t>::value &&
sizeof(T) == sizeof(uint64_t)) {
// NB: On Darwin, uintptr_t and uint64_t are different things.
return le64toh(*bytes);
}
}
template <typename T>
struct GetUnsigned {
using type = typename std::conditional<std::is_same<float, T>::value,
uint32_t, uint64_t>::type;
};
template <typename T, typename P>
T MemoryFrom(P bytes) {
static_assert(std::is_pointer<P>::value,
"MemoryFrom can only be used on pointers");
using U = typename std::conditional<std::is_integral<T>::value,
std::make_unsigned<T>,
GetUnsigned<T>>::type::type;
union {
U uval;
T tval;
} u;
u.uval = LeToHost<U>::le_to_host(reinterpret_cast<const U*>(bytes));
return u.tval;
}
} // namespace internal
// A generic type that can be used for any numeric value that corresponds to a
// C++ arithmetic value.
template <typename T>
class NumericType : public Type {
static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value,
"NumericType can only be used for numerics");
public:
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override {
T val = internal::MemoryFrom<T, const uint8_t*>(marker.byte_pos());
callback = [val](ObjectTracker* tracker, Marker& marker,
rapidjson::Value& value,
rapidjson::Document::AllocatorType& allocator) {
value.SetString(std::to_string(val).c_str(), allocator);
};
marker.AdvanceBytesBy(sizeof(T));
return marker;
}
virtual bool ValueEquals(Marker marker, size_t length,
const rapidjson::Value& value) const override {
T lhs = internal::MemoryFrom<T, const uint8_t*>(marker.byte_pos());
std::istringstream input(value["value"].GetString());
// Because int8_t is really char, and we don't want to read that.
using R =
typename std::conditional<std::is_same<T, int8_t>::value, int, T>::type;
R rhs;
input >> rhs;
return lhs == rhs;
}
virtual size_t InlineSize() const override { return sizeof(T); }
};
class BoolType : public Type {
public:
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override;
};
class StructType : public Type {
public:
StructType(const Struct& str) : struct_(str) {}
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override;
virtual size_t InlineSize() const override;
private:
const Struct& struct_;
};
class UnionType : public Type {
public:
UnionType(const Union& uni);
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override;
virtual size_t InlineSize() const override;
private:
const Union& union_;
};
// A type that can be used to express that this is a pointer to an instance of
// another type.
class PointerType : public Type {
public:
explicit PointerType(Type* target_type);
// PointerType's GetValueCallback method does the following:
//
// a) In the case where the intptr at the marker is null, returns a
// callback that sets the value to null.
// b) In the case where the intptr at the marker is not null, returns a
// callback that tracks an instance of the wrapped type out-of-line,
// with its own ObjectTracker.
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override;
private:
std::shared_ptr<Type> target_type_;
};
class ElementSequenceType : public Type {
public:
explicit ElementSequenceType(std::unique_ptr<Type>&& component_type);
protected:
explicit ElementSequenceType(std::shared_ptr<Type> component_type);
// |tracker| is the ObjectTracker to use for the callback.
// |count| is the number of elements in this sequence
// |marker| is a position in the message we're decoding
// |length| is the length of the byte sequence.
ValueGeneratingCallback GetIteratingCallback(ObjectTracker* tracker,
size_t count, Marker marker,
size_t length) const;
// The unique_ptr is converted to a shared_ptr so that it can be used by the
// callback returned by GetValueCallback, which may outlive the
// ElementSequenceType instance.
std::shared_ptr<Type> component_type_;
};
class ArrayType : public ElementSequenceType {
public:
ArrayType(std::unique_ptr<Type>&& component_type, uint32_t count);
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override;
virtual size_t InlineSize() const override {
return component_type_->InlineSize() * count_;
}
private:
uint32_t count_;
};
class VectorType : public ElementSequenceType {
public:
VectorType(std::unique_ptr<Type>&& component_type);
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override;
virtual size_t InlineSize() const override {
return sizeof(uint64_t) + sizeof(uint64_t);
}
private:
VectorType(std::shared_ptr<Type> component_type, size_t element_size);
};
class EnumType : public Type {
public:
EnumType(const Enum& e);
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override;
private:
const Enum& enum_;
};
class HandleType : public Type {
public:
HandleType() {}
virtual Marker GetValueCallback(
Marker marker, size_t length, ObjectTracker* tracker,
ValueGeneratingCallback& callback) const override;
virtual size_t InlineSize() const override { return sizeof(zx_handle_t); }
};
using Float32Type = NumericType<float>;
using Float64Type = NumericType<double>;
using Int8Type = NumericType<int8_t>;
using Int16Type = NumericType<int16_t>;
using Int32Type = NumericType<int32_t>;
using Int64Type = NumericType<int64_t>;
using Uint8Type = NumericType<uint8_t>;
using Uint16Type = NumericType<uint16_t>;
using Uint32Type = NumericType<uint32_t>;
using Uint64Type = NumericType<uint64_t>;
} // namespace fidlcat
#endif // TOOLS_FIDLCAT_LIB_WIRE_TYPES_H_