From a28ee5b51c9ea41707d9a5d2d358ad77850264c4 Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Mon, 6 May 2024 05:56:26 -0700 Subject: Recognize some simple Rust mangled names in Demangle. PiperOrigin-RevId: 631018414 Change-Id: Ice8efa0af4cb1f72b5d62fbbea4cb12cbead8634 --- absl/debugging/internal/demangle_rust_test.cc | 216 ++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 absl/debugging/internal/demangle_rust_test.cc (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc new file mode 100644 index 00000000..2841576e --- /dev/null +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -0,0 +1,216 @@ +// Copyright 2024 The Abseil Authors +// +// 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 +// +// https://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 "absl/debugging/internal/demangle_rust.h" + +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +namespace { + +// If DemangleRustSymbolEncoding(mangled, , buffer_size) returns true and seems not to have overrun its output +// buffer, returns the string written by DemangleRustSymbolEncoding; otherwise +// returns an error message. +std::string ResultOfDemangling(const char* mangled, std::size_t buffer_size) { + // Fill the buffer with something other than NUL so we test whether Demangle + // appends trailing NUL as expected. + std::string buffer(buffer_size + 1, '~'); + constexpr char kCanaryCharacter = 0x7f; // arbitrary unlikely value + buffer[buffer_size] = kCanaryCharacter; + if (!DemangleRustSymbolEncoding(mangled, &buffer[0], buffer_size)) { + return "Failed parse"; + } + if (buffer[buffer_size] != kCanaryCharacter) { + return "Buffer overrun by output: " + buffer.substr(0, buffer_size + 1) + + "..."; + } + return buffer.data(); // Not buffer itself: this trims trailing padding. +} + +// Tests that DemangleRustSymbolEncoding converts mangled into plaintext given +// enough output buffer space but returns false and avoids overrunning a buffer +// that is one byte too short. +// +// The lambda wrapping allows ASSERT_EQ to branch out the first time an +// expectation is not satisfied, preventing redundant errors for the same bug. +// +// We test first with excess space so that if the algorithm just computes the +// wrong answer, it will be clear from the error log that the bounds checks are +// unlikely to be the code at fault. +#define EXPECT_DEMANGLING(mangled, plaintext) \ + do { \ + [] { \ + constexpr std::size_t plenty_of_space = sizeof(plaintext) + 128; \ + constexpr std::size_t just_enough_space = sizeof(plaintext); \ + constexpr std::size_t one_byte_too_few = sizeof(plaintext) - 1; \ + const char* expected_plaintext = plaintext; \ + const char* expected_error = "Failed parse"; \ + ASSERT_EQ(ResultOfDemangling(mangled, plenty_of_space), \ + expected_plaintext); \ + ASSERT_EQ(ResultOfDemangling(mangled, just_enough_space), \ + expected_plaintext); \ + ASSERT_EQ(ResultOfDemangling(mangled, one_byte_too_few), \ + expected_error); \ + }(); \ + } while (0) + +// Tests that DemangleRustSymbolEncoding rejects the given input (typically, a +// truncation of a real Rust symbol name). +#define EXPECT_DEMANGLING_FAILS(mangled) \ + do { \ + constexpr std::size_t plenty_of_space = 1024; \ + const char* expected_error = "Failed parse"; \ + EXPECT_EQ(ResultOfDemangling(mangled, plenty_of_space), expected_error); \ + } while (0) + +// Piping grep -C 1 _R demangle_test.cc into your favorite c++filt +// implementation allows you to verify that the goldens below are reasonable. + +TEST(DemangleRust, EmptyDemangling) { + EXPECT_TRUE(DemangleRustSymbolEncoding("_RC0", nullptr, 0)); +} + +TEST(DemangleRust, FunctionAtCrateLevel) { + EXPECT_DEMANGLING("_RNvC10crate_name9func_name", "crate_name::func_name"); + EXPECT_DEMANGLING( + "_RNvCs09azAZ_10crate_name9func_name", "crate_name::func_name"); +} + +TEST(DemangleRust, TruncationsOfFunctionAtCrateLevel) { + EXPECT_DEMANGLING_FAILS("_R"); + EXPECT_DEMANGLING_FAILS("_RN"); + EXPECT_DEMANGLING_FAILS("_RNvC"); + EXPECT_DEMANGLING_FAILS("_RNvC10"); + EXPECT_DEMANGLING_FAILS("_RNvC10crate_nam"); + EXPECT_DEMANGLING_FAILS("_RNvC10crate_name"); + EXPECT_DEMANGLING_FAILS("_RNvC10crate_name9"); + EXPECT_DEMANGLING_FAILS("_RNvC10crate_name9func_nam"); + EXPECT_DEMANGLING_FAILS("_RNvCs"); + EXPECT_DEMANGLING_FAILS("_RNvCs09azAZ"); + EXPECT_DEMANGLING_FAILS("_RNvCs09azAZ_"); +} + +TEST(DemangleRust, VendorSuffixes) { + EXPECT_DEMANGLING("_RNvC10crate_name9func_name.!@#", "crate_name::func_name"); + EXPECT_DEMANGLING("_RNvC10crate_name9func_name$!@#", "crate_name::func_name"); +} + +TEST(DemangleRust, UnicodeIdentifiers) { + EXPECT_DEMANGLING("_RNvC7ice_cap17Eyjafjallajökull", + "ice_cap::Eyjafjallajökull"); + EXPECT_DEMANGLING("_RNvC7ice_caps_u19Eyjafjallajkull_jtb", + "ice_cap::{Punycode Eyjafjallajkull_jtb}"); +} + +TEST(DemangleRust, FunctionInModule) { + EXPECT_DEMANGLING("_RNvNtCs09azAZ_10crate_name11module_name9func_name", + "crate_name::module_name::func_name"); +} + +TEST(DemangleRust, FunctionInFunction) { + EXPECT_DEMANGLING( + "_RNvNvCs09azAZ_10crate_name15outer_func_name15inner_func_name", + "crate_name::outer_func_name::inner_func_name"); +} + +TEST(DemangleRust, ClosureInFunction) { + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_name0", + "crate_name::func_name::{closure#0}"); + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_name0Cs123_12client_crate", + "crate_name::func_name::{closure#0}"); +} + +TEST(DemangleRust, ClosureNumbering) { + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_names_0Cs123_12client_crate", + "crate_name::func_name::{closure#1}"); + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_names0_0Cs123_12client_crate", + "crate_name::func_name::{closure#2}"); + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_names9_0Cs123_12client_crate", + "crate_name::func_name::{closure#11}"); + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_namesa_0Cs123_12client_crate", + "crate_name::func_name::{closure#12}"); + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_namesz_0Cs123_12client_crate", + "crate_name::func_name::{closure#37}"); + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_namesA_0Cs123_12client_crate", + "crate_name::func_name::{closure#38}"); + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_namesZ_0Cs123_12client_crate", + "crate_name::func_name::{closure#63}"); + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_names10_0Cs123_12client_crate", + "crate_name::func_name::{closure#64}"); + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_namesg6_0Cs123_12client_crate", + "crate_name::func_name::{closure#1000}"); +} + +TEST(DemangleRust, ClosureNumberOverflowingInt) { + EXPECT_DEMANGLING( + "_RNCNvCs09azAZ_10crate_name9func_names1234567_0Cs123_12client_crate", + "crate_name::func_name::{closure#?}"); +} + +TEST(DemangleRust, UnexpectedlyNamedClosure) { + EXPECT_DEMANGLING( + "_RNCNvCs123_10crate_name9func_name12closure_nameCs456_12client_crate", + "crate_name::func_name::{closure:closure_name#0}"); + EXPECT_DEMANGLING( + "_RNCNvCs123_10crate_name9func_names2_12closure_nameCs456_12client_crate", + "crate_name::func_name::{closure:closure_name#4}"); +} + +TEST(DemangleRust, ItemNestedInsideClosure) { + EXPECT_DEMANGLING( + "_RNvNCNvCs123_10crate_name9func_name015inner_func_nameCs_12client_crate", + "crate_name::func_name::{closure#0}::inner_func_name"); +} + +TEST(DemangleRust, Shim) { + EXPECT_DEMANGLING( + "_RNSNvCs123_10crate_name9func_name6vtableCs456_12client_crate", + "crate_name::func_name::{shim:vtable#0}"); +} + +TEST(DemangleRust, UnknownUppercaseNamespace) { + EXPECT_DEMANGLING( + "_RNXNvCs123_10crate_name9func_name14mystery_objectCs456_12client_crate", + "crate_name::func_name::{X:mystery_object#0}"); +} + +TEST(DemangleRust, NestedUppercaseNamespaces) { + EXPECT_DEMANGLING( + "_RNCNXNYCs123_10crate_names0_1ys1_1xs2_0Cs456_12client_crate", + "crate_name::{Y:y#2}::{X:x#3}::{closure#4}"); +} + + +} // namespace +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl -- cgit v1.2.3 From 692d9e568301b2c01a35b8da3c9915cef4055a4f Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Mon, 13 May 2024 09:05:25 -0700 Subject: Demangle Rust's Y production for passably simple s. PiperOrigin-RevId: 633229582 Change-Id: Ibda908d261b52b1af766304992edd77a5bdd56b5 --- absl/debugging/internal/demangle_rust.cc | 145 +++++++++++++++++++++++++- absl/debugging/internal/demangle_rust.h | 6 +- absl/debugging/internal/demangle_rust_test.cc | 84 +++++++++++++++ 3 files changed, 229 insertions(+), 6 deletions(-) (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust.cc b/absl/debugging/internal/demangle_rust.cc index 7086cab2..f2a411d3 100644 --- a/absl/debugging/internal/demangle_rust.cc +++ b/absl/debugging/internal/demangle_rust.cc @@ -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) { diff --git a/absl/debugging/internal/demangle_rust.h b/absl/debugging/internal/demangle_rust.h index 8e9060b4..1c19815f 100644 --- a/absl/debugging/internal/demangle_rust.h +++ b/absl/debugging/internal/demangle_rust.h @@ -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); diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc index 2841576e..d42700fb 100644 --- a/absl/debugging/internal/demangle_rust_test.cc +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -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", + "::f"); +} + +TEST(DemangleRust, BasicTypeNames) { + EXPECT_DEMANGLING("_RNvYaNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYbNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYcNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYdNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYeNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYfNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYhNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYiNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYjNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYlNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYmNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYnNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYoNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYpNvC1c1t1f", "<_ as c::t>::f"); + EXPECT_DEMANGLING("_RNvYsNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYtNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYuNvC1c1t1f", "<() as c::t>::f"); + EXPECT_DEMANGLING("_RNvYvNvC1c1t1f", "<... as c::t>::f"); + EXPECT_DEMANGLING("_RNvYxNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYyNvC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYzNvC1c1t1f", "::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 -- cgit v1.2.3 From de8ae8715e38766e1564e2d2c738fa9c694200bd Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Tue, 14 May 2024 09:15:34 -0700 Subject: Use Nt for struct and trait names in Rust demangler test inputs. The tests work either way, but this way the inputs are more realistic. PiperOrigin-RevId: 633606944 Change-Id: I6c0440f2fceb5b8d9d4aed5818c3171d3fce2fb8 --- absl/debugging/internal/demangle_rust_test.cc | 86 +++++++++++++-------------- 1 file changed, 43 insertions(+), 43 deletions(-) (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc index d42700fb..a8ef60d6 100644 --- a/absl/debugging/internal/demangle_rust_test.cc +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -211,86 +211,86 @@ TEST(DemangleRust, NestedUppercaseNamespaces) { TEST(DemangleRust, TraitDefinition) { EXPECT_DEMANGLING( - "_RNvYNvC7crate_a9my_structNvC7crate_b8my_trait1f", + "_RNvYNtC7crate_a9my_structNtC7crate_b8my_trait1f", "::f"); } TEST(DemangleRust, BasicTypeNames) { - EXPECT_DEMANGLING("_RNvYaNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYbNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYcNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYdNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYeNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYfNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYhNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYiNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYjNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYlNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYmNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYnNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYoNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYpNvC1c1t1f", "<_ as c::t>::f"); - EXPECT_DEMANGLING("_RNvYsNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYtNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYuNvC1c1t1f", "<() as c::t>::f"); - EXPECT_DEMANGLING("_RNvYvNvC1c1t1f", "<... as c::t>::f"); - EXPECT_DEMANGLING("_RNvYxNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYyNvC1c1t1f", "::f"); - EXPECT_DEMANGLING("_RNvYzNvC1c1t1f", "::f"); - - EXPECT_DEMANGLING_FAILS("_RNvYkNvC1c1t1f"); + EXPECT_DEMANGLING("_RNvYaNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYbNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYcNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYdNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYeNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYfNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYhNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYiNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYjNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYlNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYmNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYnNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYoNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYpNtC1c1t1f", "<_ as c::t>::f"); + EXPECT_DEMANGLING("_RNvYsNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYtNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYuNtC1c1t1f", "<() as c::t>::f"); + EXPECT_DEMANGLING("_RNvYvNtC1c1t1f", "<... as c::t>::f"); + EXPECT_DEMANGLING("_RNvYxNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYyNtC1c1t1f", "::f"); + EXPECT_DEMANGLING("_RNvYzNtC1c1t1f", "::f"); + + EXPECT_DEMANGLING_FAILS("_RNvYkNtC1c1t1f"); } TEST(DemangleRust, SliceTypes) { - EXPECT_DEMANGLING("_RNvYSlNvC1c1t1f", "<[i32] as c::t>::f"); - EXPECT_DEMANGLING("_RNvYSNvC1d1sNvC1c1t1f", "<[d::s] as c::t>::f"); + EXPECT_DEMANGLING("_RNvYSlNtC1c1t1f", "<[i32] as c::t>::f"); + EXPECT_DEMANGLING("_RNvYSNtC1d1sNtC1c1t1f", "<[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"); + EXPECT_DEMANGLING("_RNvYRlNtC1c1t1f", "<&i32 as c::t>::f"); + EXPECT_DEMANGLING("_RNvYRNtC1d1sNtC1c1t1f", "<&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"); + EXPECT_DEMANGLING("_RNvYQlNtC1c1t1f", "<&mut i32 as c::t>::f"); + EXPECT_DEMANGLING("_RNvYQNtC1d1sNtC1c1t1f", "<&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"); + EXPECT_DEMANGLING("_RNvYPlNtC1c1t1f", "<*const i32 as c::t>::f"); + EXPECT_DEMANGLING("_RNvYPNtC1d1sNtC1c1t1f", "<*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"); + EXPECT_DEMANGLING("_RNvYOlNtC1c1t1f", "<*mut i32 as c::t>::f"); + EXPECT_DEMANGLING("_RNvYONtC1d1sNtC1c1t1f", "<*mut d::s as c::t>::f"); } TEST(DemangleRust, TupleLength0) { - EXPECT_DEMANGLING("_RNvYTENvC1c1t1f", "<() as c::t>::f"); + EXPECT_DEMANGLING("_RNvYTENtC1c1t1f", "<() as c::t>::f"); } TEST(DemangleRust, TupleLength1) { - EXPECT_DEMANGLING("_RNvYTlENvC1c1t1f", "<(i32,) as c::t>::f"); - EXPECT_DEMANGLING("_RNvYTNvC1d1sENvC1c1t1f", "<(d::s,) as c::t>::f"); + EXPECT_DEMANGLING("_RNvYTlENtC1c1t1f", "<(i32,) as c::t>::f"); + EXPECT_DEMANGLING("_RNvYTNtC1d1sENtC1c1t1f", "<(d::s,) as c::t>::f"); } TEST(DemangleRust, TupleLength2) { - EXPECT_DEMANGLING("_RNvYTlmENvC1c1t1f", "<(i32, u32) as c::t>::f"); - EXPECT_DEMANGLING("_RNvYTNvC1d1xNvC1e1yENvC1c1t1f", + EXPECT_DEMANGLING("_RNvYTlmENtC1c1t1f", "<(i32, u32) as c::t>::f"); + EXPECT_DEMANGLING("_RNvYTNtC1d1xNtC1e1yENtC1c1t1f", "<(d::x, e::y) as c::t>::f"); } TEST(DemangleRust, TupleLength3) { - EXPECT_DEMANGLING("_RNvYTlmnENvC1c1t1f", "<(i32, u32, i128) as c::t>::f"); - EXPECT_DEMANGLING("_RNvYTNvC1d1xNvC1e1yNvC1f1zENvC1c1t1f", + EXPECT_DEMANGLING("_RNvYTlmnENtC1c1t1f", "<(i32, u32, i128) as c::t>::f"); + EXPECT_DEMANGLING("_RNvYTNtC1d1xNtC1e1yNtC1f1zENtC1c1t1f", "<(d::x, e::y, f::z) as c::t>::f"); } TEST(DemangleRust, LongerTuplesAbbreviated) { - EXPECT_DEMANGLING("_RNvYTlmnoENvC1c1t1f", + EXPECT_DEMANGLING("_RNvYTlmnoENtC1c1t1f", "<(i32, u32, i128, ...) as c::t>::f"); - EXPECT_DEMANGLING("_RNvYTlmnNvC1d1xNvC1e1yENvC1c1t1f", + EXPECT_DEMANGLING("_RNvYTlmnNtC1d1xNtC1e1yENtC1c1t1f", "<(i32, u32, i128, ...) as c::t>::f"); } -- cgit v1.2.3 From eba8db7baf6c326870f28e58977075b7b2fb243d Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Tue, 14 May 2024 16:16:24 -0700 Subject: Demangle Rust backrefs. PiperOrigin-RevId: 633738511 Change-Id: I3f895d5de1aec5b5b9666523a328f3a3b0344e59 --- absl/debugging/internal/demangle_rust.cc | 105 ++++++++++++++++++++++++-- absl/debugging/internal/demangle_rust_test.cc | 66 ++++++++++++++++ 2 files changed, 165 insertions(+), 6 deletions(-) (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust.cc b/absl/debugging/internal/demangle_rust.cc index f2a411d3..36f54161 100644 --- a/absl/debugging/internal/demangle_rust.cc +++ b/absl/debugging/internal/demangle_rust.cc @@ -154,7 +154,7 @@ class RustSymbolParser { 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 + case 'B': goto path_backref; default: return false; } @@ -239,7 +239,7 @@ class RustSymbolParser { } 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 + if (Eat('B')) goto type_backref; goto path; // tuple-type -> T type* E (T already consumed) @@ -288,6 +288,32 @@ class RustSymbolParser { } --silence_depth_; continue; + + // backref -> B base-62-number (B already consumed) + // + // The BeginBackref call parses and range-checks the base-62-number. We + // always do that much. + // + // The recursive call parses and prints what the backref points at. We + // save CPU and stack by skipping this work if the output would be + // suppressed anyway. + path_backref: + if (!BeginBackref()) return false; + if (silence_depth_ == 0) { + ABSL_DEMANGLER_RECURSE(path, kPathBackrefEnding); + } + EndBackref(); + continue; + + // This represents the same backref production as in path_backref but + // parses the target as a type instead of a path. + type_backref: + if (!BeginBackref()) return false; + if (silence_depth_ == 0) { + ABSL_DEMANGLER_RECURSE(type, kTypeBackrefEnding); + } + EndBackref(); + continue; } } @@ -308,12 +334,22 @@ class RustSymbolParser { kAfterSecondTupleElement, kAfterThirdTupleElement, kAfterSubsequentTupleElement, + kPathBackrefEnding, + kTypeBackrefEnding, }; - // Element count for the stack_ array. A larger kStackSize accommodates more + // Element counts for the stack arrays. Larger stack sizes accommodate more // deeply nested names at the cost of a larger footprint on the C++ call // stack. - enum { kStackSize = 256 }; + enum { + // Maximum (recursive calls + N nested-names) open at one time. + kStackSize = 256, + + // Maximum number of nested backrefs. We can keep this stack pretty small + // because we do not follow backrefs inside generic-args or other contexts + // that suppress printing, so deep stacking is unlikely in practice. + kPositionStackSize = 16, + }; // Returns the next input character without consuming it. char Peek() const { return encoding_[pos_]; } @@ -536,6 +572,58 @@ class RustSymbolParser { // is not empty (data_stack_pointer_ < kStackSize). std::uint8_t PopByte() { return stack_[data_stack_pointer_++]; } + // Pushes position onto the position stack and returns true if the stack is + // not full, else returns false. + ABSL_MUST_USE_RESULT bool PushPosition(int position) { + if (position_depth_ == kPositionStackSize) return false; + position_stack_[position_depth_++] = position; + return true; + } + + // Pops the last pushed input position. Requires that the position stack is + // not empty (position_depth_ > 0). + int PopPosition() { return position_stack_[--position_depth_]; } + + // Consumes a base-62-number denoting a backref target, pushes the current + // input position on the data stack, and sets the input position to the + // beginning of the backref target. Returns true on success. Returns false + // if parsing failed, the stack is exhausted, or the backref target position + // is out of range. + ABSL_MUST_USE_RESULT bool BeginBackref() { + // backref = B base-62-number (B already consumed) + // + // Reject backrefs that don't parse, overflow int, or don't point backward. + // If the offset looks fine, adjust it to account for the _R prefix. + int offset = 0; + const int offset_of_this_backref = + pos_ - 2 /* _R */ - 1 /* B already consumed */; + if (!ParseBase62Number(offset) || offset < 0 || + offset >= offset_of_this_backref) { + return false; + } + offset += 2; + + // Save the old position to restore later. + if (!PushPosition(pos_)) return false; + + // Move the input position to the backref target. + // + // Note that we do not check whether the new position points to the + // beginning of a construct matching the context in which the backref + // appeared. We just jump to it and see whether nested parsing succeeds. + // We therefore accept various wrong manglings, e.g., a type backref + // pointing to an 'l' character inside an identifier, which happens to mean + // i32 when parsed as a type mangling. This saves the complexity and RAM + // footprint of remembering which offsets began which kinds of + // substructures. Existing demanglers take similar shortcuts. + pos_ = offset; + return true; + } + + // Cleans up after a backref production by restoring the previous input + // position from the data stack. + void EndBackref() { pos_ = PopPosition(); } + // Call and data stacks reside in stack_. The leftmost depth_ elements // contain ReturnAddresses pushed by ABSL_DEMANGLER_RECURSE. The elements // from index data_stack_pointer_ to the right edge of stack_ contain bytes @@ -544,14 +632,19 @@ class RustSymbolParser { int data_stack_pointer_ = kStackSize; int depth_ = 0; + // The leftmost position_depth_ elements of position_stack_ contain the input + // positions to return to after fully printing the targets of backrefs. + int position_stack_[kPositionStackSize] = {}; + int position_depth_ = 0; + // Anything parsed while silence_depth_ > 0 contributes nothing to the // demangled output. For constructs omitted from the demangling, such as // impl-path and the contents of generic-args, we will increment // silence_depth_ on the way in and decrement silence_depth_ on the way out. int silence_depth_ = 0; - // Input: encoding_ points just after the _R in a Rust mangled symbol, and - // encoding_[pos_] is the next input character to be scanned. + // Input: encoding_ points to a Rust mangled symbol, and encoding_[pos_] is + // the next input character to be scanned. int pos_ = 0; const char* encoding_ = nullptr; diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc index a8ef60d6..c931304d 100644 --- a/absl/debugging/internal/demangle_rust_test.cc +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -294,6 +294,72 @@ TEST(DemangleRust, LongerTuplesAbbreviated) { "<(i32, u32, i128, ...) as c::t>::f"); } +TEST(DemangleRust, PathBackrefToCrate) { + EXPECT_DEMANGLING("_RNvYNtC8my_crate9my_structNtB4_8my_trait1f", + "::f"); +} + +TEST(DemangleRust, PathBackrefToNestedPath) { + EXPECT_DEMANGLING("_RNvYNtNtC1c1m1sNtB4_1t1f", "::f"); +} + +TEST(DemangleRust, PathBackrefAsInstantiatingCrate) { + EXPECT_DEMANGLING("_RNCNvC8my_crate7my_func0B3_", + "my_crate::my_func::{closure#0}"); +} + +TEST(DemangleRust, TypeBackrefsNestedInTuple) { + EXPECT_DEMANGLING("_RNvYTTRlB4_ERB3_ENtC1c1t1f", + "<((&i32, &i32), &(&i32, &i32)) as c::t>::f"); +} + +TEST(DemangleRust, NoInfiniteLoopOnBackrefToTheWhole) { + EXPECT_DEMANGLING_FAILS("_RB_"); + EXPECT_DEMANGLING_FAILS("_RNvB_1sNtC1c1t1f"); +} + +TEST(DemangleRust, NoCrashOnForwardBackref) { + EXPECT_DEMANGLING_FAILS("_RB0_"); + EXPECT_DEMANGLING_FAILS("_RB1_"); + EXPECT_DEMANGLING_FAILS("_RB2_"); + EXPECT_DEMANGLING_FAILS("_RB3_"); + EXPECT_DEMANGLING_FAILS("_RB4_"); +} + +TEST(DemangleRust, PathBackrefsDoNotRecurseDuringSilence) { + // B_ points at the value f (the whole mangling), so the cycle would lead to + // parse failure if the parser tried to parse what was pointed to. + EXPECT_DEMANGLING("_RNvYTlmnNtB_1sENtC1c1t1f", + "<(i32, u32, i128, ...) as c::t>::f"); +} + +TEST(DemangleRust, TypeBackrefsDoNotRecurseDuringSilence) { + // B2_ points at the tuple type, likewise making a cycle that the parser + // avoids following. + EXPECT_DEMANGLING("_RNvYTlmnB2_ENtC1c1t1f", + "<(i32, u32, i128, ...) as c::t>::f"); +} + +TEST(DemangleRust, ReturnFromBackrefToInputPosition256) { + // Show that we can resume at input positions that don't fit into a byte. + EXPECT_DEMANGLING("_RNvYNtC1c238very_long_type_" + "ABCDEFGHIJabcdefghijABCDEFGHIJabcdefghij" + "ABCDEFGHIJabcdefghijABCDEFGHIJabcdefghij" + "ABCDEFGHIJabcdefghijABCDEFGHIJabcdefghij" + "ABCDEFGHIJabcdefghijABCDEFGHIJabcdefghij" + "ABCDEFGHIJabcdefghijABCDEFGHIJabcdefghij" + "ABCDEFGHIJabcdefghijABC" + "NtB4_1t1f", + "::f"); +} + } // namespace } // namespace debugging_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From bfbfc3c7ececefcadf74c9a7db345ba30189348b Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Tue, 21 May 2024 10:48:36 -0700 Subject: Recognize generic arguments containing only types in Rust mangled names. We follow the C++ demangler in not printing the arguments, just an empty <>. PiperOrigin-RevId: 635858791 Change-Id: I24903d4c5a1e2060e92ca950bf7a61647aee655f --- absl/debugging/internal/demangle_rust.cc | 24 ++++++++++++++++- absl/debugging/internal/demangle_rust_test.cc | 39 +++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust.cc b/absl/debugging/internal/demangle_rust.cc index 8fbde02a..fd205924 100644 --- a/absl/debugging/internal/demangle_rust.cc +++ b/absl/debugging/internal/demangle_rust.cc @@ -154,7 +154,7 @@ class RustSymbolParser { case 'X': return false; // trait-impl not yet implemented case 'Y': goto trait_definition; case 'N': goto nested_path; - case 'I': return false; // generic-args not yet implemented + case 'I': goto generic_args; case 'B': goto path_backref; default: return false; } @@ -290,6 +290,26 @@ class RustSymbolParser { --silence_depth_; continue; + // generic-args -> I path generic-arg* E (I already consumed) + // + // We follow the C++ demangler in omitting all the arguments from the + // output, printing only the list opening and closing tokens. + generic_args: + ABSL_DEMANGLER_RECURSE(path, kBeginGenericArgList); + if (!Emit("::<>")) return false; + ++silence_depth_; + while (!Eat('E')) { + ABSL_DEMANGLER_RECURSE(generic_arg, kContinueGenericArgList); + } + --silence_depth_; + continue; + + // generic-arg -> lifetime | type | K const + generic_arg: + if (Eat('L')) return false; // lifetime not yet implemented + if (Eat('K')) return false; // const not yet implemented + goto type; + // backref -> B base-62-number (B already consumed) // // The BeginBackref call parses and range-checks the base-62-number. We @@ -335,6 +355,8 @@ class RustSymbolParser { kAfterSecondTupleElement, kAfterThirdTupleElement, kAfterSubsequentTupleElement, + kBeginGenericArgList, + kContinueGenericArgList, kPathBackrefEnding, kTypeBackrefEnding, }; diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc index c931304d..1525b522 100644 --- a/absl/debugging/internal/demangle_rust_test.cc +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -360,6 +360,45 @@ TEST(DemangleRust, ReturnFromBackrefToInputPosition256) { " as c::t>::f"); } +TEST(DemangleRust, EmptyGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fE", "c::f::<>"); +} + +TEST(DemangleRust, OneSimpleTypeInGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1flE", // c::f:: + "c::f::<>"); +} + +TEST(DemangleRust, OneTupleInGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fTlmEE", // c::f::<(i32, u32)> + "c::f::<>"); +} + +TEST(DemangleRust, OnePathInGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fNtC1d1sE", // c::f:: + "c::f::<>"); +} + +TEST(DemangleRust, LongerGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1flmRNtC1d1sE", // c::f:: + "c::f::<>"); +} + +TEST(DemangleRust, BackrefInGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fRlB7_NtB2_1sE", // c::f::<&i32, &i32, c::s> + "c::f::<>"); +} + +TEST(DemangleRust, NestedGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fINtB2_1slEmE", // c::f::, u32> + "c::f::<>"); +} + +TEST(DemangleRust, MonomorphicEntityNestedInsideGeneric) { + EXPECT_DEMANGLING("_RNvINvC1c1fppE1g", // c::f::<_, _>::g + "c::f::<>::g"); +} + } // namespace } // namespace debugging_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From 7a730c1b22702b931431c84fe8e42b9c4b0051a0 Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Tue, 21 May 2024 15:00:10 -0700 Subject: Recognize const and array-type in Rust mangled names. PiperOrigin-RevId: 635938476 Change-Id: I49cba8789452f13cfeb4a24203b3b46624aa7269 --- absl/debugging/internal/demangle_rust.cc | 67 ++++++++++++++++++++++++++- absl/debugging/internal/demangle_rust_test.cc | 54 +++++++++++++++++++++ 2 files changed, 119 insertions(+), 2 deletions(-) (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust.cc b/absl/debugging/internal/demangle_rust.cc index fd205924..20b812a4 100644 --- a/absl/debugging/internal/demangle_rust.cc +++ b/absl/debugging/internal/demangle_rust.cc @@ -36,6 +36,7 @@ bool IsLower(char c) { return 'a' <= c && c <= 'z'; } 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 == '_'; } +bool IsLowerHexDigit(char c) { return IsDigit(c) || ('a' <= c && c <= 'f'); } const char* BasicTypeName(char c) { switch (c) { @@ -212,7 +213,15 @@ class RustSymbolParser { if (type_name == nullptr || !Emit(type_name)) return false; continue; } - if (Eat('A')) return false; // array-type not yet implemented + if (Eat('A')) { + // array-type = A type const + if (!Emit("[")) return false; + ABSL_DEMANGLER_RECURSE(type, kArraySize); + if (!Emit("; ")) return false; + ABSL_DEMANGLER_RECURSE(constant, kFinishArray); + if (!Emit("]")) return false; + continue; + } if (Eat('S')) { if (!Emit("[")) return false; ABSL_DEMANGLER_RECURSE(type, kSliceEnding); @@ -290,6 +299,48 @@ class RustSymbolParser { --silence_depth_; continue; + // const -> type const-data | p | backref + // + // const is a C++ keyword, so we use the label `constant` instead. + constant: + if (Eat('B')) goto const_backref; + if (Eat('p')) { + if (!Emit("_")) return false; + continue; + } + + // Scan the type without printing it. + // + // The Rust language restricts the type of a const generic argument + // much more than the mangling grammar does. We do not enforce this. + // + // We also do not bother printing false, true, 'A', and '\u{abcd}' for + // the types bool and char. Because we do not print generic-args + // contents, we expect to print constants only in array sizes, and + // those should not be bool or char. + ++silence_depth_; + ABSL_DEMANGLER_RECURSE(type, kConstData); + --silence_depth_; + + // const-data -> n? hex-digit* _ + // + // Although the grammar doesn't say this, existing demanglers expect + // that zero is 0, not an empty digit sequence, and no nonzero value + // may have leading zero digits. Also n0_ is accepted and printed as + // -0, though a toolchain will probably never write that encoding. + if (Eat('n') && !EmitChar('-')) return false; + if (!Emit("0x")) return false; + if (Eat('0')) { + if (!EmitChar('0')) return false; + if (!Eat('_')) return false; + continue; + } + while (IsLowerHexDigit(Peek())) { + if (!EmitChar(Take())) return false; + } + if (!Eat('_')) return false; + continue; + // generic-args -> I path generic-arg* E (I already consumed) // // We follow the C++ demangler in omitting all the arguments from the @@ -307,7 +358,7 @@ class RustSymbolParser { // generic-arg -> lifetime | type | K const generic_arg: if (Eat('L')) return false; // lifetime not yet implemented - if (Eat('K')) return false; // const not yet implemented + if (Eat('K')) goto constant; goto type; // backref -> B base-62-number (B already consumed) @@ -335,6 +386,14 @@ class RustSymbolParser { } EndBackref(); continue; + + const_backref: + if (!BeginBackref()) return false; + if (silence_depth_ == 0) { + ABSL_DEMANGLER_RECURSE(constant, kConstantBackrefEnding); + } + EndBackref(); + continue; } } @@ -350,15 +409,19 @@ class RustSymbolParser { kIdentifierInLowercaseNamespace, kTraitDefinitionInfix, kTraitDefinitionEnding, + kArraySize, + kFinishArray, kSliceEnding, kAfterFirstTupleElement, kAfterSecondTupleElement, kAfterThirdTupleElement, kAfterSubsequentTupleElement, + kConstData, kBeginGenericArgList, kContinueGenericArgList, kPathBackrefEnding, kTypeBackrefEnding, + kConstantBackrefEnding, }; // Element counts for the stack arrays. Larger stack sizes accommodate more diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc index 1525b522..5e53eae3 100644 --- a/absl/debugging/internal/demangle_rust_test.cc +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -340,6 +340,11 @@ TEST(DemangleRust, TypeBackrefsDoNotRecurseDuringSilence) { "<(i32, u32, i128, ...) as c::t>::f"); } +TEST(DemangleRust, ConstBackrefsDoNotRecurseDuringSilence) { + // B_ points at the whole I...E mangling, which does not parse as a const. + EXPECT_DEMANGLING("_RINvC1c1fAlB_E", "c::f::<>"); +} + TEST(DemangleRust, ReturnFromBackrefToInputPosition256) { // Show that we can resume at input positions that don't fit into a byte. EXPECT_DEMANGLING("_RNvYNtC1c238very_long_type_" @@ -399,6 +404,55 @@ TEST(DemangleRust, MonomorphicEntityNestedInsideGeneric) { "c::f::<>::g"); } +TEST(DemangleRust, ArrayTypeWithSimpleElementType) { + EXPECT_DEMANGLING("_RNvYAlj1f_NtC1c1t1f", "<[i32; 0x1f] as c::t>::f"); +} + +TEST(DemangleRust, ArrayTypeWithComplexElementType) { + EXPECT_DEMANGLING("_RNvYAINtC1c1slEj1f_NtB6_1t1f", + "<[c::s::<>; 0x1f] as c::t>::f"); +} + +TEST(DemangleRust, NestedArrayType) { + EXPECT_DEMANGLING("_RNvYAAlj1f_j2e_NtC1c1t1f", + "<[[i32; 0x1f]; 0x2e] as c::t>::f"); +} + +TEST(DemangleRust, BackrefArraySize) { + EXPECT_DEMANGLING("_RNvYAAlj1f_B5_NtC1c1t1f", + "<[[i32; 0x1f]; 0x1f] as c::t>::f"); +} + +TEST(DemangleRust, ZeroArraySize) { + EXPECT_DEMANGLING("_RNvYAlj0_NtC1c1t1f", "<[i32; 0x0] as c::t>::f"); +} + +TEST(DemangleRust, SurprisingMinusesInArraySize) { + // Compilers shouldn't do this stuff, but existing demanglers accept it. + EXPECT_DEMANGLING("_RNvYAljn0_NtC1c1t1f", "<[i32; -0x0] as c::t>::f"); + EXPECT_DEMANGLING("_RNvYAljn42_NtC1c1t1f", "<[i32; -0x42] as c::t>::f"); +} + +TEST(DemangleRust, NumberAsGenericArg) { + EXPECT_DEMANGLING("_RINvC1c1fKl8_E", // c::f::<0x8> + "c::f::<>"); +} + +TEST(DemangleRust, NumberAsFirstOfTwoGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fKl8_mE", // c::f::<0x8, u32> + "c::f::<>"); +} + +TEST(DemangleRust, NumberAsSecondOfTwoGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fmKl8_E", // c::f:: + "c::f::<>"); +} + +TEST(DemangleRust, NumberPlaceholder) { + EXPECT_DEMANGLING("_RNvINvC1c1fKpE1g", // c::f::<_>::g + "c::f::<>::g"); +} + } // namespace } // namespace debugging_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From a2625a648dc69c5b3d0330f25004454716cacfc8 Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Tue, 21 May 2024 15:54:34 -0700 Subject: Recognize inherent-impl and trait-impl in Rust demangling. PiperOrigin-RevId: 635955480 Change-Id: I9322b4e7732e252007f6ca6c9b0cefc25974c9f8 --- absl/debugging/internal/demangle_rust.cc | 39 +++++++++++++++++++++++++-- absl/debugging/internal/demangle_rust_test.cc | 25 +++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust.cc b/absl/debugging/internal/demangle_rust.cc index 20b812a4..83fec2d9 100644 --- a/absl/debugging/internal/demangle_rust.cc +++ b/absl/debugging/internal/demangle_rust.cc @@ -151,8 +151,8 @@ class RustSymbolParser { 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 'M': goto inherent_impl; + case 'X': goto trait_impl; case 'Y': goto trait_definition; case 'N': goto nested_path; case 'I': goto generic_args; @@ -165,6 +165,35 @@ class RustSymbolParser { if (!ParseIdentifier()) return false; continue; + // inherent-impl -> M impl-path type (M already consumed) + inherent_impl: + if (!Emit("<")) return false; + ABSL_DEMANGLER_RECURSE(impl_path, kInherentImplType); + ABSL_DEMANGLER_RECURSE(type, kInherentImplEnding); + if (!Emit(">")) return false; + continue; + + // trait-impl -> X impl-path type path (X already consumed) + trait_impl: + if (!Emit("<")) return false; + ABSL_DEMANGLER_RECURSE(impl_path, kTraitImplType); + ABSL_DEMANGLER_RECURSE(type, kTraitImplInfix); + if (!Emit(" as ")) return false; + ABSL_DEMANGLER_RECURSE(path, kTraitImplEnding); + if (!Emit(">")) return false; + continue; + + // impl-path -> disambiguator? path (but never print it!) + impl_path: + ++silence_depth_; + { + int ignored_disambiguator; + if (!ParseDisambiguator(ignored_disambiguator)) return false; + } + ABSL_DEMANGLER_RECURSE(path, kImplPathEnding); + --silence_depth_; + continue; + // trait-definition -> Y type path (Y already consumed) trait_definition: if (!Emit("<")) return false; @@ -407,6 +436,12 @@ class RustSymbolParser { kVendorSpecificSuffix, kIdentifierInUppercaseNamespace, kIdentifierInLowercaseNamespace, + kInherentImplType, + kInherentImplEnding, + kTraitImplType, + kTraitImplInfix, + kTraitImplEnding, + kImplPathEnding, kTraitDefinitionInfix, kTraitDefinitionEnding, kArraySize, diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc index 5e53eae3..453d725b 100644 --- a/absl/debugging/internal/demangle_rust_test.cc +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -453,6 +453,31 @@ TEST(DemangleRust, NumberPlaceholder) { "c::f::<>::g"); } +TEST(DemangleRust, InherentImplWithoutDisambiguator) { + EXPECT_DEMANGLING("_RNvMNtC8my_crate6my_modNtB2_9my_struct7my_func", + "::my_func"); +} + +TEST(DemangleRust, InherentImplWithDisambiguator) { + EXPECT_DEMANGLING("_RNvMs_NtC8my_crate6my_modNtB4_9my_struct7my_func", + "::my_func"); +} + +TEST(DemangleRust, TraitImplWithoutDisambiguator) { + EXPECT_DEMANGLING("_RNvXC8my_crateNtB2_9my_structNtB2_8my_trait7my_func", + "::my_func"); +} + +TEST(DemangleRust, TraitImplWithDisambiguator) { + EXPECT_DEMANGLING("_RNvXs_C8my_crateNtB4_9my_structNtB4_8my_trait7my_func", + "::my_func"); +} + +TEST(DemangleRust, TraitImplWithNonpathSelfType) { + EXPECT_DEMANGLING("_RNvXC8my_crateRlNtB2_8my_trait7my_func", + "<&i32 as my_crate::my_trait>::my_func"); +} + } // namespace } // namespace debugging_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From aaed9b4ab49047868285fe2fa0467e24790c4418 Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Wed, 22 May 2024 07:01:19 -0700 Subject: Recognize fn-type and lifetimes in Rust demangling. PiperOrigin-RevId: 636152885 Change-Id: If545903854ea39cc4b5c51c88cd555072d27d89e --- absl/debugging/internal/demangle_rust.cc | 72 +++++++++++++++++++++++++-- absl/debugging/internal/demangle_rust_test.cc | 56 +++++++++++++++++++++ 2 files changed, 124 insertions(+), 4 deletions(-) (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust.cc b/absl/debugging/internal/demangle_rust.cc index 83fec2d9..b8cc1803 100644 --- a/absl/debugging/internal/demangle_rust.cc +++ b/absl/debugging/internal/demangle_rust.cc @@ -260,12 +260,12 @@ class RustSymbolParser { if (Eat('T')) goto tuple_type; if (Eat('R')) { if (!Emit("&")) return false; - if (Eat('L')) return false; // lifetime not yet implemented + if (!ParseOptionalLifetime()) return false; goto type; } if (Eat('Q')) { if (!Emit("&mut ")) return false; - if (Eat('L')) return false; // lifetime not yet implemented + if (!ParseOptionalLifetime()) return false; goto type; } if (Eat('P')) { @@ -276,7 +276,7 @@ class RustSymbolParser { if (!Emit("*mut ")) return false; goto type; } - if (Eat('F')) return false; // fn-type not yet implemented + if (Eat('F')) goto fn_type; if (Eat('D')) return false; // dyn-trait-type not yet implemented if (Eat('B')) goto type_backref; goto path; @@ -328,6 +328,27 @@ class RustSymbolParser { --silence_depth_; continue; + // fn-type -> F fn-sig (F already consumed) + // fn-sig -> binder? U? (K abi)? type* E type + // abi -> C | undisambiguated-identifier + // + // We follow the C++ demangler in suppressing details of function + // signatures. Every function type is rendered "fn...". + fn_type: + if (!Emit("fn...")) return false; + ++silence_depth_; + if (!ParseOptionalBinder()) return false; + (void)Eat('U'); + if (Eat('K')) { + if (!Eat('C') && !ParseUndisambiguatedIdentifier()) return false; + } + while (!Eat('E')) { + ABSL_DEMANGLER_RECURSE(type, kContinueParameterList); + } + ABSL_DEMANGLER_RECURSE(type, kFinishFn); + --silence_depth_; + continue; + // const -> type const-data | p | backref // // const is a C++ keyword, so we use the label `constant` instead. @@ -386,7 +407,10 @@ class RustSymbolParser { // generic-arg -> lifetime | type | K const generic_arg: - if (Eat('L')) return false; // lifetime not yet implemented + if (Peek() == 'L') { + if (!ParseOptionalLifetime()) return false; + continue; + } if (Eat('K')) goto constant; goto type; @@ -451,6 +475,8 @@ class RustSymbolParser { kAfterSecondTupleElement, kAfterThirdTupleElement, kAfterSubsequentTupleElement, + kContinueParameterList, + kFinishFn, kConstData, kBeginGenericArgList, kContinueGenericArgList, @@ -612,6 +638,20 @@ class RustSymbolParser { int disambiguator = 0; if (!ParseDisambiguator(disambiguator)) return false; + return ParseUndisambiguatedIdentifier(uppercase_namespace, disambiguator); + } + + // Consumes from the input an identifier with no preceding disambiguator, + // returning true on success. + // + // When ParseIdentifier calls this, it passes the N character and + // disambiguator value so that "{closure#42}" and similar forms can be + // rendered correctly. + // + // At other appearances of undisambiguated-identifier in the grammar, this + // treatment is not applicable, and the call site omits both arguments. + ABSL_MUST_USE_RESULT bool ParseUndisambiguatedIdentifier( + char uppercase_namespace = '\0', int disambiguator = 0) { // undisambiguated-identifier -> u? decimal-number _? bytes const bool is_punycoded = Eat('u'); if (!IsDigit(Peek())) return false; @@ -686,6 +726,30 @@ class RustSymbolParser { return true; } + // Consumes a binder of higher-ranked lifetimes if one is present. On success + // returns true and discards the encoded lifetime count. On parse failure + // returns false. + ABSL_MUST_USE_RESULT bool ParseOptionalBinder() { + // binder -> G base-62-number + if (!Eat('G')) return true; + int ignored_binding_count; + return ParseBase62Number(ignored_binding_count); + } + + // Consumes a lifetime if one is present. + // + // On success returns true and discards the lifetime index. We do not print + // or even range-check lifetimes because they are a finer detail than other + // things we omit from output, such as the entire contents of generic-args. + // + // On parse failure returns false. + ABSL_MUST_USE_RESULT bool ParseOptionalLifetime() { + // lifetime -> L base-62-number + if (!Eat('L')) return true; + int ignored_de_bruijn_index; + return ParseBase62Number(ignored_de_bruijn_index); + } + // Pushes ns onto the namespace stack and returns true if the stack is not // full, else returns false. ABSL_MUST_USE_RESULT bool PushNamespace(char ns) { diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc index 453d725b..34b78134 100644 --- a/absl/debugging/internal/demangle_rust_test.cc +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -478,6 +478,62 @@ TEST(DemangleRust, TraitImplWithNonpathSelfType) { "<&i32 as my_crate::my_trait>::my_func"); } +TEST(DemangleRust, ThunkType) { + EXPECT_DEMANGLING("_RNvYFEuNtC1c1t1f", // ::f + "::f"); +} + +TEST(DemangleRust, NontrivialFunctionReturnType) { + EXPECT_DEMANGLING( + "_RNvYFERTlmENtC1c1t1f", // &(i32, u32) as c::t>::f + "::f"); +} + +TEST(DemangleRust, OneParameterType) { + EXPECT_DEMANGLING("_RNvYFlEuNtC1c1t1f", // ::f + "::f"); +} + +TEST(DemangleRust, TwoParameterTypes) { + EXPECT_DEMANGLING("_RNvYFlmEuNtC1c1t1f", // ::f + "::f"); +} + +TEST(DemangleRust, ExternC) { + EXPECT_DEMANGLING("_RNvYFKCEuNtC1c1t1f", // >::f + "::f"); +} + +TEST(DemangleRust, ExternOther) { + EXPECT_DEMANGLING( + "_RNvYFK5not_CEuNtC1c1t1f", // ::f + "::f"); +} + +TEST(DemangleRust, Unsafe) { + EXPECT_DEMANGLING("_RNvYFUEuNtC1c1t1f", // ::f + "::f"); +} + +TEST(DemangleRust, Binder) { + EXPECT_DEMANGLING( + // fn(&'a i32) -> &'a i32 as c::t>::f + "_RNvYFG_RL0_lEB5_NtC1c1t1f", + "::f"); +} + +TEST(DemangleRust, AllFnSigFeaturesInOrder) { + EXPECT_DEMANGLING( + // unsafe extern "C" fn(&'a i32) -> &'a i32 as c::t>::f + "_RNvYFG_UKCRL0_lEB8_NtC1c1t1f", + "::f"); +} + +TEST(DemangleRust, LifetimeInGenericArgs) { + EXPECT_DEMANGLING("_RINvC1c1fINtB2_1sL_EE", // c::f::> + "c::f::<>"); +} + } // namespace } // namespace debugging_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From 414929371ba51c475d0512e0354d911bf1d00598 Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Thu, 23 May 2024 08:41:31 -0700 Subject: Recognize dyn-trait-type in Rust demangling. PiperOrigin-RevId: 636563266 Change-Id: Id4ee907c30d7dac400f1f85776cc5f1fcb3e20b7 --- absl/debugging/internal/demangle_rust.cc | 61 ++++++++++++++++++++++++++- absl/debugging/internal/demangle_rust_test.cc | 44 +++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust.cc b/absl/debugging/internal/demangle_rust.cc index b8cc1803..06ee7a4b 100644 --- a/absl/debugging/internal/demangle_rust.cc +++ b/absl/debugging/internal/demangle_rust.cc @@ -277,7 +277,7 @@ class RustSymbolParser { goto type; } if (Eat('F')) goto fn_type; - if (Eat('D')) return false; // dyn-trait-type not yet implemented + if (Eat('D')) goto dyn_trait_type; if (Eat('B')) goto type_backref; goto path; @@ -349,6 +349,54 @@ class RustSymbolParser { --silence_depth_; continue; + // dyn-trait-type -> D dyn-bounds lifetime (D already consumed) + // dyn-bounds -> binder? dyn-trait* E + // + // The grammar strangely allows an empty trait list, even though the + // compiler should never output one. We follow existing demanglers in + // rendering DEL_ as "dyn ". + // + // Because auto traits lengthen a type name considerably without + // providing much value to a search for related source code, it would be + // desirable to abbreviate + // dyn main::Trait + std::marker::Copy + std::marker::Send + // to + // dyn main::Trait + ..., + // eliding the auto traits. But it is difficult to do so correctly, in + // part because there is no guarantee that the mangling will list the + // main trait first. So we just print all the traits in their order of + // appearance in the mangled name. + dyn_trait_type: + if (!Emit("dyn ")) return false; + if (!ParseOptionalBinder()) return false; + if (!Eat('E')) { + ABSL_DEMANGLER_RECURSE(dyn_trait, kBeginAutoTraits); + while (!Eat('E')) { + if (!Emit(" + ")) return false; + ABSL_DEMANGLER_RECURSE(dyn_trait, kContinueAutoTraits); + } + } + if (!ParseRequiredLifetime()) return false; + continue; + + // dyn-trait -> path dyn-trait-assoc-binding* + // dyn-trait-assoc-binding -> p undisambiguated-identifier type + // + // We render nonempty binding lists as <>, omitting their contents as + // for generic-args. + dyn_trait: + ABSL_DEMANGLER_RECURSE(path, kContinueDynTrait); + if (Peek() == 'p') { + if (!Emit("<>")) return false; + ++silence_depth_; + while (Eat('p')) { + if (!ParseUndisambiguatedIdentifier()) return false; + ABSL_DEMANGLER_RECURSE(type, kContinueAssocBinding); + } + --silence_depth_; + } + continue; + // const -> type const-data | p | backref // // const is a C++ keyword, so we use the label `constant` instead. @@ -477,6 +525,10 @@ class RustSymbolParser { kAfterSubsequentTupleElement, kContinueParameterList, kFinishFn, + kBeginAutoTraits, + kContinueAutoTraits, + kContinueDynTrait, + kContinueAssocBinding, kConstData, kBeginGenericArgList, kContinueGenericArgList, @@ -750,6 +802,13 @@ class RustSymbolParser { return ParseBase62Number(ignored_de_bruijn_index); } + // Consumes a lifetime just like ParseOptionalLifetime, but returns false if + // there is no lifetime here. + ABSL_MUST_USE_RESULT bool ParseRequiredLifetime() { + if (Peek() != 'L') return false; + return ParseOptionalLifetime(); + } + // Pushes ns onto the namespace stack and returns true if the stack is not // full, else returns false. ABSL_MUST_USE_RESULT bool PushNamespace(char ns) { diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc index 34b78134..00bba780 100644 --- a/absl/debugging/internal/demangle_rust_test.cc +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -534,6 +534,50 @@ TEST(DemangleRust, LifetimeInGenericArgs) { "c::f::<>"); } +TEST(DemangleRust, EmptyDynTrait) { + // This shouldn't happen, but the grammar allows it and existing demanglers + // accept it. + EXPECT_DEMANGLING("_RNvYDEL_NtC1c1t1f", + "::f"); +} + +TEST(DemangleRust, SimpleDynTrait) { + EXPECT_DEMANGLING("_RNvYDNtC1c1tEL_NtC1d1u1f", + "::f"); +} + +TEST(DemangleRust, DynTraitWithOneAssociatedType) { + EXPECT_DEMANGLING( + "_RNvYDNtC1c1tp1xlEL_NtC1d1u1f", // as d::u>::f + " as d::u>::f"); +} + +TEST(DemangleRust, DynTraitWithTwoAssociatedTypes) { + EXPECT_DEMANGLING( + // as d::u>::f + "_RNvYDNtC1c1tp1xlp1ymEL_NtC1d1u1f", + " as d::u>::f"); +} + +TEST(DemangleRust, DynTraitPlusAutoTrait) { + EXPECT_DEMANGLING( + "_RNvYDNtC1c1tNtNtC3std6marker4SendEL_NtC1d1u1f", + "::f"); +} + +TEST(DemangleRust, DynTraitPlusTwoAutoTraits) { + EXPECT_DEMANGLING( + "_RNvYDNtC1c1tNtNtC3std6marker4CopyNtBc_4SyncEL_NtC1d1u1f", + "::f"); +} + +TEST(DemangleRust, HigherRankedDynTrait) { + EXPECT_DEMANGLING( + // c::t::<&'a i32> as d::u>::f + "_RNvYDG_INtC1c1tRL0_lEEL_NtC1d1u1f", + " as d::u>::f"); +} + } // namespace } // namespace debugging_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From f04e489056d9be93072bb633d9818b1e2add6316 Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Fri, 14 Jun 2024 12:11:34 -0700 Subject: Drop std:: qualification from integer types like uint64_t. PiperOrigin-RevId: 643418422 Change-Id: Ib16cfef8ddedc8366df49ca75ab02eb60af08f26 --- absl/debugging/internal/demangle_rust.cc | 12 +++---- absl/debugging/internal/demangle_rust.h | 2 +- absl/debugging/internal/demangle_rust_test.cc | 10 +++--- absl/debugging/internal/demangle_test.cc | 2 +- .../debugging/internal/utf8_for_code_point_test.cc | 42 +++++++++++----------- 5 files changed, 34 insertions(+), 34 deletions(-) (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust.cc b/absl/debugging/internal/demangle_rust.cc index 06ee7a4b..6b686ab3 100644 --- a/absl/debugging/internal/demangle_rust.cc +++ b/absl/debugging/internal/demangle_rust.cc @@ -503,7 +503,7 @@ class RustSymbolParser { private: // Enumerates resumption points for ABSL_DEMANGLER_RECURSE calls. - enum ReturnAddress : std::uint8_t { + enum ReturnAddress : uint8_t { kInstantiatingCrate, kVendorSpecificSuffix, kIdentifierInUppercaseNamespace, @@ -585,9 +585,9 @@ class RustSymbolParser { // false if not everything fit into the output buffer. ABSL_MUST_USE_RESULT bool Emit(const char* token) { if (silence_depth_ > 0) return true; - const std::size_t token_length = std::strlen(token); - const std::size_t bytes_to_copy = token_length + 1; // token and final NUL - if (static_cast(out_end_ - out_) < bytes_to_copy) return false; + const size_t token_length = std::strlen(token); + const size_t bytes_to_copy = token_length + 1; // token and final NUL + if (static_cast(out_end_ - out_) < bytes_to_copy) return false; std::memcpy(out_, token, bytes_to_copy); out_ += token_length; return true; @@ -604,7 +604,7 @@ class RustSymbolParser { // because 999 > 256. The bound will remain correct even if future // maintenance changes the type of the disambiguator variable. char digits[3 * sizeof(disambiguator)] = {}; - std::size_t leading_digit_index = sizeof(digits) - 1; + size_t leading_digit_index = sizeof(digits) - 1; for (; disambiguator > 0; disambiguator /= 10) { digits[--leading_digit_index] = static_cast('0' + disambiguator % 10); @@ -909,7 +909,7 @@ class RustSymbolParser { } // namespace bool DemangleRustSymbolEncoding(const char* mangled, char* out, - std::size_t out_size) { + size_t out_size) { return RustSymbolParser(mangled, out, out + out_size).Parse(); } diff --git a/absl/debugging/internal/demangle_rust.h b/absl/debugging/internal/demangle_rust.h index 1c19815f..29e08ecd 100644 --- a/absl/debugging/internal/demangle_rust.h +++ b/absl/debugging/internal/demangle_rust.h @@ -36,7 +36,7 @@ namespace debugging_internal { // 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); + size_t out_size); } // namespace debugging_internal ABSL_NAMESPACE_END diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc index 00bba780..e6b68ae3 100644 --- a/absl/debugging/internal/demangle_rust_test.cc +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -29,7 +29,7 @@ namespace { // chars>, buffer_size) returns true and seems not to have overrun its output // buffer, returns the string written by DemangleRustSymbolEncoding; otherwise // returns an error message. -std::string ResultOfDemangling(const char* mangled, std::size_t buffer_size) { +std::string ResultOfDemangling(const char* mangled, size_t buffer_size) { // Fill the buffer with something other than NUL so we test whether Demangle // appends trailing NUL as expected. std::string buffer(buffer_size + 1, '~'); @@ -58,9 +58,9 @@ std::string ResultOfDemangling(const char* mangled, std::size_t buffer_size) { #define EXPECT_DEMANGLING(mangled, plaintext) \ do { \ [] { \ - constexpr std::size_t plenty_of_space = sizeof(plaintext) + 128; \ - constexpr std::size_t just_enough_space = sizeof(plaintext); \ - constexpr std::size_t one_byte_too_few = sizeof(plaintext) - 1; \ + constexpr size_t plenty_of_space = sizeof(plaintext) + 128; \ + constexpr size_t just_enough_space = sizeof(plaintext); \ + constexpr size_t one_byte_too_few = sizeof(plaintext) - 1; \ const char* expected_plaintext = plaintext; \ const char* expected_error = "Failed parse"; \ ASSERT_EQ(ResultOfDemangling(mangled, plenty_of_space), \ @@ -76,7 +76,7 @@ std::string ResultOfDemangling(const char* mangled, std::size_t buffer_size) { // truncation of a real Rust symbol name). #define EXPECT_DEMANGLING_FAILS(mangled) \ do { \ - constexpr std::size_t plenty_of_space = 1024; \ + constexpr size_t plenty_of_space = 1024; \ const char* expected_error = "Failed parse"; \ EXPECT_EQ(ResultOfDemangling(mangled, plenty_of_space), expected_error); \ } while (0) diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc index 55686814..5579221a 100644 --- a/absl/debugging/internal/demangle_test.cc +++ b/absl/debugging/internal/demangle_test.cc @@ -1209,7 +1209,7 @@ TEST(Demangle, BinaryFoldExpressions) { TEST(Demangle, SizeofPacks) { char tmp[80]; - // template struct S {}; + // template struct S {}; // // template auto f(T... p) -> S { return {}; } // template auto f(int, long) -> S<2>; diff --git a/absl/debugging/internal/utf8_for_code_point_test.cc b/absl/debugging/internal/utf8_for_code_point_test.cc index d6817c77..dd0591ae 100644 --- a/absl/debugging/internal/utf8_for_code_point_test.cc +++ b/absl/debugging/internal/utf8_for_code_point_test.cc @@ -25,32 +25,32 @@ namespace debugging_internal { namespace { TEST(Utf8ForCodePointTest, RecognizesTheSmallestCodePoint) { - Utf8ForCodePoint utf8(std::uint64_t{0}); + Utf8ForCodePoint utf8(uint64_t{0}); ASSERT_EQ(utf8.length, 1); EXPECT_EQ(utf8.bytes[0], '\0'); } TEST(Utf8ForCodePointTest, RecognizesAsciiSmallA) { - Utf8ForCodePoint utf8(std::uint64_t{'a'}); + Utf8ForCodePoint utf8(uint64_t{'a'}); ASSERT_EQ(utf8.length, 1); EXPECT_EQ(utf8.bytes[0], 'a'); } TEST(Utf8ForCodePointTest, RecognizesTheLargestOneByteCodePoint) { - Utf8ForCodePoint utf8(std::uint64_t{0x7f}); + Utf8ForCodePoint utf8(uint64_t{0x7f}); ASSERT_EQ(utf8.length, 1); EXPECT_EQ(utf8.bytes[0], '\x7f'); } TEST(Utf8ForCodePointTest, RecognizesTheSmallestTwoByteCodePoint) { - Utf8ForCodePoint utf8(std::uint64_t{0x80}); + Utf8ForCodePoint utf8(uint64_t{0x80}); ASSERT_EQ(utf8.length, 2); EXPECT_EQ(utf8.bytes[0], static_cast(0xc2)); EXPECT_EQ(utf8.bytes[1], static_cast(0x80)); } TEST(Utf8ForCodePointTest, RecognizesSmallNWithTilde) { - Utf8ForCodePoint utf8(std::uint64_t{0xf1}); + Utf8ForCodePoint utf8(uint64_t{0xf1}); ASSERT_EQ(utf8.length, 2); const char* want = "ñ"; EXPECT_EQ(utf8.bytes[0], want[0]); @@ -58,7 +58,7 @@ TEST(Utf8ForCodePointTest, RecognizesSmallNWithTilde) { } TEST(Utf8ForCodePointTest, RecognizesCapitalPi) { - Utf8ForCodePoint utf8(std::uint64_t{0x3a0}); + Utf8ForCodePoint utf8(uint64_t{0x3a0}); ASSERT_EQ(utf8.length, 2); const char* want = "Π"; EXPECT_EQ(utf8.bytes[0], want[0]); @@ -66,14 +66,14 @@ TEST(Utf8ForCodePointTest, RecognizesCapitalPi) { } TEST(Utf8ForCodePointTest, RecognizesTheLargestTwoByteCodePoint) { - Utf8ForCodePoint utf8(std::uint64_t{0x7ff}); + Utf8ForCodePoint utf8(uint64_t{0x7ff}); ASSERT_EQ(utf8.length, 2); EXPECT_EQ(utf8.bytes[0], static_cast(0xdf)); EXPECT_EQ(utf8.bytes[1], static_cast(0xbf)); } TEST(Utf8ForCodePointTest, RecognizesTheSmallestThreeByteCodePoint) { - Utf8ForCodePoint utf8(std::uint64_t{0x800}); + Utf8ForCodePoint utf8(uint64_t{0x800}); ASSERT_EQ(utf8.length, 3); EXPECT_EQ(utf8.bytes[0], static_cast(0xe0)); EXPECT_EQ(utf8.bytes[1], static_cast(0xa0)); @@ -81,7 +81,7 @@ TEST(Utf8ForCodePointTest, RecognizesTheSmallestThreeByteCodePoint) { } TEST(Utf8ForCodePointTest, RecognizesTheChineseCharacterZhong1AsInZhong1Wen2) { - Utf8ForCodePoint utf8(std::uint64_t{0x4e2d}); + Utf8ForCodePoint utf8(uint64_t{0x4e2d}); ASSERT_EQ(utf8.length, 3); const char* want = "中"; EXPECT_EQ(utf8.bytes[0], want[0]); @@ -90,7 +90,7 @@ TEST(Utf8ForCodePointTest, RecognizesTheChineseCharacterZhong1AsInZhong1Wen2) { } TEST(Utf8ForCodePointTest, RecognizesOneBeforeTheSmallestSurrogate) { - Utf8ForCodePoint utf8(std::uint64_t{0xd7ff}); + Utf8ForCodePoint utf8(uint64_t{0xd7ff}); ASSERT_EQ(utf8.length, 3); EXPECT_EQ(utf8.bytes[0], static_cast(0xed)); EXPECT_EQ(utf8.bytes[1], static_cast(0x9f)); @@ -98,17 +98,17 @@ TEST(Utf8ForCodePointTest, RecognizesOneBeforeTheSmallestSurrogate) { } TEST(Utf8ForCodePointTest, RejectsTheSmallestSurrogate) { - Utf8ForCodePoint utf8(std::uint64_t{0xd800}); + Utf8ForCodePoint utf8(uint64_t{0xd800}); EXPECT_EQ(utf8.length, 0); } TEST(Utf8ForCodePointTest, RejectsTheLargestSurrogate) { - Utf8ForCodePoint utf8(std::uint64_t{0xdfff}); + Utf8ForCodePoint utf8(uint64_t{0xdfff}); EXPECT_EQ(utf8.length, 0); } TEST(Utf8ForCodePointTest, RecognizesOnePastTheLargestSurrogate) { - Utf8ForCodePoint utf8(std::uint64_t{0xe000}); + Utf8ForCodePoint utf8(uint64_t{0xe000}); ASSERT_EQ(utf8.length, 3); EXPECT_EQ(utf8.bytes[0], static_cast(0xee)); EXPECT_EQ(utf8.bytes[1], static_cast(0x80)); @@ -116,7 +116,7 @@ TEST(Utf8ForCodePointTest, RecognizesOnePastTheLargestSurrogate) { } TEST(Utf8ForCodePointTest, RecognizesTheLargestThreeByteCodePoint) { - Utf8ForCodePoint utf8(std::uint64_t{0xffff}); + Utf8ForCodePoint utf8(uint64_t{0xffff}); ASSERT_EQ(utf8.length, 3); EXPECT_EQ(utf8.bytes[0], static_cast(0xef)); EXPECT_EQ(utf8.bytes[1], static_cast(0xbf)); @@ -124,7 +124,7 @@ TEST(Utf8ForCodePointTest, RecognizesTheLargestThreeByteCodePoint) { } TEST(Utf8ForCodePointTest, RecognizesTheSmallestFourByteCodePoint) { - Utf8ForCodePoint utf8(std::uint64_t{0x10000}); + Utf8ForCodePoint utf8(uint64_t{0x10000}); ASSERT_EQ(utf8.length, 4); EXPECT_EQ(utf8.bytes[0], static_cast(0xf0)); EXPECT_EQ(utf8.bytes[1], static_cast(0x90)); @@ -133,7 +133,7 @@ TEST(Utf8ForCodePointTest, RecognizesTheSmallestFourByteCodePoint) { } TEST(Utf8ForCodePointTest, RecognizesTheJackOfHearts) { - Utf8ForCodePoint utf8(std::uint64_t{0x1f0bb}); + Utf8ForCodePoint utf8(uint64_t{0x1f0bb}); ASSERT_EQ(utf8.length, 4); const char* want = "🂻"; EXPECT_EQ(utf8.bytes[0], want[0]); @@ -143,7 +143,7 @@ TEST(Utf8ForCodePointTest, RecognizesTheJackOfHearts) { } TEST(Utf8ForCodePointTest, RecognizesTheLargestFourByteCodePoint) { - Utf8ForCodePoint utf8(std::uint64_t{0x10ffff}); + Utf8ForCodePoint utf8(uint64_t{0x10ffff}); ASSERT_EQ(utf8.length, 4); EXPECT_EQ(utf8.bytes[0], static_cast(0xf4)); EXPECT_EQ(utf8.bytes[1], static_cast(0x8f)); @@ -152,21 +152,21 @@ TEST(Utf8ForCodePointTest, RecognizesTheLargestFourByteCodePoint) { } TEST(Utf8ForCodePointTest, RejectsTheSmallestOverlargeCodePoint) { - Utf8ForCodePoint utf8(std::uint64_t{0x110000}); + Utf8ForCodePoint utf8(uint64_t{0x110000}); EXPECT_EQ(utf8.length, 0); } TEST(Utf8ForCodePointTest, RejectsAThroughlyOverlargeCodePoint) { - Utf8ForCodePoint utf8(std::uint64_t{0xffffffff00000000}); + Utf8ForCodePoint utf8(uint64_t{0xffffffff00000000}); EXPECT_EQ(utf8.length, 0); } TEST(Utf8ForCodePointTest, OkReturnsTrueForAValidCodePoint) { - EXPECT_TRUE(Utf8ForCodePoint(std::uint64_t{0}).ok()); + EXPECT_TRUE(Utf8ForCodePoint(uint64_t{0}).ok()); } TEST(Utf8ForCodePointTest, OkReturnsFalseForAnInvalidCodePoint) { - EXPECT_FALSE(Utf8ForCodePoint(std::uint64_t{0xffffffff00000000}).ok()); + EXPECT_FALSE(Utf8ForCodePoint(uint64_t{0xffffffff00000000}).ok()); } } // namespace -- cgit v1.2.3 From 4b9a55fd27a3edefcd12cf4a7873ee8ddcccc521 Mon Sep 17 00:00:00 2001 From: Chris Mihelich Date: Thu, 27 Jun 2024 09:12:55 -0700 Subject: Decode Rust Punycode when it's not too long. PiperOrigin-RevId: 647340145 Change-Id: I4b0076595dbda1f81ffdc32adad2dc1e35cb9e04 --- absl/debugging/internal/demangle_rust.cc | 41 ++++++++++++++++----------- absl/debugging/internal/demangle_rust_test.cc | 2 +- 2 files changed, 25 insertions(+), 18 deletions(-) (limited to 'absl/debugging/internal/demangle_rust_test.cc') diff --git a/absl/debugging/internal/demangle_rust.cc b/absl/debugging/internal/demangle_rust.cc index 6b686ab3..4309bd84 100644 --- a/absl/debugging/internal/demangle_rust.cc +++ b/absl/debugging/internal/demangle_rust.cc @@ -21,6 +21,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" +#include "absl/debugging/internal/decode_rust_punycode.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -710,13 +711,19 @@ class RustSymbolParser { int num_bytes = 0; if (!ParseDecimalNumber(num_bytes)) return false; (void)Eat('_'); // optional separator, needed if a digit follows + if (is_punycoded) { + DecodeRustPunycodeOptions options; + options.punycode_begin = &encoding_[pos_]; + options.punycode_end = &encoding_[pos_] + num_bytes; + options.out_begin = out_; + options.out_end = out_end_; + out_ = DecodeRustPunycode(options); + if (out_ == nullptr) return false; + pos_ += static_cast(num_bytes); + } // 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 { + if (uppercase_namespace != '\0') { switch (uppercase_namespace) { case 'C': if (!Emit("{closure")) return false; @@ -732,24 +739,24 @@ class RustSymbolParser { } // Emit the name itself. - for (int i = 0; i < num_bytes; ++i) { - const char c = Take(); - if (!IsIdentifierChar(c) && - // The spec gives toolchains the choice of Punycode or raw UTF-8 for - // identifiers containing code points above 0x7f, so accept bytes with - // the high bit set if this is not a u... encoding. - (is_punycoded || (c & 0x80) == 0)) { - return false; + if (!is_punycoded) { + for (int i = 0; i < num_bytes; ++i) { + const char c = Take(); + if (!IsIdentifierChar(c) && + // The spec gives toolchains the choice of Punycode or raw UTF-8 for + // identifiers containing code points above 0x7f, so accept bytes + // with the high bit set. + (c & 0x80) == 0) { + return false; + } + if (!EmitChar(c)) return false; } - if (!EmitChar(c)) return false; } - // Emit the endings of braced forms: "#42}" or "}". + // Emit the endings of braced forms, e.g., "#42}". if (uppercase_namespace != '\0') { if (!EmitChar('#')) return false; if (!EmitDisambiguator(disambiguator)) return false; - } - if (uppercase_namespace != '\0' || is_punycoded) { if (!EmitChar('}')) return false; } diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc index e6b68ae3..8ceb1fd7 100644 --- a/absl/debugging/internal/demangle_rust_test.cc +++ b/absl/debugging/internal/demangle_rust_test.cc @@ -117,7 +117,7 @@ TEST(DemangleRust, UnicodeIdentifiers) { EXPECT_DEMANGLING("_RNvC7ice_cap17Eyjafjallajökull", "ice_cap::Eyjafjallajökull"); EXPECT_DEMANGLING("_RNvC7ice_caps_u19Eyjafjallajkull_jtb", - "ice_cap::{Punycode Eyjafjallajkull_jtb}"); + "ice_cap::Eyjafjallajökull"); } TEST(DemangleRust, FunctionInModule) { -- cgit v1.2.3