mirror of
https://github.com/abseil/abseil-cpp.git
synced 2026-06-04 20:14:23 +08:00
Merge branch 'master' into optimize-str-join
This commit is contained in:
@@ -900,7 +900,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
|
||||
#error ABSL_INTERNAL_HAS_CXA_DEMANGLE cannot be directly set
|
||||
#elif defined(OS_ANDROID) && (defined(__i386__) || defined(__x86_64__))
|
||||
#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 0
|
||||
#elif defined(__GNUC__) && !defined(__mips__)
|
||||
#elif defined(__GNUC__)
|
||||
#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1
|
||||
#elif defined(__clang__) && !defined(_MSC_VER)
|
||||
#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1
|
||||
|
||||
@@ -37,6 +37,33 @@ bool IsUpper(char c) { return 'A' <= c && c <= 'Z'; }
|
||||
bool IsAlpha(char c) { return IsLower(c) || IsUpper(c); }
|
||||
bool IsIdentifierChar(char c) { return IsAlpha(c) || IsDigit(c) || c == '_'; }
|
||||
|
||||
const char* BasicTypeName(char c) {
|
||||
switch (c) {
|
||||
case 'a': return "i8";
|
||||
case 'b': return "bool";
|
||||
case 'c': return "char";
|
||||
case 'd': return "f64";
|
||||
case 'e': return "str";
|
||||
case 'f': return "f32";
|
||||
case 'h': return "u8";
|
||||
case 'i': return "isize";
|
||||
case 'j': return "usize";
|
||||
case 'l': return "i32";
|
||||
case 'm': return "u32";
|
||||
case 'n': return "i128";
|
||||
case 'o': return "u128";
|
||||
case 'p': return "_";
|
||||
case 's': return "i16";
|
||||
case 't': return "u16";
|
||||
case 'u': return "()";
|
||||
case 'v': return "...";
|
||||
case 'x': return "i64";
|
||||
case 'y': return "u64";
|
||||
case 'z': return "!";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Parser for Rust symbol mangling v0, whose grammar is defined here:
|
||||
//
|
||||
// https://doc.rust-lang.org/rustc/symbol-mangling/v0.html#symbol-grammar-summary
|
||||
@@ -115,12 +142,16 @@ class RustSymbolParser {
|
||||
|
||||
// path -> crate-root | inherent-impl | trait-impl | trait-definition |
|
||||
// nested-path | generic-args | backref
|
||||
//
|
||||
// Note that ABSL_DEMANGLER_RECURSE does not work inside a nested switch
|
||||
// (which would hide the generated case label). Thus we jump out of the
|
||||
// inner switch with gotos before performing any fake recursion.
|
||||
path:
|
||||
switch (Take()) {
|
||||
case 'C': goto crate_root;
|
||||
case 'M': return false; // inherent-impl not yet implemented
|
||||
case 'X': return false; // trait-impl not yet implemented
|
||||
case 'Y': return false; // trait-definition not yet implemented
|
||||
case 'Y': goto trait_definition;
|
||||
case 'N': goto nested_path;
|
||||
case 'I': return false; // generic-args not yet implemented
|
||||
case 'B': return false; // backref not yet implemented
|
||||
@@ -132,7 +163,16 @@ class RustSymbolParser {
|
||||
if (!ParseIdentifier()) return false;
|
||||
continue;
|
||||
|
||||
// nested-path -> N namespace path identifier (N consumed above)
|
||||
// trait-definition -> Y type path (Y already consumed)
|
||||
trait_definition:
|
||||
if (!Emit("<")) return false;
|
||||
ABSL_DEMANGLER_RECURSE(type, kTraitDefinitionInfix);
|
||||
if (!Emit(" as ")) return false;
|
||||
ABSL_DEMANGLER_RECURSE(path, kTraitDefinitionEnding);
|
||||
if (!Emit(">")) return false;
|
||||
continue;
|
||||
|
||||
// nested-path -> N namespace path identifier (N already consumed)
|
||||
// namespace -> lower | upper
|
||||
nested_path:
|
||||
// Uppercase namespaces must be saved on the stack so we can print
|
||||
@@ -156,6 +196,98 @@ class RustSymbolParser {
|
||||
|
||||
// Neither upper or lower
|
||||
return false;
|
||||
|
||||
// type -> basic-type | array-type | slice-type | tuple-type |
|
||||
// ref-type | mut-ref-type | const-ptr-type | mut-ptr-type |
|
||||
// fn-type | dyn-trait-type | path | backref
|
||||
//
|
||||
// We use ifs instead of switch (Take()) because the default case jumps
|
||||
// to path, which will need to see the first character not yet Taken
|
||||
// from the input. Because we do not use a nested switch here,
|
||||
// ABSL_DEMANGLER_RECURSE works fine in the 'S' case.
|
||||
type:
|
||||
if (IsLower(Peek())) {
|
||||
const char* type_name = BasicTypeName(Take());
|
||||
if (type_name == nullptr || !Emit(type_name)) return false;
|
||||
continue;
|
||||
}
|
||||
if (Eat('A')) return false; // array-type not yet implemented
|
||||
if (Eat('S')) {
|
||||
if (!Emit("[")) return false;
|
||||
ABSL_DEMANGLER_RECURSE(type, kSliceEnding);
|
||||
if (!Emit("]")) return false;
|
||||
continue;
|
||||
}
|
||||
if (Eat('T')) goto tuple_type;
|
||||
if (Eat('R')) {
|
||||
if (!Emit("&")) return false;
|
||||
if (Eat('L')) return false; // lifetime not yet implemented
|
||||
goto type;
|
||||
}
|
||||
if (Eat('Q')) {
|
||||
if (!Emit("&mut ")) return false;
|
||||
if (Eat('L')) return false; // lifetime not yet implemented
|
||||
goto type;
|
||||
}
|
||||
if (Eat('P')) {
|
||||
if (!Emit("*const ")) return false;
|
||||
goto type;
|
||||
}
|
||||
if (Eat('O')) {
|
||||
if (!Emit("*mut ")) return false;
|
||||
goto type;
|
||||
}
|
||||
if (Eat('F')) return false; // fn-type not yet implemented
|
||||
if (Eat('D')) return false; // dyn-trait-type not yet implemented
|
||||
if (Eat('B')) return false; // backref not yet implemented
|
||||
goto path;
|
||||
|
||||
// tuple-type -> T type* E (T already consumed)
|
||||
tuple_type:
|
||||
if (!Emit("(")) return false;
|
||||
|
||||
// The toolchain should call the unit type u instead of TE, but the
|
||||
// grammar and other demanglers also recognize TE, so we do too.
|
||||
if (Eat('E')) {
|
||||
if (!Emit(")")) return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// A tuple with one element is rendered (type,) instead of (type).
|
||||
ABSL_DEMANGLER_RECURSE(type, kAfterFirstTupleElement);
|
||||
if (Eat('E')) {
|
||||
if (!Emit(",)")) return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// A tuple with two elements is of course (x, y).
|
||||
if (!Emit(", ")) return false;
|
||||
ABSL_DEMANGLER_RECURSE(type, kAfterSecondTupleElement);
|
||||
if (Eat('E')) {
|
||||
if (!Emit(")")) return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// And (x, y, z) for three elements.
|
||||
if (!Emit(", ")) return false;
|
||||
ABSL_DEMANGLER_RECURSE(type, kAfterThirdTupleElement);
|
||||
if (Eat('E')) {
|
||||
if (!Emit(")")) return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// For longer tuples we write (x, y, z, ...), printing none of the
|
||||
// content of the fourth and later types. Thus we avoid exhausting
|
||||
// output buffers and human readers' patience when some library has a
|
||||
// long tuple as an implementation detail, without having to
|
||||
// completely obfuscate all tuples.
|
||||
if (!Emit(", ...)")) return false;
|
||||
++silence_depth_;
|
||||
while (!Eat('E')) {
|
||||
ABSL_DEMANGLER_RECURSE(type, kAfterSubsequentTupleElement);
|
||||
}
|
||||
--silence_depth_;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +301,13 @@ class RustSymbolParser {
|
||||
kVendorSpecificSuffix,
|
||||
kIdentifierInUppercaseNamespace,
|
||||
kIdentifierInLowercaseNamespace,
|
||||
kTraitDefinitionInfix,
|
||||
kTraitDefinitionEnding,
|
||||
kSliceEnding,
|
||||
kAfterFirstTupleElement,
|
||||
kAfterSecondTupleElement,
|
||||
kAfterThirdTupleElement,
|
||||
kAfterSubsequentTupleElement,
|
||||
};
|
||||
|
||||
// Element count for the stack_ array. A larger kStackSize accommodates more
|
||||
@@ -320,6 +459,8 @@ class RustSymbolParser {
|
||||
|
||||
// Emit the beginnings of braced forms like {shim:vtable#0}.
|
||||
if (uppercase_namespace == '\0') {
|
||||
// Decoding of Punycode is not yet implemented. For now we emit
|
||||
// "{Punycode ...}" with the raw encoding inside.
|
||||
if (is_punycoded && !Emit("{Punycode ")) return false;
|
||||
} else {
|
||||
switch (uppercase_namespace) {
|
||||
|
||||
@@ -33,10 +33,8 @@ namespace debugging_internal {
|
||||
// call-stack space. It is suitable for symbolizing stack traces in a signal
|
||||
// handler.
|
||||
//
|
||||
// The demangling logic is under development. In this version of Abseil,
|
||||
// DemangleRustSymbolEncoding parses a few simple kinds of symbol names, but
|
||||
// nothing having backreferences in the input or angle brackets in the
|
||||
// demangling, and it emits raw Punycode instead of the UTF-8 represented by it.
|
||||
// The demangling logic is under development; search for "not yet implemented"
|
||||
// in the .cc file to see where the gaps are.
|
||||
bool DemangleRustSymbolEncoding(const char* mangled, char* out,
|
||||
std::size_t out_size);
|
||||
|
||||
|
||||
@@ -209,6 +209,90 @@ TEST(DemangleRust, NestedUppercaseNamespaces) {
|
||||
"crate_name::{Y:y#2}::{X:x#3}::{closure#4}");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, TraitDefinition) {
|
||||
EXPECT_DEMANGLING(
|
||||
"_RNvYNvC7crate_a9my_structNvC7crate_b8my_trait1f",
|
||||
"<crate_a::my_struct as crate_b::my_trait>::f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, BasicTypeNames) {
|
||||
EXPECT_DEMANGLING("_RNvYaNvC1c1t1f", "<i8 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYbNvC1c1t1f", "<bool as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYcNvC1c1t1f", "<char as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYdNvC1c1t1f", "<f64 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYeNvC1c1t1f", "<str as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYfNvC1c1t1f", "<f32 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYhNvC1c1t1f", "<u8 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYiNvC1c1t1f", "<isize as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYjNvC1c1t1f", "<usize as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYlNvC1c1t1f", "<i32 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYmNvC1c1t1f", "<u32 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYnNvC1c1t1f", "<i128 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYoNvC1c1t1f", "<u128 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYpNvC1c1t1f", "<_ as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYsNvC1c1t1f", "<i16 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYtNvC1c1t1f", "<u16 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYuNvC1c1t1f", "<() as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYvNvC1c1t1f", "<... as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYxNvC1c1t1f", "<i64 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYyNvC1c1t1f", "<u64 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYzNvC1c1t1f", "<! as c::t>::f");
|
||||
|
||||
EXPECT_DEMANGLING_FAILS("_RNvYkNvC1c1t1f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, SliceTypes) {
|
||||
EXPECT_DEMANGLING("_RNvYSlNvC1c1t1f", "<[i32] as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYSNvC1d1sNvC1c1t1f", "<[d::s] as c::t>::f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, ImmutableReferenceTypes) {
|
||||
EXPECT_DEMANGLING("_RNvYRlNvC1c1t1f", "<&i32 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYRNvC1d1sNvC1c1t1f", "<&d::s as c::t>::f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, MutableReferenceTypes) {
|
||||
EXPECT_DEMANGLING("_RNvYQlNvC1c1t1f", "<&mut i32 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYQNvC1d1sNvC1c1t1f", "<&mut d::s as c::t>::f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, ConstantRawPointerTypes) {
|
||||
EXPECT_DEMANGLING("_RNvYPlNvC1c1t1f", "<*const i32 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYPNvC1d1sNvC1c1t1f", "<*const d::s as c::t>::f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, MutableRawPointerTypes) {
|
||||
EXPECT_DEMANGLING("_RNvYOlNvC1c1t1f", "<*mut i32 as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYONvC1d1sNvC1c1t1f", "<*mut d::s as c::t>::f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, TupleLength0) {
|
||||
EXPECT_DEMANGLING("_RNvYTENvC1c1t1f", "<() as c::t>::f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, TupleLength1) {
|
||||
EXPECT_DEMANGLING("_RNvYTlENvC1c1t1f", "<(i32,) as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYTNvC1d1sENvC1c1t1f", "<(d::s,) as c::t>::f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, TupleLength2) {
|
||||
EXPECT_DEMANGLING("_RNvYTlmENvC1c1t1f", "<(i32, u32) as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYTNvC1d1xNvC1e1yENvC1c1t1f",
|
||||
"<(d::x, e::y) as c::t>::f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, TupleLength3) {
|
||||
EXPECT_DEMANGLING("_RNvYTlmnENvC1c1t1f", "<(i32, u32, i128) as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYTNvC1d1xNvC1e1yNvC1f1zENvC1c1t1f",
|
||||
"<(d::x, e::y, f::z) as c::t>::f");
|
||||
}
|
||||
|
||||
TEST(DemangleRust, LongerTuplesAbbreviated) {
|
||||
EXPECT_DEMANGLING("_RNvYTlmnoENvC1c1t1f",
|
||||
"<(i32, u32, i128, ...) as c::t>::f");
|
||||
EXPECT_DEMANGLING("_RNvYTlmnNvC1d1xNvC1e1yENvC1c1t1f",
|
||||
"<(i32, u32, i128, ...) as c::t>::f");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace debugging_internal
|
||||
|
||||
@@ -247,12 +247,20 @@ std::string StrJoin(const Range& range, absl::string_view separator,
|
||||
return strings_internal::JoinRange(range, separator, fmt);
|
||||
}
|
||||
|
||||
template <typename T, typename Formatter>
|
||||
template <typename T, typename Formatter,
|
||||
typename = typename std::enable_if<
|
||||
!std::is_convertible<T, absl::string_view>::value>::type>
|
||||
std::string StrJoin(std::initializer_list<T> il, absl::string_view separator,
|
||||
Formatter&& fmt) {
|
||||
return strings_internal::JoinRange(il, separator, fmt);
|
||||
}
|
||||
|
||||
template <typename Formatter>
|
||||
inline std::string StrJoin(std::initializer_list<absl::string_view> il,
|
||||
absl::string_view separator, Formatter&& fmt) {
|
||||
return strings_internal::JoinRange(il, separator, fmt);
|
||||
}
|
||||
|
||||
template <typename... T, typename Formatter>
|
||||
std::string StrJoin(const std::tuple<T...>& value, absl::string_view separator,
|
||||
Formatter&& fmt) {
|
||||
@@ -269,11 +277,17 @@ std::string StrJoin(const Range& range, absl::string_view separator) {
|
||||
return strings_internal::JoinRange(range, separator);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename T, typename = typename std::enable_if<!std::is_convertible<
|
||||
T, absl::string_view>::value>::type>
|
||||
std::string StrJoin(std::initializer_list<T> il, absl::string_view separator) {
|
||||
return strings_internal::JoinRange(il, separator);
|
||||
}
|
||||
|
||||
inline std::string StrJoin(std::initializer_list<absl::string_view> il,
|
||||
absl::string_view separator) {
|
||||
return strings_internal::JoinRange(il, separator);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
std::string StrJoin(const std::tuple<T...>& value,
|
||||
absl::string_view separator) {
|
||||
|
||||
@@ -428,6 +428,42 @@ TEST(StrJoin, InitializerList) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StrJoin, StringViewInitializerList) {
|
||||
{
|
||||
// Tests initializer_list of string_views
|
||||
std::string b = "b";
|
||||
EXPECT_EQ("a-b-c", absl::StrJoin({"a", b, "c"}, "-"));
|
||||
}
|
||||
{
|
||||
// Tests initializer_list of string_views with a non-default formatter
|
||||
TestingParenFormatter f;
|
||||
std::string b = "b";
|
||||
EXPECT_EQ("(a)-(b)-(c)", absl::StrJoin({"a", b, "c"}, "-", f));
|
||||
}
|
||||
|
||||
class NoCopy {
|
||||
public:
|
||||
explicit NoCopy(absl::string_view view) : view_(view) {}
|
||||
NoCopy(const NoCopy&) = delete;
|
||||
operator absl::string_view() { return view_; } // NOLINT
|
||||
private:
|
||||
absl::string_view view_;
|
||||
};
|
||||
{
|
||||
// Tests initializer_list of string_views preferred over initializer_list<T>
|
||||
// for T that is implicitly convertible to string_view
|
||||
EXPECT_EQ("a-b-c",
|
||||
absl::StrJoin({NoCopy("a"), NoCopy("b"), NoCopy("c")}, "-"));
|
||||
}
|
||||
{
|
||||
// Tests initializer_list of string_views preferred over initializer_list<T>
|
||||
// for T that is implicitly convertible to string_view
|
||||
TestingParenFormatter f;
|
||||
EXPECT_EQ("(a)-(b)-(c)",
|
||||
absl::StrJoin({NoCopy("a"), NoCopy("b"), NoCopy("c")}, "-", f));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StrJoin, Tuple) {
|
||||
EXPECT_EQ("", absl::StrJoin(std::make_tuple(), "-"));
|
||||
EXPECT_EQ("hello", absl::StrJoin(std::make_tuple("hello"), "-"));
|
||||
|
||||
Reference in New Issue
Block a user