修改pods
This commit is contained in:
126
Pods/abseil/absl/debugging/internal/bounded_utf8_length_sequence.h
generated
Normal file
126
Pods/abseil/absl/debugging/internal/bounded_utf8_length_sequence.h
generated
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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.
|
||||
|
||||
#ifndef ABSL_DEBUGGING_INTERNAL_BOUNDED_UTF8_LENGTH_SEQUENCE_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_BOUNDED_UTF8_LENGTH_SEQUENCE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/numeric/bits.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace debugging_internal {
|
||||
|
||||
// A sequence of up to max_elements integers between 1 and 4 inclusive, whose
|
||||
// insertion operation computes the sum of all the elements before the insertion
|
||||
// point. This is useful in decoding Punycode, where one needs to know where in
|
||||
// a UTF-8 byte stream the n-th code point begins.
|
||||
//
|
||||
// BoundedUtf8LengthSequence is async-signal-safe and suitable for use in
|
||||
// symbolizing stack traces in a signal handler, provided max_elements is not
|
||||
// improvidently large. For inputs of lengths accepted by the Rust demangler,
|
||||
// up to a couple hundred code points, InsertAndReturnSumOfPredecessors should
|
||||
// run in a few dozen clock cycles, on par with the other arithmetic required
|
||||
// for Punycode decoding.
|
||||
template <uint32_t max_elements>
|
||||
class BoundedUtf8LengthSequence {
|
||||
public:
|
||||
// Constructs an empty sequence.
|
||||
BoundedUtf8LengthSequence() = default;
|
||||
|
||||
// Inserts `utf_length` at position `index`, shifting any existing elements at
|
||||
// or beyond `index` one position to the right. If the sequence is already
|
||||
// full, the rightmost element is discarded.
|
||||
//
|
||||
// Returns the sum of the elements at positions 0 to `index - 1` inclusive.
|
||||
// If `index` is greater than the number of elements already inserted, the
|
||||
// excess positions in the range count 1 apiece.
|
||||
//
|
||||
// REQUIRES: index < max_elements and 1 <= utf8_length <= 4.
|
||||
uint32_t InsertAndReturnSumOfPredecessors(
|
||||
uint32_t index, uint32_t utf8_length) {
|
||||
// The caller shouldn't pass out-of-bounds inputs, but if it does happen,
|
||||
// clamp the values and try to continue. If we're being called from a
|
||||
// signal handler, the last thing we want to do is crash. Emitting
|
||||
// malformed UTF-8 is a lesser evil.
|
||||
if (index >= max_elements) index = max_elements - 1;
|
||||
if (utf8_length == 0 || utf8_length > 4) utf8_length = 1;
|
||||
|
||||
const uint32_t word_index = index/32;
|
||||
const uint32_t bit_index = 2 * (index % 32);
|
||||
const uint64_t ones_bit = uint64_t{1} << bit_index;
|
||||
|
||||
// Compute the sum of predecessors.
|
||||
// - Each value from 1 to 4 is represented by a bit field with value from
|
||||
// 0 to 3, so the desired sum is index plus the sum of the
|
||||
// representations actually stored.
|
||||
// - For each bit field, a set low bit should contribute 1 to the sum, and
|
||||
// a set high bit should contribute 2.
|
||||
// - Another way to say the same thing is that each set bit contributes 1,
|
||||
// and each set high bit contributes an additional 1.
|
||||
// - So the sum we want is index + popcount(everything) + popcount(bits in
|
||||
// odd positions).
|
||||
const uint64_t odd_bits_mask = 0xaaaaaaaaaaaaaaaa;
|
||||
const uint64_t lower_seminibbles_mask = ones_bit - 1;
|
||||
const uint64_t higher_seminibbles_mask = ~lower_seminibbles_mask;
|
||||
const uint64_t same_word_bits_below_insertion =
|
||||
rep_[word_index] & lower_seminibbles_mask;
|
||||
int full_popcount = absl::popcount(same_word_bits_below_insertion);
|
||||
int odd_popcount =
|
||||
absl::popcount(same_word_bits_below_insertion & odd_bits_mask);
|
||||
for (uint32_t j = word_index; j > 0; --j) {
|
||||
const uint64_t word_below_insertion = rep_[j - 1];
|
||||
full_popcount += absl::popcount(word_below_insertion);
|
||||
odd_popcount += absl::popcount(word_below_insertion & odd_bits_mask);
|
||||
}
|
||||
const uint32_t sum_of_predecessors =
|
||||
index + static_cast<uint32_t>(full_popcount + odd_popcount);
|
||||
|
||||
// Now insert utf8_length's representation, shifting successors up one
|
||||
// place.
|
||||
for (uint32_t j = max_elements/32 - 1; j > word_index; --j) {
|
||||
rep_[j] = (rep_[j] << 2) | (rep_[j - 1] >> 62);
|
||||
}
|
||||
rep_[word_index] =
|
||||
(rep_[word_index] & lower_seminibbles_mask) |
|
||||
(uint64_t{utf8_length - 1} << bit_index) |
|
||||
((rep_[word_index] & higher_seminibbles_mask) << 2);
|
||||
|
||||
return sum_of_predecessors;
|
||||
}
|
||||
|
||||
private:
|
||||
// If the (32 * i + j)-th element of the represented sequence has the value k
|
||||
// (0 <= j < 32, 1 <= k <= 4), then bits 2 * j and 2 * j + 1 of rep_[i]
|
||||
// contain the seminibble (k - 1).
|
||||
//
|
||||
// In particular, the zero-initialization of rep_ makes positions not holding
|
||||
// any inserted element count as 1 in InsertAndReturnSumOfPredecessors.
|
||||
//
|
||||
// Example: rep_ = {0xb1, ... the rest zeroes ...} represents the sequence
|
||||
// (2, 1, 4, 3, ... the rest 1's ...). Constructing the sequence of Unicode
|
||||
// code points "Àa🂻中" = {U+00C0, U+0061, U+1F0BB, U+4E2D} (among many
|
||||
// other examples) would yield this value of rep_.
|
||||
static_assert(max_elements > 0 && max_elements % 32 == 0,
|
||||
"max_elements must be a positive multiple of 32");
|
||||
uint64_t rep_[max_elements/32] = {};
|
||||
};
|
||||
|
||||
} // namespace debugging_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_BOUNDED_UTF8_LENGTH_SEQUENCE_H_
|
||||
258
Pods/abseil/absl/debugging/internal/decode_rust_punycode.cc
generated
Normal file
258
Pods/abseil/absl/debugging/internal/decode_rust_punycode.cc
generated
Normal file
@@ -0,0 +1,258 @@
|
||||
// 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/decode_rust_punycode.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/nullability.h"
|
||||
#include "absl/debugging/internal/bounded_utf8_length_sequence.h"
|
||||
#include "absl/debugging/internal/utf8_for_code_point.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace debugging_internal {
|
||||
|
||||
namespace {
|
||||
|
||||
// Decoding Punycode requires repeated random-access insertion into a stream of
|
||||
// variable-length UTF-8 code-point encodings. We need this to be tolerably
|
||||
// fast (no N^2 slowdown for unfortunate inputs), and we can't allocate any data
|
||||
// structures on the heap (async-signal-safety).
|
||||
//
|
||||
// It is pragmatic to impose a moderately low limit on the identifier length and
|
||||
// bail out if we ever hit it. Then BoundedUtf8LengthSequence efficiently
|
||||
// determines where to insert the next code point, and memmove efficiently makes
|
||||
// room for it.
|
||||
//
|
||||
// The chosen limit is a round number several times larger than identifiers
|
||||
// expected in practice, yet still small enough that a memmove of this many
|
||||
// UTF-8 characters is not much more expensive than the division and modulus
|
||||
// operations that Punycode decoding requires.
|
||||
constexpr uint32_t kMaxChars = 256;
|
||||
|
||||
// Constants from RFC 3492 section 5.
|
||||
constexpr uint32_t kBase = 36, kTMin = 1, kTMax = 26, kSkew = 38, kDamp = 700;
|
||||
|
||||
constexpr uint32_t kMaxCodePoint = 0x10ffff;
|
||||
|
||||
// Overflow threshold in DecodeRustPunycode's inner loop; see comments there.
|
||||
constexpr uint32_t kMaxI = 1 << 30;
|
||||
|
||||
// If punycode_begin .. punycode_end begins with a prefix matching the regular
|
||||
// expression [0-9a-zA-Z_]+_, removes that prefix, copies all but the final
|
||||
// underscore into out_begin .. out_end, sets num_ascii_chars to the number of
|
||||
// bytes copied, and returns true. (A prefix of this sort represents the
|
||||
// nonempty subsequence of ASCII characters in the corresponding plaintext.)
|
||||
//
|
||||
// If punycode_begin .. punycode_end does not contain an underscore, sets
|
||||
// num_ascii_chars to zero and returns true. (The encoding of a plaintext
|
||||
// without any ASCII characters does not carry such a prefix.)
|
||||
//
|
||||
// Returns false and zeroes num_ascii_chars on failure (either parse error or
|
||||
// not enough space in the output buffer).
|
||||
bool ConsumeOptionalAsciiPrefix(const char*& punycode_begin,
|
||||
const char* const punycode_end,
|
||||
char* const out_begin,
|
||||
char* const out_end,
|
||||
uint32_t& num_ascii_chars) {
|
||||
num_ascii_chars = 0;
|
||||
|
||||
// Remember the last underscore if any. Also use the same string scan to
|
||||
// reject any ASCII bytes that do not belong in an identifier, including NUL,
|
||||
// as well as non-ASCII bytes, which should have been delta-encoded instead.
|
||||
int last_underscore = -1;
|
||||
for (int i = 0; i < punycode_end - punycode_begin; ++i) {
|
||||
const char c = punycode_begin[i];
|
||||
if (c == '_') {
|
||||
last_underscore = i;
|
||||
continue;
|
||||
}
|
||||
// We write out the meaning of absl::ascii_isalnum rather than call that
|
||||
// function because its documentation does not promise it will remain
|
||||
// async-signal-safe under future development.
|
||||
if ('a' <= c && c <= 'z') continue;
|
||||
if ('A' <= c && c <= 'Z') continue;
|
||||
if ('0' <= c && c <= '9') continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there was no underscore, that means there were no ASCII characters in
|
||||
// the plaintext, so there is no prefix to consume. Our work is done.
|
||||
if (last_underscore < 0) return true;
|
||||
|
||||
// Otherwise there will be an underscore delimiter somewhere. It can't be
|
||||
// initial because then there would be no ASCII characters to its left, and no
|
||||
// delimiter would have been added in that case.
|
||||
if (last_underscore == 0) return false;
|
||||
|
||||
// Any other position is reasonable. Make sure there's room in the buffer.
|
||||
if (last_underscore + 1 > out_end - out_begin) return false;
|
||||
|
||||
// Consume and write out the ASCII characters.
|
||||
num_ascii_chars = static_cast<uint32_t>(last_underscore);
|
||||
std::memcpy(out_begin, punycode_begin, num_ascii_chars);
|
||||
out_begin[num_ascii_chars] = '\0';
|
||||
punycode_begin += num_ascii_chars + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the value of `c` as a base-36 digit according to RFC 3492 section 5,
|
||||
// or -1 if `c` is not such a digit.
|
||||
int DigitValue(char c) {
|
||||
if ('0' <= c && c <= '9') return c - '0' + 26;
|
||||
if ('a' <= c && c <= 'z') return c - 'a';
|
||||
if ('A' <= c && c <= 'Z') return c - 'A';
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Consumes the next delta encoding from punycode_begin .. punycode_end,
|
||||
// updating i accordingly. Returns true on success. Returns false on parse
|
||||
// failure or arithmetic overflow.
|
||||
bool ScanNextDelta(const char*& punycode_begin, const char* const punycode_end,
|
||||
uint32_t bias, uint32_t& i) {
|
||||
uint64_t w = 1; // 64 bits to prevent overflow in w *= kBase - t
|
||||
|
||||
// "for k = base to infinity in steps of base do begin ... end" in RFC 3492
|
||||
// section 6.2. Each loop iteration scans one digit of the delta.
|
||||
for (uint32_t k = kBase; punycode_begin != punycode_end; k += kBase) {
|
||||
const int digit_value = DigitValue(*punycode_begin++);
|
||||
if (digit_value < 0) return false;
|
||||
|
||||
// Compute this in 64-bit arithmetic so we can check for overflow afterward.
|
||||
const uint64_t new_i = i + static_cast<uint64_t>(digit_value) * w;
|
||||
|
||||
// Valid deltas are bounded by (#chars already emitted) * kMaxCodePoint, but
|
||||
// invalid input could encode an arbitrarily large delta. Nip that in the
|
||||
// bud here.
|
||||
static_assert(
|
||||
kMaxI >= kMaxChars * kMaxCodePoint,
|
||||
"kMaxI is too small to prevent spurious failures on good input");
|
||||
if (new_i > kMaxI) return false;
|
||||
|
||||
static_assert(
|
||||
kMaxI < (uint64_t{1} << 32),
|
||||
"Make kMaxI smaller or i 64 bits wide to prevent silent wraparound");
|
||||
i = static_cast<uint32_t>(new_i);
|
||||
|
||||
// Compute the threshold that determines whether this is the last digit and
|
||||
// (if not) what the next digit's place value will be. This logic from RFC
|
||||
// 3492 section 6.2 is explained in section 3.3.
|
||||
uint32_t t;
|
||||
if (k <= bias + kTMin) {
|
||||
t = kTMin;
|
||||
} else if (k >= bias + kTMax) {
|
||||
t = kTMax;
|
||||
} else {
|
||||
t = k - bias;
|
||||
}
|
||||
if (static_cast<uint32_t>(digit_value) < t) return true;
|
||||
|
||||
// If this gets too large, the range check on new_i in the next iteration
|
||||
// will catch it. We know this multiplication will not overwrap because w
|
||||
// is 64 bits wide.
|
||||
w *= kBase - t;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
absl::Nullable<char*> DecodeRustPunycode(DecodeRustPunycodeOptions options) {
|
||||
const char* punycode_begin = options.punycode_begin;
|
||||
const char* const punycode_end = options.punycode_end;
|
||||
char* const out_begin = options.out_begin;
|
||||
char* const out_end = options.out_end;
|
||||
|
||||
// Write a NUL terminator first. Later memcpy calls will keep bumping it
|
||||
// along to its new right place.
|
||||
const size_t out_size = static_cast<size_t>(out_end - out_begin);
|
||||
if (out_size == 0) return nullptr;
|
||||
*out_begin = '\0';
|
||||
|
||||
// RFC 3492 section 6.2 begins here. We retain the names of integer variables
|
||||
// appearing in that text.
|
||||
uint32_t n = 128, i = 0, bias = 72, num_chars = 0;
|
||||
|
||||
// If there are any ASCII characters, consume them and their trailing
|
||||
// underscore delimiter.
|
||||
if (!ConsumeOptionalAsciiPrefix(punycode_begin, punycode_end,
|
||||
out_begin, out_end, num_chars)) {
|
||||
return nullptr;
|
||||
}
|
||||
uint32_t total_utf8_bytes = num_chars;
|
||||
|
||||
BoundedUtf8LengthSequence<kMaxChars> utf8_lengths;
|
||||
|
||||
// "while the input is not exhausted do begin ... end"
|
||||
while (punycode_begin != punycode_end) {
|
||||
if (num_chars >= kMaxChars) return nullptr;
|
||||
|
||||
const uint32_t old_i = i;
|
||||
|
||||
if (!ScanNextDelta(punycode_begin, punycode_end, bias, i)) return nullptr;
|
||||
|
||||
// Update bias as in RFC 3492 section 6.1. (We have inlined adapt.)
|
||||
uint32_t delta = i - old_i;
|
||||
delta /= (old_i == 0 ? kDamp : 2);
|
||||
delta += delta/(num_chars + 1);
|
||||
bias = 0;
|
||||
while (delta > ((kBase - kTMin) * kTMax)/2) {
|
||||
delta /= kBase - kTMin;
|
||||
bias += kBase;
|
||||
}
|
||||
bias += ((kBase - kTMin + 1) * delta)/(delta + kSkew);
|
||||
|
||||
// Back in section 6.2, compute the new code point and insertion index.
|
||||
static_assert(
|
||||
kMaxI + kMaxCodePoint < (uint64_t{1} << 32),
|
||||
"Make kMaxI smaller or n 64 bits wide to prevent silent wraparound");
|
||||
n += i/(num_chars + 1);
|
||||
i %= num_chars + 1;
|
||||
|
||||
// To actually insert, we need to convert the code point n to UTF-8 and the
|
||||
// character index i to an index into the byte stream emitted so far. First
|
||||
// prepare the UTF-8 encoding for n, rejecting surrogates, overlarge values,
|
||||
// and anything that won't fit into the remaining output storage.
|
||||
Utf8ForCodePoint utf8_for_code_point(n);
|
||||
if (!utf8_for_code_point.ok()) return nullptr;
|
||||
if (total_utf8_bytes + utf8_for_code_point.length + 1 > out_size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Now insert the new character into both our length map and the output.
|
||||
uint32_t n_index =
|
||||
utf8_lengths.InsertAndReturnSumOfPredecessors(
|
||||
i, utf8_for_code_point.length);
|
||||
std::memmove(
|
||||
out_begin + n_index + utf8_for_code_point.length, out_begin + n_index,
|
||||
total_utf8_bytes + 1 - n_index);
|
||||
std::memcpy(out_begin + n_index, utf8_for_code_point.bytes,
|
||||
utf8_for_code_point.length);
|
||||
total_utf8_bytes += utf8_for_code_point.length;
|
||||
++num_chars;
|
||||
|
||||
// Finally, advance to the next state before continuing.
|
||||
++i;
|
||||
}
|
||||
|
||||
return out_begin + total_utf8_bytes;
|
||||
}
|
||||
|
||||
} // namespace debugging_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
55
Pods/abseil/absl/debugging/internal/decode_rust_punycode.h
generated
Normal file
55
Pods/abseil/absl/debugging/internal/decode_rust_punycode.h
generated
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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.
|
||||
|
||||
#ifndef ABSL_DEBUGGING_INTERNAL_DECODE_RUST_PUNYCODE_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_DECODE_RUST_PUNYCODE_H_
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/nullability.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace debugging_internal {
|
||||
|
||||
struct DecodeRustPunycodeOptions {
|
||||
const char* punycode_begin;
|
||||
const char* punycode_end;
|
||||
char* out_begin;
|
||||
char* out_end;
|
||||
};
|
||||
|
||||
// Given Rust Punycode in `punycode_begin .. punycode_end`, writes the
|
||||
// corresponding UTF-8 plaintext into `out_begin .. out_end`, followed by a NUL
|
||||
// character, and returns a pointer to that final NUL on success. On failure
|
||||
// returns a null pointer, and the contents of `out_begin .. out_end` are
|
||||
// unspecified.
|
||||
//
|
||||
// Failure occurs in precisely these cases:
|
||||
// - Any input byte does not match [0-9a-zA-Z_].
|
||||
// - The first input byte is an underscore, but no other underscore appears in
|
||||
// the input.
|
||||
// - The delta sequence does not represent a valid sequence of code-point
|
||||
// insertions.
|
||||
// - The plaintext would contain more than 256 code points.
|
||||
//
|
||||
// DecodeRustPunycode is async-signal-safe with bounded runtime and a small
|
||||
// stack footprint, making it suitable for use in demangling Rust symbol names
|
||||
// from a signal handler.
|
||||
absl::Nullable<char*> DecodeRustPunycode(DecodeRustPunycodeOptions options);
|
||||
|
||||
} // namespace debugging_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_DECODE_RUST_PUNYCODE_H_
|
||||
1169
Pods/abseil/absl/debugging/internal/demangle.cc
generated
1169
Pods/abseil/absl/debugging/internal/demangle.cc
generated
@@ -14,18 +14,19 @@
|
||||
|
||||
// For reference check out:
|
||||
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
|
||||
//
|
||||
// Note that we only have partial C++11 support yet.
|
||||
|
||||
#include "absl/debugging/internal/demangle.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/debugging/internal/demangle_rust.h"
|
||||
|
||||
#if ABSL_INTERNAL_HAS_CXA_DEMANGLE
|
||||
#include <cxxabi.h>
|
||||
@@ -44,14 +45,16 @@ typedef struct {
|
||||
|
||||
// List of operators from Itanium C++ ABI.
|
||||
static const AbbrevPair kOperatorList[] = {
|
||||
// New has special syntax (not currently supported).
|
||||
// New has special syntax.
|
||||
{"nw", "new", 0},
|
||||
{"na", "new[]", 0},
|
||||
|
||||
// Works except that the 'gs' prefix is not supported.
|
||||
// Special-cased elsewhere to support the optional gs prefix.
|
||||
{"dl", "delete", 1},
|
||||
{"da", "delete[]", 1},
|
||||
|
||||
{"aw", "co_await", 1},
|
||||
|
||||
{"ps", "+", 1}, // "positive"
|
||||
{"ng", "-", 1}, // "negative"
|
||||
{"ad", "&", 1}, // "address-of"
|
||||
@@ -79,6 +82,7 @@ static const AbbrevPair kOperatorList[] = {
|
||||
{"rs", ">>", 2},
|
||||
{"lS", "<<=", 2},
|
||||
{"rS", ">>=", 2},
|
||||
{"ss", "<=>", 2},
|
||||
{"eq", "==", 2},
|
||||
{"ne", "!=", 2},
|
||||
{"lt", "<", 2},
|
||||
@@ -98,6 +102,7 @@ static const AbbrevPair kOperatorList[] = {
|
||||
{"qu", "?", 3},
|
||||
{"st", "sizeof", 0}, // Special syntax
|
||||
{"sz", "sizeof", 1}, // Not a real operator name, but used in expressions.
|
||||
{"sZ", "sizeof...", 0}, // Special syntax
|
||||
{nullptr, nullptr, 0},
|
||||
};
|
||||
|
||||
@@ -187,9 +192,50 @@ typedef struct {
|
||||
int recursion_depth; // For stack exhaustion prevention.
|
||||
int steps; // Cap how much work we'll do, regardless of depth.
|
||||
ParseState parse_state; // Backtrackable state copied for most frames.
|
||||
|
||||
// Conditionally compiled support for marking the position of the first
|
||||
// construct Demangle couldn't parse. This preprocessor symbol is intended
|
||||
// for use by Abseil demangler maintainers only; its behavior is not part of
|
||||
// Abseil's public interface.
|
||||
#ifdef ABSL_INTERNAL_DEMANGLE_RECORDS_HIGH_WATER_MARK
|
||||
int high_water_mark; // Input position where parsing failed.
|
||||
bool too_complex; // True if any guard.IsTooComplex() call returned true.
|
||||
#endif
|
||||
} State;
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef ABSL_INTERNAL_DEMANGLE_RECORDS_HIGH_WATER_MARK
|
||||
void UpdateHighWaterMark(State *state) {
|
||||
if (state->high_water_mark < state->parse_state.mangled_idx) {
|
||||
state->high_water_mark = state->parse_state.mangled_idx;
|
||||
}
|
||||
}
|
||||
|
||||
void ReportHighWaterMark(State *state) {
|
||||
// Write out the mangled name with the trouble point marked, provided that the
|
||||
// output buffer is large enough and the mangled name did not hit a complexity
|
||||
// limit (in which case the high water mark wouldn't point out an unparsable
|
||||
// construct, only the point where a budget ran out).
|
||||
const size_t input_length = std::strlen(state->mangled_begin);
|
||||
if (input_length + 6 > static_cast<size_t>(state->out_end_idx) ||
|
||||
state->too_complex) {
|
||||
if (state->out_end_idx > 0) state->out[0] = '\0';
|
||||
return;
|
||||
}
|
||||
const size_t high_water_mark = static_cast<size_t>(state->high_water_mark);
|
||||
std::memcpy(state->out, state->mangled_begin, high_water_mark);
|
||||
std::memcpy(state->out + high_water_mark, "--!--", 5);
|
||||
std::memcpy(state->out + high_water_mark + 5,
|
||||
state->mangled_begin + high_water_mark,
|
||||
input_length - high_water_mark);
|
||||
state->out[input_length + 5] = '\0';
|
||||
}
|
||||
#else
|
||||
void UpdateHighWaterMark(State *) {}
|
||||
void ReportHighWaterMark(State *) {}
|
||||
#endif
|
||||
|
||||
// Prevent deep recursion / stack exhaustion.
|
||||
// Also prevent unbounded handling of complex inputs.
|
||||
class ComplexityGuard {
|
||||
@@ -201,7 +247,7 @@ class ComplexityGuard {
|
||||
~ComplexityGuard() { --state_->recursion_depth; }
|
||||
|
||||
// 256 levels of recursion seems like a reasonable upper limit on depth.
|
||||
// 128 is not enough to demagle synthetic tests from demangle_unittest.txt:
|
||||
// 128 is not enough to demangle synthetic tests from demangle_unittest.txt:
|
||||
// "_ZaaZZZZ..." and "_ZaaZcvZcvZ..."
|
||||
static constexpr int kRecursionDepthLimit = 256;
|
||||
|
||||
@@ -222,8 +268,14 @@ class ComplexityGuard {
|
||||
static constexpr int kParseStepsLimit = 1 << 17;
|
||||
|
||||
bool IsTooComplex() const {
|
||||
return state_->recursion_depth > kRecursionDepthLimit ||
|
||||
state_->steps > kParseStepsLimit;
|
||||
if (state_->recursion_depth > kRecursionDepthLimit ||
|
||||
state_->steps > kParseStepsLimit) {
|
||||
#ifdef ABSL_INTERNAL_DEMANGLE_RECORDS_HIGH_WATER_MARK
|
||||
state_->too_complex = true;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -270,6 +322,10 @@ static void InitState(State* state,
|
||||
state->out_end_idx = static_cast<int>(out_size);
|
||||
state->recursion_depth = 0;
|
||||
state->steps = 0;
|
||||
#ifdef ABSL_INTERNAL_DEMANGLE_RECORDS_HIGH_WATER_MARK
|
||||
state->high_water_mark = 0;
|
||||
state->too_complex = false;
|
||||
#endif
|
||||
|
||||
state->parse_state.mangled_idx = 0;
|
||||
state->parse_state.out_cur_idx = 0;
|
||||
@@ -291,13 +347,14 @@ static bool ParseOneCharToken(State *state, const char one_char_token) {
|
||||
if (guard.IsTooComplex()) return false;
|
||||
if (RemainingInput(state)[0] == one_char_token) {
|
||||
++state->parse_state.mangled_idx;
|
||||
UpdateHighWaterMark(state);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true and advances "mangled_cur" if we find "two_char_token"
|
||||
// at "mangled_cur" position. It is assumed that "two_char_token" does
|
||||
// Returns true and advances "mangled_idx" if we find "two_char_token"
|
||||
// at "mangled_idx" position. It is assumed that "two_char_token" does
|
||||
// not contain '\0'.
|
||||
static bool ParseTwoCharToken(State *state, const char *two_char_token) {
|
||||
ComplexityGuard guard(state);
|
||||
@@ -305,11 +362,45 @@ static bool ParseTwoCharToken(State *state, const char *two_char_token) {
|
||||
if (RemainingInput(state)[0] == two_char_token[0] &&
|
||||
RemainingInput(state)[1] == two_char_token[1]) {
|
||||
state->parse_state.mangled_idx += 2;
|
||||
UpdateHighWaterMark(state);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true and advances "mangled_idx" if we find "three_char_token"
|
||||
// at "mangled_idx" position. It is assumed that "three_char_token" does
|
||||
// not contain '\0'.
|
||||
static bool ParseThreeCharToken(State *state, const char *three_char_token) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
if (RemainingInput(state)[0] == three_char_token[0] &&
|
||||
RemainingInput(state)[1] == three_char_token[1] &&
|
||||
RemainingInput(state)[2] == three_char_token[2]) {
|
||||
state->parse_state.mangled_idx += 3;
|
||||
UpdateHighWaterMark(state);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true and advances "mangled_idx" if we find a copy of the
|
||||
// NUL-terminated string "long_token" at "mangled_idx" position.
|
||||
static bool ParseLongToken(State *state, const char *long_token) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
int i = 0;
|
||||
for (; long_token[i] != '\0'; ++i) {
|
||||
// Note that we cannot run off the end of the NUL-terminated input here.
|
||||
// Inside the loop body, long_token[i] is known to be different from NUL.
|
||||
// So if we read the NUL on the end of the input here, we return at once.
|
||||
if (RemainingInput(state)[i] != long_token[i]) return false;
|
||||
}
|
||||
state->parse_state.mangled_idx += i;
|
||||
UpdateHighWaterMark(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true and advances "mangled_cur" if we find any character in
|
||||
// "char_class" at "mangled_cur" position.
|
||||
static bool ParseCharClass(State *state, const char *char_class) {
|
||||
@@ -322,6 +413,7 @@ static bool ParseCharClass(State *state, const char *char_class) {
|
||||
for (; *p != '\0'; ++p) {
|
||||
if (RemainingInput(state)[0] == *p) {
|
||||
++state->parse_state.mangled_idx;
|
||||
UpdateHighWaterMark(state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -554,6 +646,7 @@ static bool ParseFloatNumber(State *state);
|
||||
static bool ParseSeqId(State *state);
|
||||
static bool ParseIdentifier(State *state, size_t length);
|
||||
static bool ParseOperatorName(State *state, int *arity);
|
||||
static bool ParseConversionOperatorType(State *state);
|
||||
static bool ParseSpecialName(State *state);
|
||||
static bool ParseCallOffset(State *state);
|
||||
static bool ParseNVOffset(State *state);
|
||||
@@ -563,21 +656,33 @@ static bool ParseCtorDtorName(State *state);
|
||||
static bool ParseDecltype(State *state);
|
||||
static bool ParseType(State *state);
|
||||
static bool ParseCVQualifiers(State *state);
|
||||
static bool ParseExtendedQualifier(State *state);
|
||||
static bool ParseBuiltinType(State *state);
|
||||
static bool ParseVendorExtendedType(State *state);
|
||||
static bool ParseFunctionType(State *state);
|
||||
static bool ParseBareFunctionType(State *state);
|
||||
static bool ParseOverloadAttribute(State *state);
|
||||
static bool ParseClassEnumType(State *state);
|
||||
static bool ParseArrayType(State *state);
|
||||
static bool ParsePointerToMemberType(State *state);
|
||||
static bool ParseTemplateParam(State *state);
|
||||
static bool ParseTemplateParamDecl(State *state);
|
||||
static bool ParseTemplateTemplateParam(State *state);
|
||||
static bool ParseTemplateArgs(State *state);
|
||||
static bool ParseTemplateArg(State *state);
|
||||
static bool ParseBaseUnresolvedName(State *state);
|
||||
static bool ParseUnresolvedName(State *state);
|
||||
static bool ParseUnresolvedQualifierLevel(State *state);
|
||||
static bool ParseUnionSelector(State* state);
|
||||
static bool ParseFunctionParam(State* state);
|
||||
static bool ParseBracedExpression(State *state);
|
||||
static bool ParseExpression(State *state);
|
||||
static bool ParseInitializer(State *state);
|
||||
static bool ParseExprPrimary(State *state);
|
||||
static bool ParseExprCastValue(State *state);
|
||||
static bool ParseExprCastValueAndTrailingE(State *state);
|
||||
static bool ParseQRequiresClauseExpr(State *state);
|
||||
static bool ParseRequirement(State *state);
|
||||
static bool ParseTypeConstraint(State *state);
|
||||
static bool ParseLocalName(State *state);
|
||||
static bool ParseLocalNameSuffix(State *state);
|
||||
static bool ParseDiscriminator(State *state);
|
||||
@@ -622,22 +727,34 @@ static bool ParseMangledName(State *state) {
|
||||
}
|
||||
|
||||
// <encoding> ::= <(function) name> <bare-function-type>
|
||||
// [`Q` <requires-clause expr>]
|
||||
// ::= <(data) name>
|
||||
// ::= <special-name>
|
||||
//
|
||||
// NOTE: Based on http://shortn/_Hoq9qG83rx
|
||||
static bool ParseEncoding(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
// Implementing the first two productions together as <name>
|
||||
// [<bare-function-type>] avoids exponential blowup of backtracking.
|
||||
// Since the first two productions both start with <name>, attempt
|
||||
// to parse it only once to avoid exponential blowup of backtracking.
|
||||
//
|
||||
// Since Optional(...) can't fail, there's no need to copy the state for
|
||||
// backtracking.
|
||||
if (ParseName(state) && Optional(ParseBareFunctionType(state))) {
|
||||
// We're careful about exponential blowup because <encoding> recursively
|
||||
// appears in other productions downstream of its first two productions,
|
||||
// which means that every call to `ParseName` would possibly indirectly
|
||||
// result in two calls to `ParseName` etc.
|
||||
if (ParseName(state)) {
|
||||
if (!ParseBareFunctionType(state)) {
|
||||
return true; // <(data) name>
|
||||
}
|
||||
|
||||
// Parsed: <(function) name> <bare-function-type>
|
||||
// Pending: [`Q` <requires-clause expr>]
|
||||
ParseQRequiresClauseExpr(state); // restores state on failure
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ParseSpecialName(state)) {
|
||||
return true;
|
||||
return true; // <special-name>
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -723,19 +840,26 @@ static bool ParseNestedName(State *state) {
|
||||
// <prefix> ::= <prefix> <unqualified-name>
|
||||
// ::= <template-prefix> <template-args>
|
||||
// ::= <template-param>
|
||||
// ::= <decltype>
|
||||
// ::= <substitution>
|
||||
// ::= # empty
|
||||
// <template-prefix> ::= <prefix> <(template) unqualified-name>
|
||||
// ::= <template-param>
|
||||
// ::= <substitution>
|
||||
// ::= <vendor-extended-type>
|
||||
static bool ParsePrefix(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
bool has_something = false;
|
||||
while (true) {
|
||||
MaybeAppendSeparator(state);
|
||||
if (ParseTemplateParam(state) ||
|
||||
if (ParseTemplateParam(state) || ParseDecltype(state) ||
|
||||
ParseSubstitution(state, /*accept_std=*/true) ||
|
||||
// Although the official grammar does not mention it, nested-names
|
||||
// shaped like Nu14__some_builtinIiE6memberE occur in practice, and it
|
||||
// is not clear what else a compiler is supposed to do when a
|
||||
// vendor-extended type has named members.
|
||||
ParseVendorExtendedType(state) ||
|
||||
ParseUnscopedName(state) ||
|
||||
(ParseOneCharToken(state, 'M') && ParseUnnamedTypeName(state))) {
|
||||
has_something = true;
|
||||
@@ -757,8 +881,14 @@ static bool ParsePrefix(State *state) {
|
||||
// ::= <source-name> [<abi-tags>]
|
||||
// ::= <local-source-name> [<abi-tags>]
|
||||
// ::= <unnamed-type-name> [<abi-tags>]
|
||||
// ::= DC <source-name>+ E # C++17 structured binding
|
||||
// ::= F <source-name> # C++20 constrained friend
|
||||
// ::= F <operator-name> # C++20 constrained friend
|
||||
//
|
||||
// <local-source-name> is a GCC extension; see below.
|
||||
//
|
||||
// For the F notation for constrained friends, see
|
||||
// https://github.com/itanium-cxx-abi/cxx-abi/issues/24#issuecomment-1491130332.
|
||||
static bool ParseUnqualifiedName(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
@@ -767,6 +897,23 @@ static bool ParseUnqualifiedName(State *state) {
|
||||
ParseUnnamedTypeName(state)) {
|
||||
return ParseAbiTags(state);
|
||||
}
|
||||
|
||||
// DC <source-name>+ E
|
||||
ParseState copy = state->parse_state;
|
||||
if (ParseTwoCharToken(state, "DC") && OneOrMore(ParseSourceName, state) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// F <source-name>
|
||||
// F <operator-name>
|
||||
if (ParseOneCharToken(state, 'F') && MaybeAppend(state, "friend ") &&
|
||||
(ParseSourceName(state) || ParseOperatorName(state, nullptr))) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -824,7 +971,11 @@ static bool ParseLocalSourceName(State *state) {
|
||||
// <unnamed-type-name> ::= Ut [<(nonnegative) number>] _
|
||||
// ::= <closure-type-name>
|
||||
// <closure-type-name> ::= Ul <lambda-sig> E [<(nonnegative) number>] _
|
||||
// <lambda-sig> ::= <(parameter) type>+
|
||||
// <lambda-sig> ::= <template-param-decl>* <(parameter) type>+
|
||||
//
|
||||
// For <template-param-decl>* in <lambda-sig> see:
|
||||
//
|
||||
// https://github.com/itanium-cxx-abi/cxx-abi/issues/31
|
||||
static bool ParseUnnamedTypeName(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
@@ -847,6 +998,7 @@ static bool ParseUnnamedTypeName(State *state) {
|
||||
// Closure type.
|
||||
which = -1;
|
||||
if (ParseTwoCharToken(state, "Ul") && DisableAppend(state) &&
|
||||
ZeroOrMore(ParseTemplateParamDecl, state) &&
|
||||
OneOrMore(ParseType, state) && RestoreAppend(state, copy.append) &&
|
||||
ParseOneCharToken(state, 'E') && Optional(ParseNumber(state, &which)) &&
|
||||
which <= std::numeric_limits<int>::max() - 2 && // Don't overflow.
|
||||
@@ -888,6 +1040,7 @@ static bool ParseNumber(State *state, int *number_out) {
|
||||
}
|
||||
if (p != RemainingInput(state)) { // Conversion succeeded.
|
||||
state->parse_state.mangled_idx += p - RemainingInput(state);
|
||||
UpdateHighWaterMark(state);
|
||||
if (number_out != nullptr) {
|
||||
// Note: possibly truncate "number".
|
||||
*number_out = static_cast<int>(number);
|
||||
@@ -910,6 +1063,7 @@ static bool ParseFloatNumber(State *state) {
|
||||
}
|
||||
if (p != RemainingInput(state)) { // Conversion succeeded.
|
||||
state->parse_state.mangled_idx += p - RemainingInput(state);
|
||||
UpdateHighWaterMark(state);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -928,6 +1082,7 @@ static bool ParseSeqId(State *state) {
|
||||
}
|
||||
if (p != RemainingInput(state)) { // Conversion succeeded.
|
||||
state->parse_state.mangled_idx += p - RemainingInput(state);
|
||||
UpdateHighWaterMark(state);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -946,11 +1101,13 @@ static bool ParseIdentifier(State *state, size_t length) {
|
||||
MaybeAppendWithLength(state, RemainingInput(state), length);
|
||||
}
|
||||
state->parse_state.mangled_idx += length;
|
||||
UpdateHighWaterMark(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
// <operator-name> ::= nw, and other two letters cases
|
||||
// ::= cv <type> # (cast)
|
||||
// ::= li <source-name> # C++11 user-defined literal
|
||||
// ::= v <digit> <source-name> # vendor extended operator
|
||||
static bool ParseOperatorName(State *state, int *arity) {
|
||||
ComplexityGuard guard(state);
|
||||
@@ -961,7 +1118,7 @@ static bool ParseOperatorName(State *state, int *arity) {
|
||||
// First check with "cv" (cast) case.
|
||||
ParseState copy = state->parse_state;
|
||||
if (ParseTwoCharToken(state, "cv") && MaybeAppend(state, "operator ") &&
|
||||
EnterNestedName(state) && ParseType(state) &&
|
||||
EnterNestedName(state) && ParseConversionOperatorType(state) &&
|
||||
LeaveNestedName(state, copy.nest_level)) {
|
||||
if (arity != nullptr) {
|
||||
*arity = 1;
|
||||
@@ -970,6 +1127,13 @@ static bool ParseOperatorName(State *state, int *arity) {
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// Then user-defined literals.
|
||||
if (ParseTwoCharToken(state, "li") && MaybeAppend(state, "operator\"\" ") &&
|
||||
ParseSourceName(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// Then vendor extended operators.
|
||||
if (ParseOneCharToken(state, 'v') && ParseDigit(state, arity) &&
|
||||
ParseSourceName(state)) {
|
||||
@@ -997,36 +1161,120 @@ static bool ParseOperatorName(State *state, int *arity) {
|
||||
}
|
||||
MaybeAppend(state, p->real_name);
|
||||
state->parse_state.mangled_idx += 2;
|
||||
UpdateHighWaterMark(state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// <operator-name> ::= cv <type> # (cast)
|
||||
//
|
||||
// The name of a conversion operator is the one place where cv-qualifiers, *, &,
|
||||
// and other simple type combinators are expected to appear in our stripped-down
|
||||
// demangling (elsewhere they appear in function signatures or template
|
||||
// arguments, which we omit from the output). We make reasonable efforts to
|
||||
// render simple cases accurately.
|
||||
static bool ParseConversionOperatorType(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
|
||||
// Scan pointers, const, and other easy mangling prefixes with postfix
|
||||
// demanglings. Remember the range of input for later rescanning.
|
||||
//
|
||||
// See `ParseType` and the `switch` below for the meaning of each char.
|
||||
const char* begin_simple_prefixes = RemainingInput(state);
|
||||
while (ParseCharClass(state, "OPRCGrVK")) {}
|
||||
const char* end_simple_prefixes = RemainingInput(state);
|
||||
|
||||
// Emit the base type first.
|
||||
if (!ParseType(state)) {
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Then rescan the easy type combinators in reverse order to emit their
|
||||
// demanglings in the expected output order.
|
||||
while (begin_simple_prefixes != end_simple_prefixes) {
|
||||
switch (*--end_simple_prefixes) {
|
||||
case 'P':
|
||||
MaybeAppend(state, "*");
|
||||
break;
|
||||
case 'R':
|
||||
MaybeAppend(state, "&");
|
||||
break;
|
||||
case 'O':
|
||||
MaybeAppend(state, "&&");
|
||||
break;
|
||||
case 'C':
|
||||
MaybeAppend(state, " _Complex");
|
||||
break;
|
||||
case 'G':
|
||||
MaybeAppend(state, " _Imaginary");
|
||||
break;
|
||||
case 'r':
|
||||
MaybeAppend(state, " restrict");
|
||||
break;
|
||||
case 'V':
|
||||
MaybeAppend(state, " volatile");
|
||||
break;
|
||||
case 'K':
|
||||
MaybeAppend(state, " const");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// <special-name> ::= TV <type>
|
||||
// ::= TT <type>
|
||||
// ::= TI <type>
|
||||
// ::= TS <type>
|
||||
// ::= TH <type> # thread-local
|
||||
// ::= TW <name> # thread-local wrapper
|
||||
// ::= TH <name> # thread-local initialization
|
||||
// ::= Tc <call-offset> <call-offset> <(base) encoding>
|
||||
// ::= GV <(object) name>
|
||||
// ::= GR <(object) name> [<seq-id>] _
|
||||
// ::= T <call-offset> <(base) encoding>
|
||||
// ::= GTt <encoding> # transaction-safe entry point
|
||||
// ::= TA <template-arg> # nontype template parameter object
|
||||
// G++ extensions:
|
||||
// ::= TC <type> <(offset) number> _ <(base) type>
|
||||
// ::= TF <type>
|
||||
// ::= TJ <type>
|
||||
// ::= GR <name>
|
||||
// ::= GR <name> # without final _, perhaps an earlier form?
|
||||
// ::= GA <encoding>
|
||||
// ::= Th <call-offset> <(base) encoding>
|
||||
// ::= Tv <call-offset> <(base) encoding>
|
||||
//
|
||||
// Note: we don't care much about them since they don't appear in
|
||||
// stack traces. The are special data.
|
||||
// Note: Most of these are special data, not functions that occur in stack
|
||||
// traces. Exceptions are TW and TH, which denote functions supporting the
|
||||
// thread_local feature. For these see:
|
||||
//
|
||||
// https://maskray.me/blog/2021-02-14-all-about-thread-local-storage
|
||||
//
|
||||
// For TA see https://github.com/itanium-cxx-abi/cxx-abi/issues/63.
|
||||
static bool ParseSpecialName(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTISH") &&
|
||||
|
||||
if (ParseTwoCharToken(state, "TW")) {
|
||||
MaybeAppend(state, "thread-local wrapper routine for ");
|
||||
if (ParseName(state)) return true;
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ParseTwoCharToken(state, "TH")) {
|
||||
MaybeAppend(state, "thread-local initialization routine for ");
|
||||
if (ParseName(state)) return true;
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTIS") &&
|
||||
ParseType(state)) {
|
||||
return true;
|
||||
}
|
||||
@@ -1064,12 +1312,30 @@ static bool ParseSpecialName(State *state) {
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "GR") && ParseName(state)) {
|
||||
// <special-name> ::= GR <(object) name> [<seq-id>] _ # modern standard
|
||||
// ::= GR <(object) name> # also recognized
|
||||
if (ParseTwoCharToken(state, "GR")) {
|
||||
MaybeAppend(state, "reference temporary for ");
|
||||
if (!ParseName(state)) {
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
const bool has_seq_id = ParseSeqId(state);
|
||||
const bool has_underscore = ParseOneCharToken(state, '_');
|
||||
if (has_seq_id && !has_underscore) {
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) {
|
||||
if (ParseThreeCharToken(state, "GTt") &&
|
||||
MaybeAppend(state, "transaction clone for ") && ParseEncoding(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
@@ -1079,6 +1345,18 @@ static bool ParseSpecialName(State *state) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "TA")) {
|
||||
bool append = state->parse_state.append;
|
||||
DisableAppend(state);
|
||||
if (ParseTemplateArg(state)) {
|
||||
RestoreAppend(state, append);
|
||||
MaybeAppend(state, "template parameter object");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1182,7 +1460,6 @@ static bool ParseDecltype(State *state) {
|
||||
// ::= O <type> # rvalue reference-to (C++0x)
|
||||
// ::= C <type> # complex pair (C 2000)
|
||||
// ::= G <type> # imaginary (C 2000)
|
||||
// ::= U <source-name> <type> # vendor extended type qualifier
|
||||
// ::= <builtin-type>
|
||||
// ::= <function-type>
|
||||
// ::= <class-enum-type> # note: just an alias for <name>
|
||||
@@ -1193,7 +1470,9 @@ static bool ParseDecltype(State *state) {
|
||||
// ::= <decltype>
|
||||
// ::= <substitution>
|
||||
// ::= Dp <type> # pack expansion of (C++0x)
|
||||
// ::= Dv <num-elems> _ # GNU vector extension
|
||||
// ::= Dv <(elements) number> _ <type> # GNU vector extension
|
||||
// ::= Dv <(bytes) expression> _ <type>
|
||||
// ::= Dk <type-constraint> # constrained auto
|
||||
//
|
||||
static bool ParseType(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
@@ -1236,12 +1515,6 @@ static bool ParseType(State *state) {
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseOneCharToken(state, 'U') && ParseSourceName(state) &&
|
||||
ParseType(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseBuiltinType(state) || ParseFunctionType(state) ||
|
||||
ParseClassEnumType(state) || ParseArrayType(state) ||
|
||||
ParsePointerToMemberType(state) || ParseDecltype(state) ||
|
||||
@@ -1260,54 +1533,160 @@ static bool ParseType(State *state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// GNU vector extension Dv <number> _ <type>
|
||||
if (ParseTwoCharToken(state, "Dv") && ParseNumber(state, nullptr) &&
|
||||
ParseOneCharToken(state, '_')) {
|
||||
ParseOneCharToken(state, '_') && ParseType(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
return false;
|
||||
// GNU vector extension Dv <expression> _ <type>
|
||||
if (ParseTwoCharToken(state, "Dv") && ParseExpression(state) &&
|
||||
ParseOneCharToken(state, '_') && ParseType(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "Dk") && ParseTypeConstraint(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// For this notation see CXXNameMangler::mangleType in Clang's source code.
|
||||
// The relevant logic and its comment "not clear how to mangle this!" date
|
||||
// from 2011, so it may be with us awhile.
|
||||
return ParseLongToken(state, "_SUBSTPACK_");
|
||||
}
|
||||
|
||||
// <qualifiers> ::= <extended-qualifier>* <CV-qualifiers>
|
||||
// <CV-qualifiers> ::= [r] [V] [K]
|
||||
//
|
||||
// We don't allow empty <CV-qualifiers> to avoid infinite loop in
|
||||
// ParseType().
|
||||
static bool ParseCVQualifiers(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
int num_cv_qualifiers = 0;
|
||||
while (ParseExtendedQualifier(state)) ++num_cv_qualifiers;
|
||||
num_cv_qualifiers += ParseOneCharToken(state, 'r');
|
||||
num_cv_qualifiers += ParseOneCharToken(state, 'V');
|
||||
num_cv_qualifiers += ParseOneCharToken(state, 'K');
|
||||
return num_cv_qualifiers > 0;
|
||||
}
|
||||
|
||||
// <extended-qualifier> ::= U <source-name> [<template-args>]
|
||||
static bool ParseExtendedQualifier(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
|
||||
if (!ParseOneCharToken(state, 'U')) return false;
|
||||
|
||||
bool append = state->parse_state.append;
|
||||
DisableAppend(state);
|
||||
if (!ParseSourceName(state)) {
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
Optional(ParseTemplateArgs(state));
|
||||
RestoreAppend(state, append);
|
||||
return true;
|
||||
}
|
||||
|
||||
// <builtin-type> ::= v, etc. # single-character builtin types
|
||||
// ::= u <source-name>
|
||||
// ::= <vendor-extended-type>
|
||||
// ::= Dd, etc. # two-character builtin types
|
||||
// ::= DB (<number> | <expression>) _ # _BitInt(N)
|
||||
// ::= DU (<number> | <expression>) _ # unsigned _BitInt(N)
|
||||
// ::= DF <number> _ # _FloatN (N bits)
|
||||
// ::= DF <number> x # _FloatNx
|
||||
// ::= DF16b # std::bfloat16_t
|
||||
//
|
||||
// Not supported:
|
||||
// ::= DF <number> _ # _FloatN (N bits)
|
||||
//
|
||||
// ::= [DS] DA <fixed-point-size>
|
||||
// ::= [DS] DR <fixed-point-size>
|
||||
// because real implementations of N1169 fixed-point are scant.
|
||||
static bool ParseBuiltinType(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
const AbbrevPair *p;
|
||||
for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) {
|
||||
ParseState copy = state->parse_state;
|
||||
|
||||
// DB (<number> | <expression>) _ # _BitInt(N)
|
||||
// DU (<number> | <expression>) _ # unsigned _BitInt(N)
|
||||
if (ParseTwoCharToken(state, "DB") ||
|
||||
(ParseTwoCharToken(state, "DU") && MaybeAppend(state, "unsigned "))) {
|
||||
bool append = state->parse_state.append;
|
||||
DisableAppend(state);
|
||||
int number = -1;
|
||||
if (!ParseNumber(state, &number) && !ParseExpression(state)) {
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
RestoreAppend(state, append);
|
||||
|
||||
if (!ParseOneCharToken(state, '_')) {
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
MaybeAppend(state, "_BitInt(");
|
||||
if (number >= 0) {
|
||||
MaybeAppendDecimal(state, number);
|
||||
} else {
|
||||
MaybeAppend(state, "?"); // the best we can do for dependent sizes
|
||||
}
|
||||
MaybeAppend(state, ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
// DF <number> _ # _FloatN
|
||||
// DF <number> x # _FloatNx
|
||||
// DF16b # std::bfloat16_t
|
||||
if (ParseTwoCharToken(state, "DF")) {
|
||||
if (ParseThreeCharToken(state, "16b")) {
|
||||
MaybeAppend(state, "std::bfloat16_t");
|
||||
return true;
|
||||
}
|
||||
int number = 0;
|
||||
if (!ParseNumber(state, &number)) {
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
MaybeAppend(state, "_Float");
|
||||
MaybeAppendDecimal(state, number);
|
||||
if (ParseOneCharToken(state, 'x')) {
|
||||
MaybeAppend(state, "x");
|
||||
return true;
|
||||
}
|
||||
if (ParseOneCharToken(state, '_')) return true;
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const AbbrevPair *p = kBuiltinTypeList; p->abbrev != nullptr; ++p) {
|
||||
// Guaranteed only 1- or 2-character strings in kBuiltinTypeList.
|
||||
if (p->abbrev[1] == '\0') {
|
||||
if (ParseOneCharToken(state, p->abbrev[0])) {
|
||||
MaybeAppend(state, p->real_name);
|
||||
return true;
|
||||
return true; // ::= v, etc. # single-character builtin types
|
||||
}
|
||||
} else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) {
|
||||
MaybeAppend(state, p->real_name);
|
||||
return true;
|
||||
return true; // ::= Dd, etc. # two-character builtin types
|
||||
}
|
||||
}
|
||||
|
||||
return ParseVendorExtendedType(state);
|
||||
}
|
||||
|
||||
// <vendor-extended-type> ::= u <source-name> [<template-args>]
|
||||
static bool ParseVendorExtendedType(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
|
||||
ParseState copy = state->parse_state;
|
||||
if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) {
|
||||
if (ParseOneCharToken(state, 'u') && ParseSourceName(state) &&
|
||||
Optional(ParseTemplateArgs(state))) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
@@ -1342,28 +1721,44 @@ static bool ParseExceptionSpec(State *state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// <function-type> ::= [exception-spec] F [Y] <bare-function-type> [O] E
|
||||
// <function-type> ::=
|
||||
// [exception-spec] [Dx] F [Y] <bare-function-type> [<ref-qualifier>] E
|
||||
//
|
||||
// <ref-qualifier> ::= R | O
|
||||
static bool ParseFunctionType(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
if (Optional(ParseExceptionSpec(state)) && ParseOneCharToken(state, 'F') &&
|
||||
Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) &&
|
||||
Optional(ParseOneCharToken(state, 'O')) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
Optional(ParseExceptionSpec(state));
|
||||
Optional(ParseTwoCharToken(state, "Dx"));
|
||||
if (!ParseOneCharToken(state, 'F')) {
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
Optional(ParseOneCharToken(state, 'Y'));
|
||||
if (!ParseBareFunctionType(state)) {
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
Optional(ParseCharClass(state, "RO"));
|
||||
if (!ParseOneCharToken(state, 'E')) {
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// <bare-function-type> ::= <(signature) type>+
|
||||
// <bare-function-type> ::= <overload-attribute>* <(signature) type>+
|
||||
//
|
||||
// The <overload-attribute>* prefix is nonstandard; see the comment on
|
||||
// ParseOverloadAttribute.
|
||||
static bool ParseBareFunctionType(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
DisableAppend(state);
|
||||
if (OneOrMore(ParseType, state)) {
|
||||
if (ZeroOrMore(ParseOverloadAttribute, state) &&
|
||||
OneOrMore(ParseType, state)) {
|
||||
RestoreAppend(state, copy.append);
|
||||
MaybeAppend(state, "()");
|
||||
return true;
|
||||
@@ -1372,11 +1767,43 @@ static bool ParseBareFunctionType(State *state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// <overload-attribute> ::= Ua <name>
|
||||
//
|
||||
// The nonstandard <overload-attribute> production is sufficient to accept the
|
||||
// current implementation of __attribute__((enable_if(condition, "message")))
|
||||
// and future attributes of a similar shape. See
|
||||
// https://clang.llvm.org/docs/AttributeReference.html#enable-if and the
|
||||
// definition of CXXNameMangler::mangleFunctionEncodingBareType in Clang's
|
||||
// source code.
|
||||
static bool ParseOverloadAttribute(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
if (ParseTwoCharToken(state, "Ua") && ParseName(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
// <class-enum-type> ::= <name>
|
||||
// ::= Ts <name> # struct Name or class Name
|
||||
// ::= Tu <name> # union Name
|
||||
// ::= Te <name> # enum Name
|
||||
//
|
||||
// See http://shortn/_W3YrltiEd0.
|
||||
static bool ParseClassEnumType(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
return ParseName(state);
|
||||
ParseState copy = state->parse_state;
|
||||
if (Optional(ParseTwoCharToken(state, "Ts") ||
|
||||
ParseTwoCharToken(state, "Tu") ||
|
||||
ParseTwoCharToken(state, "Te")) &&
|
||||
ParseName(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
// <array-type> ::= A <(positive dimension) number> _ <(element) type>
|
||||
@@ -1413,21 +1840,83 @@ static bool ParsePointerToMemberType(State *state) {
|
||||
|
||||
// <template-param> ::= T_
|
||||
// ::= T <parameter-2 non-negative number> _
|
||||
// ::= TL <level-1> __
|
||||
// ::= TL <level-1> _ <parameter-2 non-negative number> _
|
||||
static bool ParseTemplateParam(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
if (ParseTwoCharToken(state, "T_")) {
|
||||
MaybeAppend(state, "?"); // We don't support template substitutions.
|
||||
return true;
|
||||
return true; // ::= T_
|
||||
}
|
||||
|
||||
ParseState copy = state->parse_state;
|
||||
if (ParseOneCharToken(state, 'T') && ParseNumber(state, nullptr) &&
|
||||
ParseOneCharToken(state, '_')) {
|
||||
MaybeAppend(state, "?"); // We don't support template substitutions.
|
||||
return true; // ::= T <parameter-2 non-negative number> _
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "TL") && ParseNumber(state, nullptr)) {
|
||||
if (ParseTwoCharToken(state, "__")) {
|
||||
MaybeAppend(state, "?"); // We don't support template substitutions.
|
||||
return true; // ::= TL <level-1> __
|
||||
}
|
||||
|
||||
if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr) &&
|
||||
ParseOneCharToken(state, '_')) {
|
||||
MaybeAppend(state, "?"); // We don't support template substitutions.
|
||||
return true; // ::= TL <level-1> _ <parameter-2 non-negative number> _
|
||||
}
|
||||
}
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
// <template-param-decl>
|
||||
// ::= Ty # template type parameter
|
||||
// ::= Tk <concept name> [<template-args>] # constrained type parameter
|
||||
// ::= Tn <type> # template non-type parameter
|
||||
// ::= Tt <template-param-decl>* E # template template parameter
|
||||
// ::= Tp <template-param-decl> # template parameter pack
|
||||
//
|
||||
// NOTE: <concept name> is just a <name>: http://shortn/_MqJVyr0fc1
|
||||
// TODO(b/324066279): Implement optional suffix for `Tt`:
|
||||
// [Q <requires-clause expr>]
|
||||
static bool ParseTemplateParamDecl(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
|
||||
if (ParseTwoCharToken(state, "Ty")) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "Tk") && ParseName(state) &&
|
||||
Optional(ParseTemplateArgs(state))) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "Tn") && ParseType(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "Tt") &&
|
||||
ZeroOrMore(ParseTemplateParamDecl, state) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "Tp") && ParseTemplateParamDecl(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1441,13 +1930,14 @@ static bool ParseTemplateTemplateParam(State *state) {
|
||||
ParseSubstitution(state, /*accept_std=*/false));
|
||||
}
|
||||
|
||||
// <template-args> ::= I <template-arg>+ E
|
||||
// <template-args> ::= I <template-arg>+ [Q <requires-clause expr>] E
|
||||
static bool ParseTemplateArgs(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
DisableAppend(state);
|
||||
if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) &&
|
||||
Optional(ParseQRequiresClauseExpr(state)) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
RestoreAppend(state, copy.append);
|
||||
MaybeAppend(state, "<>");
|
||||
@@ -1457,7 +1947,8 @@ static bool ParseTemplateArgs(State *state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// <template-arg> ::= <type>
|
||||
// <template-arg> ::= <template-param-decl> <template-arg>
|
||||
// ::= <type>
|
||||
// ::= <expr-primary>
|
||||
// ::= J <template-arg>* E # argument pack
|
||||
// ::= X <expression> E
|
||||
@@ -1541,7 +2032,7 @@ static bool ParseTemplateArg(State *state) {
|
||||
// ::= L <source-name> [<template-args>] [<expr-cast-value> E]
|
||||
if (ParseLocalSourceName(state) && Optional(ParseTemplateArgs(state))) {
|
||||
copy = state->parse_state;
|
||||
if (ParseExprCastValue(state) && ParseOneCharToken(state, 'E')) {
|
||||
if (ParseExprCastValueAndTrailingE(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
@@ -1560,6 +2051,12 @@ static bool ParseTemplateArg(State *state) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTemplateParamDecl(state) && ParseTemplateArg(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1614,6 +2111,13 @@ static bool ParseBaseUnresolvedName(State *state) {
|
||||
// <base-unresolved-name>
|
||||
// ::= [gs] sr <unresolved-qualifier-level>+ E
|
||||
// <base-unresolved-name>
|
||||
// ::= sr St <simple-id> <simple-id> # nonstandard
|
||||
//
|
||||
// The last case is not part of the official grammar but has been observed in
|
||||
// real-world examples that the GNU demangler (but not the LLVM demangler) is
|
||||
// able to decode; see demangle_test.cc for one such symbol name. The shape
|
||||
// sr St <simple-id> <simple-id> was inferred by closed-box testing of the GNU
|
||||
// demangler.
|
||||
static bool ParseUnresolvedName(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
@@ -1633,7 +2137,7 @@ static bool ParseUnresolvedName(State *state) {
|
||||
|
||||
if (ParseTwoCharToken(state, "sr") && ParseOneCharToken(state, 'N') &&
|
||||
ParseUnresolvedType(state) &&
|
||||
OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) &&
|
||||
OneOrMore(ParseUnresolvedQualifierLevel, state) &&
|
||||
ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) {
|
||||
return true;
|
||||
}
|
||||
@@ -1641,35 +2145,160 @@ static bool ParseUnresolvedName(State *state) {
|
||||
|
||||
if (Optional(ParseTwoCharToken(state, "gs")) &&
|
||||
ParseTwoCharToken(state, "sr") &&
|
||||
OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) &&
|
||||
OneOrMore(ParseUnresolvedQualifierLevel, state) &&
|
||||
ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "sr") && ParseTwoCharToken(state, "St") &&
|
||||
ParseSimpleId(state) && ParseSimpleId(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// <expression> ::= <1-ary operator-name> <expression>
|
||||
// ::= <2-ary operator-name> <expression> <expression>
|
||||
// ::= <3-ary operator-name> <expression> <expression> <expression>
|
||||
// ::= cl <expression>+ E
|
||||
// ::= cp <simple-id> <expression>* E # Clang-specific.
|
||||
// ::= cv <type> <expression> # type (expression)
|
||||
// ::= cv <type> _ <expression>* E # type (expr-list)
|
||||
// ::= st <type>
|
||||
// ::= <template-param>
|
||||
// ::= <function-param>
|
||||
// ::= <expr-primary>
|
||||
// ::= dt <expression> <unresolved-name> # expr.name
|
||||
// ::= pt <expression> <unresolved-name> # expr->name
|
||||
// ::= sp <expression> # argument pack expansion
|
||||
// ::= sr <type> <unqualified-name> <template-args>
|
||||
// ::= sr <type> <unqualified-name>
|
||||
// <unresolved-qualifier-level> ::= <simple-id>
|
||||
// ::= <substitution> <template-args>
|
||||
//
|
||||
// The production <substitution> <template-args> is nonstandard but is observed
|
||||
// in practice. An upstream discussion on the best shape of <unresolved-name>
|
||||
// has not converged:
|
||||
//
|
||||
// https://github.com/itanium-cxx-abi/cxx-abi/issues/38
|
||||
static bool ParseUnresolvedQualifierLevel(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
|
||||
if (ParseSimpleId(state)) return true;
|
||||
|
||||
ParseState copy = state->parse_state;
|
||||
if (ParseSubstitution(state, /*accept_std=*/false) &&
|
||||
ParseTemplateArgs(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
// <union-selector> ::= _ [<number>]
|
||||
//
|
||||
// https://github.com/itanium-cxx-abi/cxx-abi/issues/47
|
||||
static bool ParseUnionSelector(State *state) {
|
||||
return ParseOneCharToken(state, '_') && Optional(ParseNumber(state, nullptr));
|
||||
}
|
||||
|
||||
// <function-param> ::= fp <(top-level) CV-qualifiers> _
|
||||
// ::= fp <(top-level) CV-qualifiers> <number> _
|
||||
// ::= fL <number> p <(top-level) CV-qualifiers> _
|
||||
// ::= fL <number> p <(top-level) CV-qualifiers> <number> _
|
||||
// ::= fpT # this
|
||||
static bool ParseFunctionParam(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
|
||||
ParseState copy = state->parse_state;
|
||||
|
||||
// Function-param expression (level 0).
|
||||
if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) &&
|
||||
Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// Function-param expression (level 1+).
|
||||
if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) &&
|
||||
ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) &&
|
||||
Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
return ParseThreeCharToken(state, "fpT");
|
||||
}
|
||||
|
||||
// <braced-expression> ::= <expression>
|
||||
// ::= di <field source-name> <braced-expression>
|
||||
// ::= dx <index expression> <braced-expression>
|
||||
// ::= dX <expression> <expression> <braced-expression>
|
||||
static bool ParseBracedExpression(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
|
||||
ParseState copy = state->parse_state;
|
||||
|
||||
if (ParseTwoCharToken(state, "di") && ParseSourceName(state) &&
|
||||
ParseBracedExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "dx") && ParseExpression(state) &&
|
||||
ParseBracedExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "dX") &&
|
||||
ParseExpression(state) && ParseExpression(state) &&
|
||||
ParseBracedExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
return ParseExpression(state);
|
||||
}
|
||||
|
||||
// <expression> ::= <1-ary operator-name> <expression>
|
||||
// ::= <2-ary operator-name> <expression> <expression>
|
||||
// ::= <3-ary operator-name> <expression> <expression> <expression>
|
||||
// ::= pp_ <expression> # ++e; pp <expression> is e++
|
||||
// ::= mm_ <expression> # --e; mm <expression> is e--
|
||||
// ::= cl <expression>+ E
|
||||
// ::= cp <simple-id> <expression>* E # Clang-specific.
|
||||
// ::= so <type> <expression> [<number>] <union-selector>* [p] E
|
||||
// ::= cv <type> <expression> # type (expression)
|
||||
// ::= cv <type> _ <expression>* E # type (expr-list)
|
||||
// ::= tl <type> <braced-expression>* E
|
||||
// ::= il <braced-expression>* E
|
||||
// ::= [gs] nw <expression>* _ <type> E
|
||||
// ::= [gs] nw <expression>* _ <type> <initializer>
|
||||
// ::= [gs] na <expression>* _ <type> E
|
||||
// ::= [gs] na <expression>* _ <type> <initializer>
|
||||
// ::= [gs] dl <expression>
|
||||
// ::= [gs] da <expression>
|
||||
// ::= dc <type> <expression>
|
||||
// ::= sc <type> <expression>
|
||||
// ::= cc <type> <expression>
|
||||
// ::= rc <type> <expression>
|
||||
// ::= ti <type>
|
||||
// ::= te <expression>
|
||||
// ::= st <type>
|
||||
// ::= at <type>
|
||||
// ::= az <expression>
|
||||
// ::= nx <expression>
|
||||
// ::= <template-param>
|
||||
// ::= <function-param>
|
||||
// ::= sZ <template-param>
|
||||
// ::= sZ <function-param>
|
||||
// ::= sP <template-arg>* E
|
||||
// ::= <expr-primary>
|
||||
// ::= dt <expression> <unresolved-name> # expr.name
|
||||
// ::= pt <expression> <unresolved-name> # expr->name
|
||||
// ::= sp <expression> # argument pack expansion
|
||||
// ::= fl <binary operator-name> <expression>
|
||||
// ::= fr <binary operator-name> <expression>
|
||||
// ::= fL <binary operator-name> <expression> <expression>
|
||||
// ::= fR <binary operator-name> <expression> <expression>
|
||||
// ::= tw <expression>
|
||||
// ::= tr
|
||||
// ::= sr <type> <unqualified-name> <template-args>
|
||||
// ::= sr <type> <unqualified-name>
|
||||
// ::= u <source-name> <template-arg>* E # vendor extension
|
||||
// ::= rq <requirement>+ E
|
||||
// ::= rQ <bare-function-type> _ <requirement>+ E
|
||||
static bool ParseExpression(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
@@ -1686,6 +2315,15 @@ static bool ParseExpression(State *state) {
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// Preincrement and predecrement. Postincrement and postdecrement are handled
|
||||
// by the operator-name logic later on.
|
||||
if ((ParseThreeCharToken(state, "pp_") ||
|
||||
ParseThreeCharToken(state, "mm_")) &&
|
||||
ParseExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// Clang-specific "cp <simple-id> <expression>* E"
|
||||
// https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338
|
||||
if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) &&
|
||||
@@ -1694,17 +2332,65 @@ static bool ParseExpression(State *state) {
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// Function-param expression (level 0).
|
||||
if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) &&
|
||||
Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {
|
||||
// <expression> ::= so <type> <expression> [<number>] <union-selector>* [p] E
|
||||
//
|
||||
// https://github.com/itanium-cxx-abi/cxx-abi/issues/47
|
||||
if (ParseTwoCharToken(state, "so") && ParseType(state) &&
|
||||
ParseExpression(state) && Optional(ParseNumber(state, nullptr)) &&
|
||||
ZeroOrMore(ParseUnionSelector, state) &&
|
||||
Optional(ParseOneCharToken(state, 'p')) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// Function-param expression (level 1+).
|
||||
if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) &&
|
||||
ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) &&
|
||||
Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {
|
||||
// <expression> ::= <function-param>
|
||||
if (ParseFunctionParam(state)) return true;
|
||||
state->parse_state = copy;
|
||||
|
||||
// <expression> ::= tl <type> <braced-expression>* E
|
||||
if (ParseTwoCharToken(state, "tl") && ParseType(state) &&
|
||||
ZeroOrMore(ParseBracedExpression, state) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// <expression> ::= il <braced-expression>* E
|
||||
if (ParseTwoCharToken(state, "il") &&
|
||||
ZeroOrMore(ParseBracedExpression, state) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// <expression> ::= [gs] nw <expression>* _ <type> E
|
||||
// ::= [gs] nw <expression>* _ <type> <initializer>
|
||||
// ::= [gs] na <expression>* _ <type> E
|
||||
// ::= [gs] na <expression>* _ <type> <initializer>
|
||||
if (Optional(ParseTwoCharToken(state, "gs")) &&
|
||||
(ParseTwoCharToken(state, "nw") || ParseTwoCharToken(state, "na")) &&
|
||||
ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, '_') &&
|
||||
ParseType(state) &&
|
||||
(ParseOneCharToken(state, 'E') || ParseInitializer(state))) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// <expression> ::= [gs] dl <expression>
|
||||
// ::= [gs] da <expression>
|
||||
if (Optional(ParseTwoCharToken(state, "gs")) &&
|
||||
(ParseTwoCharToken(state, "dl") || ParseTwoCharToken(state, "da")) &&
|
||||
ParseExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// dynamic_cast, static_cast, const_cast, reinterpret_cast.
|
||||
//
|
||||
// <expression> ::= (dc | sc | cc | rc) <type> <expression>
|
||||
if (ParseCharClass(state, "dscr") && ParseOneCharToken(state, 'c') &&
|
||||
ParseType(state) && ParseExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
@@ -1746,15 +2432,96 @@ static bool ParseExpression(State *state) {
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// typeid(type)
|
||||
if (ParseTwoCharToken(state, "ti") && ParseType(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// typeid(expression)
|
||||
if (ParseTwoCharToken(state, "te") && ParseExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// sizeof type
|
||||
if (ParseTwoCharToken(state, "st") && ParseType(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// alignof(type)
|
||||
if (ParseTwoCharToken(state, "at") && ParseType(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// alignof(expression), a GNU extension
|
||||
if (ParseTwoCharToken(state, "az") && ParseExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// noexcept(expression) appearing as an expression in a dependent signature
|
||||
if (ParseTwoCharToken(state, "nx") && ParseExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// sizeof...(pack)
|
||||
//
|
||||
// <expression> ::= sZ <template-param>
|
||||
// ::= sZ <function-param>
|
||||
if (ParseTwoCharToken(state, "sZ") &&
|
||||
(ParseFunctionParam(state) || ParseTemplateParam(state))) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// sizeof...(pack) captured from an alias template
|
||||
//
|
||||
// <expression> ::= sP <template-arg>* E
|
||||
if (ParseTwoCharToken(state, "sP") && ZeroOrMore(ParseTemplateArg, state) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// Unary folds (... op pack) and (pack op ...).
|
||||
//
|
||||
// <expression> ::= fl <binary operator-name> <expression>
|
||||
// ::= fr <binary operator-name> <expression>
|
||||
if ((ParseTwoCharToken(state, "fl") || ParseTwoCharToken(state, "fr")) &&
|
||||
ParseOperatorName(state, nullptr) && ParseExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// Binary folds (init op ... op pack) and (pack op ... op init).
|
||||
//
|
||||
// <expression> ::= fL <binary operator-name> <expression> <expression>
|
||||
// ::= fR <binary operator-name> <expression> <expression>
|
||||
if ((ParseTwoCharToken(state, "fL") || ParseTwoCharToken(state, "fR")) &&
|
||||
ParseOperatorName(state, nullptr) && ParseExpression(state) &&
|
||||
ParseExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// tw <expression>: throw e
|
||||
if (ParseTwoCharToken(state, "tw") && ParseExpression(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// tr: throw (rethrows an exception from the handler that caught it)
|
||||
if (ParseTwoCharToken(state, "tr")) return true;
|
||||
|
||||
// Object and pointer member access expressions.
|
||||
//
|
||||
// <expression> ::= (dt | pt) <expression> <unresolved-name>
|
||||
if ((ParseTwoCharToken(state, "dt") || ParseTwoCharToken(state, "pt")) &&
|
||||
ParseExpression(state) && ParseType(state)) {
|
||||
ParseExpression(state) && ParseUnresolvedName(state)) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
@@ -1774,9 +2541,61 @@ static bool ParseExpression(State *state) {
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// Vendor extended expressions
|
||||
if (ParseOneCharToken(state, 'u') && ParseSourceName(state) &&
|
||||
ZeroOrMore(ParseTemplateArg, state) && ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// <expression> ::= rq <requirement>+ E
|
||||
//
|
||||
// https://github.com/itanium-cxx-abi/cxx-abi/issues/24
|
||||
if (ParseTwoCharToken(state, "rq") && OneOrMore(ParseRequirement, state) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// <expression> ::= rQ <bare-function-type> _ <requirement>+ E
|
||||
//
|
||||
// https://github.com/itanium-cxx-abi/cxx-abi/issues/24
|
||||
if (ParseTwoCharToken(state, "rQ") && ParseBareFunctionType(state) &&
|
||||
ParseOneCharToken(state, '_') && OneOrMore(ParseRequirement, state) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
return ParseUnresolvedName(state);
|
||||
}
|
||||
|
||||
// <initializer> ::= pi <expression>* E
|
||||
// ::= il <braced-expression>* E
|
||||
//
|
||||
// The il ... E form is not in the ABI spec but is seen in practice for
|
||||
// braced-init-lists in new-expressions, which are standard syntax from C++11
|
||||
// on.
|
||||
static bool ParseInitializer(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
|
||||
if (ParseTwoCharToken(state, "pi") && ZeroOrMore(ParseExpression, state) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseTwoCharToken(state, "il") &&
|
||||
ZeroOrMore(ParseBracedExpression, state) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
// <expr-primary> ::= L <type> <(value) number> E
|
||||
// ::= L <type> <(value) float> E
|
||||
// ::= L <mangled-name> E
|
||||
@@ -1819,10 +2638,35 @@ static bool ParseExprPrimary(State *state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The merged cast production.
|
||||
if (ParseOneCharToken(state, 'L') && ParseType(state) &&
|
||||
ParseExprCastValue(state)) {
|
||||
return true;
|
||||
if (ParseOneCharToken(state, 'L')) {
|
||||
// There are two special cases in which a literal may or must contain a type
|
||||
// without a value. The first is that both LDnE and LDn0E are valid
|
||||
// encodings of nullptr, used in different situations. Recognize LDnE here,
|
||||
// leaving LDn0E to be recognized by the general logic afterward.
|
||||
if (ParseThreeCharToken(state, "DnE")) return true;
|
||||
|
||||
// The second special case is a string literal, currently mangled in C++98
|
||||
// style as LA<length + 1>_KcE. This is inadequate to support C++11 and
|
||||
// later versions, and the discussion of this problem has not converged.
|
||||
//
|
||||
// https://github.com/itanium-cxx-abi/cxx-abi/issues/64
|
||||
//
|
||||
// For now the bare-type mangling is what's used in practice, so we
|
||||
// recognize this form and only this form if an array type appears here.
|
||||
// Someday we'll probably have to accept a new form of value mangling in
|
||||
// LA...E constructs. (Note also that C++20 allows a wide range of
|
||||
// class-type objects as template arguments, so someday their values will be
|
||||
// mangled and we'll have to recognize them here too.)
|
||||
if (RemainingInput(state)[0] == 'A' /* an array type follows */) {
|
||||
if (ParseType(state) && ParseOneCharToken(state, 'E')) return true;
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The merged cast production.
|
||||
if (ParseType(state) && ParseExprCastValueAndTrailingE(state)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
@@ -1836,7 +2680,7 @@ static bool ParseExprPrimary(State *state) {
|
||||
}
|
||||
|
||||
// <number> or <float>, followed by 'E', as described above ParseExprPrimary.
|
||||
static bool ParseExprCastValue(State *state) {
|
||||
static bool ParseExprCastValueAndTrailingE(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
// We have to be able to backtrack after accepting a number because we could
|
||||
@@ -1848,39 +2692,148 @@ static bool ParseExprCastValue(State *state) {
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseFloatNumber(state) && ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
if (ParseFloatNumber(state)) {
|
||||
// <float> for ordinary floating-point types
|
||||
if (ParseOneCharToken(state, 'E')) return true;
|
||||
|
||||
// <float> _ <float> for complex floating-point types
|
||||
if (ParseOneCharToken(state, '_') && ParseFloatNumber(state) &&
|
||||
ParseOneCharToken(state, 'E')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parses `Q <requires-clause expr>`.
|
||||
// If parsing fails, applies backtracking to `state`.
|
||||
//
|
||||
// This function covers two symbols instead of one for convenience,
|
||||
// because in LLVM's Itanium ABI mangling grammar, <requires-clause expr>
|
||||
// always appears after Q.
|
||||
//
|
||||
// Does not emit the parsed `requires` clause to simplify the implementation.
|
||||
// In other words, these two functions' mangled names will demangle identically:
|
||||
//
|
||||
// template <typename T>
|
||||
// int foo(T) requires IsIntegral<T>;
|
||||
//
|
||||
// vs.
|
||||
//
|
||||
// template <typename T>
|
||||
// int foo(T);
|
||||
static bool ParseQRequiresClauseExpr(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
DisableAppend(state);
|
||||
|
||||
// <requires-clause expr> is just an <expression>: http://shortn/_9E1Ul0rIM8
|
||||
if (ParseOneCharToken(state, 'Q') && ParseExpression(state)) {
|
||||
RestoreAppend(state, copy.append);
|
||||
return true;
|
||||
}
|
||||
|
||||
// also restores append
|
||||
state->parse_state = copy;
|
||||
return false;
|
||||
}
|
||||
|
||||
// <requirement> ::= X <expression> [N] [R <type-constraint>]
|
||||
// <requirement> ::= T <type>
|
||||
// <requirement> ::= Q <constraint-expression>
|
||||
//
|
||||
// <constraint-expression> ::= <expression>
|
||||
//
|
||||
// https://github.com/itanium-cxx-abi/cxx-abi/issues/24
|
||||
static bool ParseRequirement(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
|
||||
ParseState copy = state->parse_state;
|
||||
|
||||
if (ParseOneCharToken(state, 'X') && ParseExpression(state) &&
|
||||
Optional(ParseOneCharToken(state, 'N')) &&
|
||||
// This logic backtracks cleanly if we eat an R but a valid type doesn't
|
||||
// follow it.
|
||||
(!ParseOneCharToken(state, 'R') || ParseTypeConstraint(state))) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseOneCharToken(state, 'T') && ParseType(state)) return true;
|
||||
state->parse_state = copy;
|
||||
|
||||
if (ParseOneCharToken(state, 'Q') && ParseExpression(state)) return true;
|
||||
state->parse_state = copy;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// <type-constraint> ::= <name>
|
||||
static bool ParseTypeConstraint(State *state) {
|
||||
return ParseName(state);
|
||||
}
|
||||
|
||||
// <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>]
|
||||
// ::= Z <(function) encoding> E s [<discriminator>]
|
||||
// ::= Z <(function) encoding> E d [<(parameter) number>] _ <name>
|
||||
//
|
||||
// Parsing a common prefix of these two productions together avoids an
|
||||
// exponential blowup of backtracking. Parse like:
|
||||
// <local-name> := Z <encoding> E <local-name-suffix>
|
||||
// <local-name-suffix> ::= s [<discriminator>]
|
||||
// ::= d [<(parameter) number>] _ <name>
|
||||
// ::= <name> [<discriminator>]
|
||||
|
||||
static bool ParseLocalNameSuffix(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
|
||||
// <local-name-suffix> ::= d [<(parameter) number>] _ <name>
|
||||
if (ParseOneCharToken(state, 'd') &&
|
||||
(IsDigit(RemainingInput(state)[0]) || RemainingInput(state)[0] == '_')) {
|
||||
int number = -1;
|
||||
Optional(ParseNumber(state, &number));
|
||||
if (number < -1 || number > 2147483645) {
|
||||
// Work around overflow cases. We do not expect these outside of a fuzzer
|
||||
// or other source of adversarial input. If we do detect overflow here,
|
||||
// we'll print {default arg#1}.
|
||||
number = -1;
|
||||
}
|
||||
number += 2;
|
||||
|
||||
// The ::{default arg#1}:: infix must be rendered before the lambda itself,
|
||||
// so print this before parsing the rest of the <local-name-suffix>.
|
||||
MaybeAppend(state, "::{default arg#");
|
||||
MaybeAppendDecimal(state, number);
|
||||
MaybeAppend(state, "}::");
|
||||
if (ParseOneCharToken(state, '_') && ParseName(state)) return true;
|
||||
|
||||
// On late parse failure, roll back not only the input but also the output,
|
||||
// whose trailing NUL was overwritten.
|
||||
state->parse_state = copy;
|
||||
if (state->parse_state.append) {
|
||||
state->out[state->parse_state.out_cur_idx] = '\0';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
|
||||
// <local-name-suffix> ::= <name> [<discriminator>]
|
||||
if (MaybeAppend(state, "::") && ParseName(state) &&
|
||||
Optional(ParseDiscriminator(state))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Since we're not going to overwrite the above "::" by re-parsing the
|
||||
// <encoding> (whose trailing '\0' byte was in the byte now holding the
|
||||
// first ':'), we have to rollback the "::" if the <name> parse failed.
|
||||
state->parse_state = copy;
|
||||
if (state->parse_state.append) {
|
||||
state->out[state->parse_state.out_cur_idx - 2] = '\0';
|
||||
state->out[state->parse_state.out_cur_idx] = '\0';
|
||||
}
|
||||
|
||||
// <local-name-suffix> ::= s [<discriminator>]
|
||||
return ParseOneCharToken(state, 's') && Optional(ParseDiscriminator(state));
|
||||
}
|
||||
|
||||
@@ -1896,12 +2849,22 @@ static bool ParseLocalName(State *state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// <discriminator> := _ <(non-negative) number>
|
||||
// <discriminator> := _ <digit>
|
||||
// := __ <number (>= 10)> _
|
||||
static bool ParseDiscriminator(State *state) {
|
||||
ComplexityGuard guard(state);
|
||||
if (guard.IsTooComplex()) return false;
|
||||
ParseState copy = state->parse_state;
|
||||
if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr)) {
|
||||
|
||||
// Both forms start with _ so parse that first.
|
||||
if (!ParseOneCharToken(state, '_')) return false;
|
||||
|
||||
// <digit>
|
||||
if (ParseDigit(state, nullptr)) return true;
|
||||
|
||||
// _ <number> _
|
||||
if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr) &&
|
||||
ParseOneCharToken(state, '_')) {
|
||||
return true;
|
||||
}
|
||||
state->parse_state = copy;
|
||||
@@ -1947,6 +2910,7 @@ static bool ParseSubstitution(State *state, bool accept_std) {
|
||||
MaybeAppend(state, p->real_name);
|
||||
}
|
||||
++state->parse_state.mangled_idx;
|
||||
UpdateHighWaterMark(state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1972,10 +2936,13 @@ static bool ParseTopLevelMangledName(State *state) {
|
||||
MaybeAppend(state, RemainingInput(state));
|
||||
return true;
|
||||
}
|
||||
ReportHighWaterMark(state);
|
||||
return false; // Unconsumed suffix.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ReportHighWaterMark(state);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1985,6 +2952,10 @@ static bool Overflowed(const State *state) {
|
||||
|
||||
// The demangler entry point.
|
||||
bool Demangle(const char* mangled, char* out, size_t out_size) {
|
||||
if (mangled[0] == '_' && mangled[1] == 'R') {
|
||||
return DemangleRustSymbolEncoding(mangled, out, out_size);
|
||||
}
|
||||
|
||||
State state;
|
||||
InitState(&state, mangled, out, out_size);
|
||||
return ParseTopLevelMangledName(&state) && !Overflowed(&state) &&
|
||||
|
||||
3
Pods/abseil/absl/debugging/internal/demangle.h
generated
3
Pods/abseil/absl/debugging/internal/demangle.h
generated
@@ -56,6 +56,9 @@ namespace debugging_internal {
|
||||
//
|
||||
// See the unit test for more examples.
|
||||
//
|
||||
// Demangle also recognizes Rust mangled names by delegating the parsing of
|
||||
// anything that starts with _R to DemangleRustSymbolEncoding (demangle_rust.h).
|
||||
//
|
||||
// Note: we might want to write demanglers for ABIs other than Itanium
|
||||
// C++ ABI in the future.
|
||||
bool Demangle(const char* mangled, char* out, size_t out_size);
|
||||
|
||||
925
Pods/abseil/absl/debugging/internal/demangle_rust.cc
generated
Normal file
925
Pods/abseil/absl/debugging/internal/demangle_rust.cc
generated
Normal file
@@ -0,0 +1,925 @@
|
||||
// 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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/debugging/internal/decode_rust_punycode.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace debugging_internal {
|
||||
|
||||
namespace {
|
||||
|
||||
// Same step limit as the C++ demangler in demangle.cc uses.
|
||||
constexpr int kMaxReturns = 1 << 17;
|
||||
|
||||
bool IsDigit(char c) { return '0' <= c && c <= '9'; }
|
||||
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) {
|
||||
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
|
||||
class RustSymbolParser {
|
||||
public:
|
||||
// Prepares to demangle the given encoding, a Rust symbol name starting with
|
||||
// _R, into the output buffer [out, out_end). The caller is expected to
|
||||
// continue by calling the new object's Parse function.
|
||||
RustSymbolParser(const char* encoding, char* out, char* const out_end)
|
||||
: encoding_(encoding), out_(out), out_end_(out_end) {
|
||||
if (out_ != out_end_) *out_ = '\0';
|
||||
}
|
||||
|
||||
// Parses the constructor's encoding argument, writing output into the range
|
||||
// [out, out_end). Returns true on success and false for input whose
|
||||
// structure was not recognized or exceeded implementation limits, such as by
|
||||
// nesting structures too deep. In either case *this should not be used
|
||||
// again.
|
||||
ABSL_MUST_USE_RESULT bool Parse() && {
|
||||
// Recursively parses the grammar production named by callee, then resumes
|
||||
// execution at the next statement.
|
||||
//
|
||||
// Recursive-descent parsing is a beautifully readable translation of a
|
||||
// grammar, but it risks stack overflow if implemented by naive recursion on
|
||||
// the C++ call stack. So we simulate recursion by goto and switch instead,
|
||||
// keeping a bounded stack of "return addresses" in the recursion_stack_
|
||||
// member.
|
||||
//
|
||||
// The callee argument is a statement label. We goto that label after
|
||||
// saving the "return address" on recursion_stack_. The next continue
|
||||
// statement in the for loop below "returns" from this "call".
|
||||
//
|
||||
// The caller argument names the return point. Each value of caller must
|
||||
// appear in only one ABSL_DEMANGLER_RECURSE call and be listed in the
|
||||
// definition of enum ReturnAddress. The switch implements the control
|
||||
// transfer from the end of a "called" subroutine back to the statement
|
||||
// after the "call".
|
||||
//
|
||||
// Note that not all the grammar productions have to be packed into the
|
||||
// switch, but only those which appear in a cycle in the grammar. Anything
|
||||
// acyclic can be written as ordinary functions and function calls, e.g.,
|
||||
// ParseIdentifier.
|
||||
#define ABSL_DEMANGLER_RECURSE(callee, caller) \
|
||||
do { \
|
||||
if (recursion_depth_ == kStackSize) return false; \
|
||||
/* The next continue will switch on this saved value ... */ \
|
||||
recursion_stack_[recursion_depth_++] = caller; \
|
||||
goto callee; \
|
||||
/* ... and will land here, resuming the suspended code. */ \
|
||||
case caller: {} \
|
||||
} while (0)
|
||||
|
||||
// Parse the encoding, counting completed recursive calls to guard against
|
||||
// excessively complex input and infinite-loop bugs.
|
||||
int iter = 0;
|
||||
goto whole_encoding;
|
||||
for (; iter < kMaxReturns && recursion_depth_ > 0; ++iter) {
|
||||
// This switch resumes the code path most recently suspended by
|
||||
// ABSL_DEMANGLER_RECURSE.
|
||||
switch (recursion_stack_[--recursion_depth_]) {
|
||||
//
|
||||
// symbol-name ->
|
||||
// _R decimal-number? path instantiating-crate? vendor-specific-suffix?
|
||||
whole_encoding:
|
||||
if (!Eat('_') || !Eat('R')) return false;
|
||||
// decimal-number? is always empty today, so proceed to path, which
|
||||
// can't start with a decimal digit.
|
||||
ABSL_DEMANGLER_RECURSE(path, kInstantiatingCrate);
|
||||
if (IsAlpha(Peek())) {
|
||||
++silence_depth_; // Print nothing more from here on.
|
||||
ABSL_DEMANGLER_RECURSE(path, kVendorSpecificSuffix);
|
||||
}
|
||||
switch (Take()) {
|
||||
case '.': case '$': case '\0': return true;
|
||||
}
|
||||
return false; // unexpected trailing content
|
||||
|
||||
// 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': goto inherent_impl;
|
||||
case 'X': goto trait_impl;
|
||||
case 'Y': goto trait_definition;
|
||||
case 'N': goto nested_path;
|
||||
case 'I': goto generic_args;
|
||||
case 'B': goto path_backref;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// crate-root -> C identifier (C consumed above)
|
||||
crate_root:
|
||||
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;
|
||||
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 a stack so we can print
|
||||
// ::{closure#0} or ::{shim:vtable#0} or ::{X:name#0} as needed.
|
||||
if (IsUpper(Peek())) {
|
||||
if (!PushNamespace(Take())) return false;
|
||||
ABSL_DEMANGLER_RECURSE(path, kIdentifierInUppercaseNamespace);
|
||||
if (!Emit("::")) return false;
|
||||
if (!ParseIdentifier(PopNamespace())) return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lowercase namespaces, however, are never represented in the output;
|
||||
// they all emit just ::name.
|
||||
if (IsLower(Take())) {
|
||||
ABSL_DEMANGLER_RECURSE(path, kIdentifierInLowercaseNamespace);
|
||||
if (!Emit("::")) return false;
|
||||
if (!ParseIdentifier()) return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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')) {
|
||||
// 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);
|
||||
if (!Emit("]")) return false;
|
||||
continue;
|
||||
}
|
||||
if (Eat('T')) goto tuple_type;
|
||||
if (Eat('R')) {
|
||||
if (!Emit("&")) return false;
|
||||
if (!ParseOptionalLifetime()) return false;
|
||||
goto type;
|
||||
}
|
||||
if (Eat('Q')) {
|
||||
if (!Emit("&mut ")) return false;
|
||||
if (!ParseOptionalLifetime()) return false;
|
||||
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')) goto fn_type;
|
||||
if (Eat('D')) goto dyn_trait_type;
|
||||
if (Eat('B')) goto type_backref;
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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.
|
||||
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
|
||||
// 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 (Peek() == 'L') {
|
||||
if (!ParseOptionalLifetime()) return false;
|
||||
continue;
|
||||
}
|
||||
if (Eat('K')) goto constant;
|
||||
goto type;
|
||||
|
||||
// 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;
|
||||
|
||||
const_backref:
|
||||
if (!BeginBackref()) return false;
|
||||
if (silence_depth_ == 0) {
|
||||
ABSL_DEMANGLER_RECURSE(constant, kConstantBackrefEnding);
|
||||
}
|
||||
EndBackref();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // hit iteration limit or a bug in our stack handling
|
||||
}
|
||||
|
||||
private:
|
||||
// Enumerates resumption points for ABSL_DEMANGLER_RECURSE calls.
|
||||
enum ReturnAddress : uint8_t {
|
||||
kInstantiatingCrate,
|
||||
kVendorSpecificSuffix,
|
||||
kIdentifierInUppercaseNamespace,
|
||||
kIdentifierInLowercaseNamespace,
|
||||
kInherentImplType,
|
||||
kInherentImplEnding,
|
||||
kTraitImplType,
|
||||
kTraitImplInfix,
|
||||
kTraitImplEnding,
|
||||
kImplPathEnding,
|
||||
kTraitDefinitionInfix,
|
||||
kTraitDefinitionEnding,
|
||||
kArraySize,
|
||||
kFinishArray,
|
||||
kSliceEnding,
|
||||
kAfterFirstTupleElement,
|
||||
kAfterSecondTupleElement,
|
||||
kAfterThirdTupleElement,
|
||||
kAfterSubsequentTupleElement,
|
||||
kContinueParameterList,
|
||||
kFinishFn,
|
||||
kBeginAutoTraits,
|
||||
kContinueAutoTraits,
|
||||
kContinueDynTrait,
|
||||
kContinueAssocBinding,
|
||||
kConstData,
|
||||
kBeginGenericArgList,
|
||||
kContinueGenericArgList,
|
||||
kPathBackrefEnding,
|
||||
kTypeBackrefEnding,
|
||||
kConstantBackrefEnding,
|
||||
};
|
||||
|
||||
// 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 {
|
||||
// Maximum recursive calls outstanding at one time.
|
||||
kStackSize = 256,
|
||||
|
||||
// Maximum N<uppercase> nested-paths open at once. We do not expect
|
||||
// closures inside closures inside closures as much as functions inside
|
||||
// modules inside other modules, so we can use a smaller array here.
|
||||
kNamespaceStackSize = 64,
|
||||
|
||||
// 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_]; }
|
||||
|
||||
// Consumes and returns the next input character.
|
||||
char Take() { return encoding_[pos_++]; }
|
||||
|
||||
// If the next input character is the given character, consumes it and returns
|
||||
// true; otherwise returns false without consuming a character.
|
||||
ABSL_MUST_USE_RESULT bool Eat(char want) {
|
||||
if (encoding_[pos_] != want) return false;
|
||||
++pos_;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Provided there is enough remaining output space, appends c to the output,
|
||||
// writing a fresh NUL terminator afterward, and returns true. Returns false
|
||||
// if the output buffer had less than two bytes free.
|
||||
ABSL_MUST_USE_RESULT bool EmitChar(char c) {
|
||||
if (silence_depth_ > 0) return true;
|
||||
if (out_end_ - out_ < 2) return false;
|
||||
*out_++ = c;
|
||||
*out_ = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
// Provided there is enough remaining output space, appends the C string token
|
||||
// to the output, followed by a NUL character, and returns true. Returns
|
||||
// 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 size_t token_length = std::strlen(token);
|
||||
const size_t bytes_to_copy = token_length + 1; // token and final NUL
|
||||
if (static_cast<size_t>(out_end_ - out_) < bytes_to_copy) return false;
|
||||
std::memcpy(out_, token, bytes_to_copy);
|
||||
out_ += token_length;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Provided there is enough remaining output space, appends the decimal form
|
||||
// of disambiguator (if it's nonnegative) or "?" (if it's negative) to the
|
||||
// output, followed by a NUL character, and returns true. Returns false if
|
||||
// not everything fit into the output buffer.
|
||||
ABSL_MUST_USE_RESULT bool EmitDisambiguator(int disambiguator) {
|
||||
if (disambiguator < 0) return EmitChar('?'); // parsed but too large
|
||||
if (disambiguator == 0) return EmitChar('0');
|
||||
// Convert disambiguator to decimal text. Three digits per byte is enough
|
||||
// because 999 > 256. The bound will remain correct even if future
|
||||
// maintenance changes the type of the disambiguator variable.
|
||||
char digits[3 * sizeof(disambiguator)] = {};
|
||||
size_t leading_digit_index = sizeof(digits) - 1;
|
||||
for (; disambiguator > 0; disambiguator /= 10) {
|
||||
digits[--leading_digit_index] =
|
||||
static_cast<char>('0' + disambiguator % 10);
|
||||
}
|
||||
return Emit(digits + leading_digit_index);
|
||||
}
|
||||
|
||||
// Consumes an optional disambiguator (s123_) from the input.
|
||||
//
|
||||
// On success returns true and fills value with the encoded value if it was
|
||||
// not too big, otherwise with -1. If the optional disambiguator was omitted,
|
||||
// value is 0. On parse failure returns false and sets value to -1.
|
||||
ABSL_MUST_USE_RESULT bool ParseDisambiguator(int& value) {
|
||||
value = -1;
|
||||
|
||||
// disambiguator = s base-62-number
|
||||
//
|
||||
// Disambiguators are optional. An omitted disambiguator is zero.
|
||||
if (!Eat('s')) {
|
||||
value = 0;
|
||||
return true;
|
||||
}
|
||||
int base_62_value = 0;
|
||||
if (!ParseBase62Number(base_62_value)) return false;
|
||||
value = base_62_value < 0 ? -1 : base_62_value + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Consumes a base-62 number like _ or 123_ from the input.
|
||||
//
|
||||
// On success returns true and fills value with the encoded value if it was
|
||||
// not too big, otherwise with -1. On parse failure returns false and sets
|
||||
// value to -1.
|
||||
ABSL_MUST_USE_RESULT bool ParseBase62Number(int& value) {
|
||||
value = -1;
|
||||
|
||||
// base-62-number = (digit | lower | upper)* _
|
||||
//
|
||||
// An empty base-62 digit sequence means 0.
|
||||
if (Eat('_')) {
|
||||
value = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// A nonempty digit sequence denotes its base-62 value plus 1.
|
||||
int encoded_number = 0;
|
||||
bool overflowed = false;
|
||||
while (IsAlpha(Peek()) || IsDigit(Peek())) {
|
||||
const char c = Take();
|
||||
if (encoded_number >= std::numeric_limits<int>::max()/62) {
|
||||
// If we are close to overflowing an int, keep parsing but stop updating
|
||||
// encoded_number and remember to return -1 at the end. The point is to
|
||||
// avoid undefined behavior while parsing crate-root disambiguators,
|
||||
// which are large in practice but not shown in demangling, while
|
||||
// successfully computing closure and shim disambiguators, which are
|
||||
// typically small and are printed out.
|
||||
overflowed = true;
|
||||
} else {
|
||||
int digit;
|
||||
if (IsDigit(c)) {
|
||||
digit = c - '0';
|
||||
} else if (IsLower(c)) {
|
||||
digit = c - 'a' + 10;
|
||||
} else {
|
||||
digit = c - 'A' + 36;
|
||||
}
|
||||
encoded_number = 62 * encoded_number + digit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Eat('_')) return false;
|
||||
if (!overflowed) value = encoded_number + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Consumes an identifier from the input, returning true on success.
|
||||
//
|
||||
// A nonzero uppercase_namespace specifies the character after the N in a
|
||||
// nested-identifier, e.g., 'C' for a closure, allowing ParseIdentifier to
|
||||
// write out the name with the conventional decoration for that namespace.
|
||||
ABSL_MUST_USE_RESULT bool ParseIdentifier(char uppercase_namespace = '\0') {
|
||||
// identifier -> disambiguator? undisambiguated-identifier
|
||||
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<namespace> 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;
|
||||
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<size_t>(num_bytes);
|
||||
}
|
||||
|
||||
// Emit the beginnings of braced forms like {shim:vtable#0}.
|
||||
if (uppercase_namespace != '\0') {
|
||||
switch (uppercase_namespace) {
|
||||
case 'C':
|
||||
if (!Emit("{closure")) return false;
|
||||
break;
|
||||
case 'S':
|
||||
if (!Emit("{shim")) return false;
|
||||
break;
|
||||
default:
|
||||
if (!EmitChar('{') || !EmitChar(uppercase_namespace)) return false;
|
||||
break;
|
||||
}
|
||||
if (num_bytes > 0 && !Emit(":")) return false;
|
||||
}
|
||||
|
||||
// Emit the name itself.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the endings of braced forms, e.g., "#42}".
|
||||
if (uppercase_namespace != '\0') {
|
||||
if (!EmitChar('#')) return false;
|
||||
if (!EmitDisambiguator(disambiguator)) return false;
|
||||
if (!EmitChar('}')) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Consumes a decimal number like 0 or 123 from the input. On success returns
|
||||
// true and fills value with the encoded value. If the encoded value is too
|
||||
// large or otherwise unparsable, returns false and sets value to -1.
|
||||
ABSL_MUST_USE_RESULT bool ParseDecimalNumber(int& value) {
|
||||
value = -1;
|
||||
if (!IsDigit(Peek())) return false;
|
||||
int encoded_number = Take() - '0';
|
||||
if (encoded_number == 0) {
|
||||
// Decimal numbers are never encoded with extra leading zeroes.
|
||||
value = 0;
|
||||
return true;
|
||||
}
|
||||
while (IsDigit(Peek()) &&
|
||||
// avoid overflow
|
||||
encoded_number < std::numeric_limits<int>::max()/10) {
|
||||
encoded_number = 10 * encoded_number + (Take() - '0');
|
||||
}
|
||||
if (IsDigit(Peek())) return false; // too big
|
||||
value = encoded_number;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (namespace_depth_ == kNamespaceStackSize) return false;
|
||||
namespace_stack_[namespace_depth_++] = ns;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pops the last pushed namespace. Requires that the namespace stack is not
|
||||
// empty (namespace_depth_ > 0).
|
||||
char PopNamespace() { return namespace_stack_[--namespace_depth_]; }
|
||||
|
||||
// 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(); }
|
||||
|
||||
// The leftmost recursion_depth_ elements of recursion_stack_ contain the
|
||||
// ReturnAddresses pushed by ABSL_DEMANGLER_RECURSE calls not yet completed.
|
||||
ReturnAddress recursion_stack_[kStackSize] = {};
|
||||
int recursion_depth_ = 0;
|
||||
|
||||
// The leftmost namespace_depth_ elements of namespace_stack_ contain the
|
||||
// uppercase namespace identifiers for open nested-paths, e.g., 'C' for a
|
||||
// closure.
|
||||
char namespace_stack_[kNamespaceStackSize] = {};
|
||||
int namespace_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 to a Rust mangled symbol, and encoding_[pos_] is
|
||||
// the next input character to be scanned.
|
||||
int pos_ = 0;
|
||||
const char* encoding_ = nullptr;
|
||||
|
||||
// Output: *out_ is where the next output character should be written, and
|
||||
// out_end_ points past the last byte of available space.
|
||||
char* out_ = nullptr;
|
||||
char* out_end_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool DemangleRustSymbolEncoding(const char* mangled, char* out,
|
||||
size_t out_size) {
|
||||
return RustSymbolParser(mangled, out, out + out_size).Parse();
|
||||
}
|
||||
|
||||
} // namespace debugging_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
42
Pods/abseil/absl/debugging/internal/demangle_rust.h
generated
Normal file
42
Pods/abseil/absl/debugging/internal/demangle_rust.h
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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.
|
||||
|
||||
#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_RUST_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_RUST_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace debugging_internal {
|
||||
|
||||
// Demangle the Rust encoding `mangled`. On success, return true and write the
|
||||
// demangled symbol name to `out`. Otherwise, return false, leaving unspecified
|
||||
// contents in `out`. For example, calling DemangleRustSymbolEncoding with
|
||||
// `mangled = "_RNvC8my_crate7my_func"` will yield `my_crate::my_func` in `out`,
|
||||
// provided `out_size` is large enough for that value and its trailing NUL.
|
||||
//
|
||||
// DemangleRustSymbolEncoding is async-signal-safe and runs in bounded C++
|
||||
// call-stack space. It is suitable for symbolizing stack traces in a signal
|
||||
// handler.
|
||||
bool DemangleRustSymbolEncoding(const char* mangled, char* out,
|
||||
size_t out_size);
|
||||
|
||||
} // namespace debugging_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_DEMANGLE_RUST_H_
|
||||
59
Pods/abseil/absl/debugging/internal/elf_mem_image.cc
generated
59
Pods/abseil/absl/debugging/internal/elf_mem_image.cc
generated
@@ -20,8 +20,11 @@
|
||||
#ifdef ABSL_HAVE_ELF_MEM_IMAGE // defined in elf_mem_image.h
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
|
||||
@@ -86,20 +89,14 @@ ElfMemImage::ElfMemImage(const void *base) {
|
||||
Init(base);
|
||||
}
|
||||
|
||||
int ElfMemImage::GetNumSymbols() const {
|
||||
if (!hash_) {
|
||||
return 0;
|
||||
}
|
||||
// See http://www.caldera.com/developers/gabi/latest/ch5.dynamic.html#hash
|
||||
return static_cast<int>(hash_[1]);
|
||||
}
|
||||
uint32_t ElfMemImage::GetNumSymbols() const { return num_syms_; }
|
||||
|
||||
const ElfW(Sym) *ElfMemImage::GetDynsym(int index) const {
|
||||
const ElfW(Sym) * ElfMemImage::GetDynsym(uint32_t index) const {
|
||||
ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range");
|
||||
return dynsym_ + index;
|
||||
}
|
||||
|
||||
const ElfW(Versym) *ElfMemImage::GetVersym(int index) const {
|
||||
const ElfW(Versym) *ElfMemImage::GetVersym(uint32_t index) const {
|
||||
ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range");
|
||||
return versym_ + index;
|
||||
}
|
||||
@@ -154,7 +151,7 @@ void ElfMemImage::Init(const void *base) {
|
||||
dynstr_ = nullptr;
|
||||
versym_ = nullptr;
|
||||
verdef_ = nullptr;
|
||||
hash_ = nullptr;
|
||||
num_syms_ = 0;
|
||||
strsize_ = 0;
|
||||
verdefnum_ = 0;
|
||||
// Sentinel: PT_LOAD .p_vaddr can't possibly be this.
|
||||
@@ -219,12 +216,17 @@ void ElfMemImage::Init(const void *base) {
|
||||
base_as_char - reinterpret_cast<const char *>(link_base_);
|
||||
ElfW(Dyn)* dynamic_entry = reinterpret_cast<ElfW(Dyn)*>(
|
||||
static_cast<intptr_t>(dynamic_program_header->p_vaddr) + relocation);
|
||||
uint32_t *sysv_hash = nullptr;
|
||||
uint32_t *gnu_hash = nullptr;
|
||||
for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) {
|
||||
const auto value =
|
||||
static_cast<intptr_t>(dynamic_entry->d_un.d_val) + relocation;
|
||||
switch (dynamic_entry->d_tag) {
|
||||
case DT_HASH:
|
||||
hash_ = reinterpret_cast<ElfW(Word) *>(value);
|
||||
sysv_hash = reinterpret_cast<uint32_t *>(value);
|
||||
break;
|
||||
case DT_GNU_HASH:
|
||||
gnu_hash = reinterpret_cast<uint32_t *>(value);
|
||||
break;
|
||||
case DT_SYMTAB:
|
||||
dynsym_ = reinterpret_cast<ElfW(Sym) *>(value);
|
||||
@@ -249,13 +251,38 @@ void ElfMemImage::Init(const void *base) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hash_ || !dynsym_ || !dynstr_ || !versym_ ||
|
||||
if ((!sysv_hash && !gnu_hash) || !dynsym_ || !dynstr_ || !versym_ ||
|
||||
!verdef_ || !verdefnum_ || !strsize_) {
|
||||
assert(false); // invalid VDSO
|
||||
// Mark this image as not present. Can not recur infinitely.
|
||||
Init(nullptr);
|
||||
return;
|
||||
}
|
||||
if (sysv_hash) {
|
||||
num_syms_ = sysv_hash[1];
|
||||
} else {
|
||||
assert(gnu_hash);
|
||||
// Compute the number of symbols for DT_GNU_HASH, which is specified by
|
||||
// https://sourceware.org/gnu-gabi/program-loading-and-dynamic-linking.txt
|
||||
uint32_t nbuckets = gnu_hash[0];
|
||||
// The buckets array is located after the header (4 uint32) and the bloom
|
||||
// filter (size_t array of gnu_hash[2] elements).
|
||||
uint32_t *buckets = gnu_hash + 4 + sizeof(size_t) / 4 * gnu_hash[2];
|
||||
// Find the chain of the last non-empty bucket.
|
||||
uint32_t idx = 0;
|
||||
for (uint32_t i = nbuckets; i > 0;) {
|
||||
idx = buckets[--i];
|
||||
if (idx != 0) break;
|
||||
}
|
||||
if (idx != 0) {
|
||||
// Find the last element of the chain, which has an odd value.
|
||||
// Add one to get the number of symbols.
|
||||
uint32_t *chain = buckets + nbuckets - gnu_hash[1];
|
||||
while (chain[idx++] % 2 == 0) {
|
||||
}
|
||||
}
|
||||
num_syms_ = idx;
|
||||
}
|
||||
}
|
||||
|
||||
bool ElfMemImage::LookupSymbol(const char *name,
|
||||
@@ -300,9 +327,9 @@ bool ElfMemImage::LookupSymbolByAddress(const void *address,
|
||||
return false;
|
||||
}
|
||||
|
||||
ElfMemImage::SymbolIterator::SymbolIterator(const void *const image, int index)
|
||||
: index_(index), image_(image) {
|
||||
}
|
||||
ElfMemImage::SymbolIterator::SymbolIterator(const void *const image,
|
||||
uint32_t index)
|
||||
: index_(index), image_(image) {}
|
||||
|
||||
const ElfMemImage::SymbolInfo *ElfMemImage::SymbolIterator::operator->() const {
|
||||
return &info_;
|
||||
@@ -335,7 +362,7 @@ ElfMemImage::SymbolIterator ElfMemImage::end() const {
|
||||
return SymbolIterator(this, GetNumSymbols());
|
||||
}
|
||||
|
||||
void ElfMemImage::SymbolIterator::Update(int increment) {
|
||||
void ElfMemImage::SymbolIterator::Update(uint32_t increment) {
|
||||
const ElfMemImage *image = reinterpret_cast<const ElfMemImage *>(image_);
|
||||
ABSL_RAW_CHECK(image->IsPresent() || increment == 0, "");
|
||||
if (!image->IsPresent()) {
|
||||
|
||||
15
Pods/abseil/absl/debugging/internal/elf_mem_image.h
generated
15
Pods/abseil/absl/debugging/internal/elf_mem_image.h
generated
@@ -22,6 +22,7 @@
|
||||
// Including this will define the __GLIBC__ macro if glibc is being
|
||||
// used.
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
@@ -82,10 +83,10 @@ class ElfMemImage {
|
||||
bool operator!=(const SymbolIterator &rhs) const;
|
||||
bool operator==(const SymbolIterator &rhs) const;
|
||||
private:
|
||||
SymbolIterator(const void *const image, int index);
|
||||
void Update(int incr);
|
||||
SymbolIterator(const void *const image, uint32_t index);
|
||||
void Update(uint32_t incr);
|
||||
SymbolInfo info_;
|
||||
int index_;
|
||||
uint32_t index_;
|
||||
const void *const image_;
|
||||
};
|
||||
|
||||
@@ -94,14 +95,14 @@ class ElfMemImage {
|
||||
void Init(const void *base);
|
||||
bool IsPresent() const { return ehdr_ != nullptr; }
|
||||
const ElfW(Phdr)* GetPhdr(int index) const;
|
||||
const ElfW(Sym)* GetDynsym(int index) const;
|
||||
const ElfW(Versym)* GetVersym(int index) const;
|
||||
const ElfW(Sym) * GetDynsym(uint32_t index) const;
|
||||
const ElfW(Versym)* GetVersym(uint32_t index) const;
|
||||
const ElfW(Verdef)* GetVerdef(int index) const;
|
||||
const ElfW(Verdaux)* GetVerdefAux(const ElfW(Verdef) *verdef) const;
|
||||
const char* GetDynstr(ElfW(Word) offset) const;
|
||||
const void* GetSymAddr(const ElfW(Sym) *sym) const;
|
||||
const char* GetVerstr(ElfW(Word) offset) const;
|
||||
int GetNumSymbols() const;
|
||||
uint32_t GetNumSymbols() const;
|
||||
|
||||
SymbolIterator begin() const;
|
||||
SymbolIterator end() const;
|
||||
@@ -124,8 +125,8 @@ class ElfMemImage {
|
||||
const ElfW(Sym) *dynsym_;
|
||||
const ElfW(Versym) *versym_;
|
||||
const ElfW(Verdef) *verdef_;
|
||||
const ElfW(Word) *hash_;
|
||||
const char *dynstr_;
|
||||
uint32_t num_syms_;
|
||||
size_t strsize_;
|
||||
size_t verdefnum_;
|
||||
ElfW(Addr) link_base_; // Link-time base (p_vaddr of first PT_LOAD).
|
||||
|
||||
320
Pods/abseil/absl/debugging/internal/examine_stack.cc
generated
Normal file
320
Pods/abseil/absl/debugging/internal/examine_stack.cc
generated
Normal file
@@ -0,0 +1,320 @@
|
||||
//
|
||||
// Copyright 2018 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/examine_stack.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
#ifdef ABSL_HAVE_MMAP
|
||||
#include <sys/mman.h>
|
||||
#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
#include <sys/ucontext.h>
|
||||
#endif
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/debugging/stacktrace.h"
|
||||
#include "absl/debugging/symbolize.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace debugging_internal {
|
||||
|
||||
namespace {
|
||||
constexpr int kDefaultDumpStackFramesLimit = 64;
|
||||
// The %p field width for printf() functions is two characters per byte,
|
||||
// and two extra for the leading "0x".
|
||||
constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
|
||||
|
||||
ABSL_CONST_INIT SymbolizeUrlEmitter debug_stack_trace_hook = nullptr;
|
||||
|
||||
// Async-signal safe mmap allocator.
|
||||
void* Allocate(size_t num_bytes) {
|
||||
#ifdef ABSL_HAVE_MMAP
|
||||
void* p = ::mmap(nullptr, num_bytes, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
return p == MAP_FAILED ? nullptr : p;
|
||||
#else
|
||||
(void)num_bytes;
|
||||
return nullptr;
|
||||
#endif // ABSL_HAVE_MMAP
|
||||
}
|
||||
|
||||
void Deallocate(void* p, size_t size) {
|
||||
#ifdef ABSL_HAVE_MMAP
|
||||
::munmap(p, size);
|
||||
#else
|
||||
(void)p;
|
||||
(void)size;
|
||||
#endif // ABSL_HAVE_MMAP
|
||||
}
|
||||
|
||||
// Print a program counter only.
|
||||
void DumpPC(OutputWriter* writer, void* writer_arg, void* const pc,
|
||||
const char* const prefix) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "%s@ %*p\n", prefix, kPrintfPointerFieldWidth, pc);
|
||||
writer(buf, writer_arg);
|
||||
}
|
||||
|
||||
// Print a program counter and the corresponding stack frame size.
|
||||
void DumpPCAndFrameSize(OutputWriter* writer, void* writer_arg, void* const pc,
|
||||
int framesize, const char* const prefix) {
|
||||
char buf[100];
|
||||
if (framesize <= 0) {
|
||||
snprintf(buf, sizeof(buf), "%s@ %*p (unknown)\n", prefix,
|
||||
kPrintfPointerFieldWidth, pc);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s@ %*p %9d\n", prefix,
|
||||
kPrintfPointerFieldWidth, pc, framesize);
|
||||
}
|
||||
writer(buf, writer_arg);
|
||||
}
|
||||
|
||||
// Print a program counter and the corresponding symbol.
|
||||
void DumpPCAndSymbol(OutputWriter* writer, void* writer_arg, void* const pc,
|
||||
const char* const prefix) {
|
||||
char tmp[1024];
|
||||
const char* symbol = "(unknown)";
|
||||
// Symbolizes the previous address of pc because pc may be in the
|
||||
// next function. The overrun happens when the function ends with
|
||||
// a call to a function annotated noreturn (e.g. CHECK).
|
||||
// If symbolization of pc-1 fails, also try pc on the off-chance
|
||||
// that we crashed on the first instruction of a function (that
|
||||
// actually happens very often for e.g. __restore_rt).
|
||||
const uintptr_t prev_pc = reinterpret_cast<uintptr_t>(pc) - 1;
|
||||
if (absl::Symbolize(reinterpret_cast<const char*>(prev_pc), tmp,
|
||||
sizeof(tmp)) ||
|
||||
absl::Symbolize(pc, tmp, sizeof(tmp))) {
|
||||
symbol = tmp;
|
||||
}
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf), "%s@ %*p %s\n", prefix, kPrintfPointerFieldWidth,
|
||||
pc, symbol);
|
||||
writer(buf, writer_arg);
|
||||
}
|
||||
|
||||
// Print a program counter, its stack frame size, and its symbol name.
|
||||
// Note that there is a separate symbolize_pc argument. Return addresses may be
|
||||
// at the end of the function, and this allows the caller to back up from pc if
|
||||
// appropriate.
|
||||
void DumpPCAndFrameSizeAndSymbol(OutputWriter* writer, void* writer_arg,
|
||||
void* const pc, void* const symbolize_pc,
|
||||
int framesize, const char* const prefix) {
|
||||
char tmp[1024];
|
||||
const char* symbol = "(unknown)";
|
||||
if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp))) {
|
||||
symbol = tmp;
|
||||
}
|
||||
char buf[1024];
|
||||
if (framesize <= 0) {
|
||||
snprintf(buf, sizeof(buf), "%s@ %*p (unknown) %s\n", prefix,
|
||||
kPrintfPointerFieldWidth, pc, symbol);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s@ %*p %9d %s\n", prefix,
|
||||
kPrintfPointerFieldWidth, pc, framesize, symbol);
|
||||
}
|
||||
writer(buf, writer_arg);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook) {
|
||||
debug_stack_trace_hook = hook;
|
||||
}
|
||||
|
||||
SymbolizeUrlEmitter GetDebugStackTraceHook() { return debug_stack_trace_hook; }
|
||||
|
||||
// Returns the program counter from signal context, nullptr if
|
||||
// unknown. vuc is a ucontext_t*. We use void* to avoid the use of
|
||||
// ucontext_t on non-POSIX systems.
|
||||
void* GetProgramCounter(void* const vuc) {
|
||||
#ifdef __linux__
|
||||
if (vuc != nullptr) {
|
||||
ucontext_t* context = reinterpret_cast<ucontext_t*>(vuc);
|
||||
#if defined(__aarch64__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.pc);
|
||||
#elif defined(__alpha__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.sc_pc);
|
||||
#elif defined(__arm__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.arm_pc);
|
||||
#elif defined(__hppa__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.sc_iaoq[0]);
|
||||
#elif defined(__i386__)
|
||||
if (14 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.gregs[14]);
|
||||
#elif defined(__ia64__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.sc_ip);
|
||||
#elif defined(__m68k__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
|
||||
#elif defined(__mips__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.pc);
|
||||
#elif defined(__powerpc64__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]);
|
||||
#elif defined(__powerpc__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.uc_regs->gregs[32]);
|
||||
#elif defined(__riscv)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.__gregs[REG_PC]);
|
||||
#elif defined(__s390__) && !defined(__s390x__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.psw.addr & 0x7fffffff);
|
||||
#elif defined(__s390__) && defined(__s390x__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.psw.addr);
|
||||
#elif defined(__sh__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.pc);
|
||||
#elif defined(__sparc__) && !defined(__arch64__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.gregs[19]);
|
||||
#elif defined(__sparc__) && defined(__arch64__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.mc_gregs[19]);
|
||||
#elif defined(__x86_64__)
|
||||
if (16 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
|
||||
#elif defined(__e2k__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.cr0_hi);
|
||||
#elif defined(__loongarch__)
|
||||
return reinterpret_cast<void*>(context->uc_mcontext.__pc);
|
||||
#else
|
||||
#error "Undefined Architecture."
|
||||
#endif
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
if (vuc != nullptr) {
|
||||
ucontext_t* signal_ucontext = reinterpret_cast<ucontext_t*>(vuc);
|
||||
#if defined(__aarch64__)
|
||||
return reinterpret_cast<void*>(
|
||||
__darwin_arm_thread_state64_get_pc(signal_ucontext->uc_mcontext->__ss));
|
||||
#elif defined(__arm__)
|
||||
#if __DARWIN_UNIX03
|
||||
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__pc);
|
||||
#else
|
||||
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.pc);
|
||||
#endif
|
||||
#elif defined(__i386__)
|
||||
#if __DARWIN_UNIX03
|
||||
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__eip);
|
||||
#else
|
||||
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.eip);
|
||||
#endif
|
||||
#elif defined(__x86_64__)
|
||||
#if __DARWIN_UNIX03
|
||||
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__rip);
|
||||
#else
|
||||
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.rip);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#elif defined(__akaros__)
|
||||
auto* ctx = reinterpret_cast<struct user_context*>(vuc);
|
||||
return reinterpret_cast<void*>(get_user_ctx_pc(ctx));
|
||||
#endif
|
||||
static_cast<void>(vuc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[],
|
||||
int frame_sizes[], int depth,
|
||||
int min_dropped_frames,
|
||||
bool symbolize_stacktrace,
|
||||
OutputWriter* writer, void* writer_arg) {
|
||||
if (pc != nullptr) {
|
||||
// We don't know the stack frame size for PC, use 0.
|
||||
if (symbolize_stacktrace) {
|
||||
DumpPCAndFrameSizeAndSymbol(writer, writer_arg, pc, pc, 0, "PC: ");
|
||||
} else {
|
||||
DumpPCAndFrameSize(writer, writer_arg, pc, 0, "PC: ");
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < depth; i++) {
|
||||
if (symbolize_stacktrace) {
|
||||
// Pass the previous address of pc as the symbol address because pc is a
|
||||
// return address, and an overrun may occur when the function ends with a
|
||||
// call to a function annotated noreturn (e.g. CHECK). Note that we don't
|
||||
// do this for pc above, as the adjustment is only correct for return
|
||||
// addresses.
|
||||
DumpPCAndFrameSizeAndSymbol(writer, writer_arg, stack[i],
|
||||
reinterpret_cast<char*>(stack[i]) - 1,
|
||||
frame_sizes[i], " ");
|
||||
} else {
|
||||
DumpPCAndFrameSize(writer, writer_arg, stack[i], frame_sizes[i], " ");
|
||||
}
|
||||
}
|
||||
if (min_dropped_frames > 0) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), " @ ... and at least %d more frames\n",
|
||||
min_dropped_frames);
|
||||
writer(buf, writer_arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Dump current stack trace as directed by writer.
|
||||
// Make sure this function is not inlined to avoid skipping too many top frames.
|
||||
ABSL_ATTRIBUTE_NOINLINE
|
||||
void DumpStackTrace(int min_dropped_frames, int max_num_frames,
|
||||
bool symbolize_stacktrace, OutputWriter* writer,
|
||||
void* writer_arg) {
|
||||
// Print stack trace
|
||||
void* stack_buf[kDefaultDumpStackFramesLimit];
|
||||
void** stack = stack_buf;
|
||||
int num_stack = kDefaultDumpStackFramesLimit;
|
||||
size_t allocated_bytes = 0;
|
||||
|
||||
if (num_stack >= max_num_frames) {
|
||||
// User requested fewer frames than we already have space for.
|
||||
num_stack = max_num_frames;
|
||||
} else {
|
||||
const size_t needed_bytes =
|
||||
static_cast<size_t>(max_num_frames) * sizeof(stack[0]);
|
||||
void* p = Allocate(needed_bytes);
|
||||
if (p != nullptr) { // We got the space.
|
||||
num_stack = max_num_frames;
|
||||
stack = reinterpret_cast<void**>(p);
|
||||
allocated_bytes = needed_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
int depth = absl::GetStackTrace(stack, num_stack, min_dropped_frames + 1);
|
||||
for (int i = 0; i < depth; i++) {
|
||||
if (symbolize_stacktrace) {
|
||||
DumpPCAndSymbol(writer, writer_arg, stack[static_cast<size_t>(i)],
|
||||
" ");
|
||||
} else {
|
||||
DumpPC(writer, writer_arg, stack[static_cast<size_t>(i)], " ");
|
||||
}
|
||||
}
|
||||
|
||||
auto hook = GetDebugStackTraceHook();
|
||||
if (hook != nullptr) {
|
||||
(*hook)(stack, depth, writer, writer_arg);
|
||||
}
|
||||
|
||||
if (allocated_bytes != 0) Deallocate(stack, allocated_bytes);
|
||||
}
|
||||
|
||||
} // namespace debugging_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
64
Pods/abseil/absl/debugging/internal/examine_stack.h
generated
Normal file
64
Pods/abseil/absl/debugging/internal/examine_stack.h
generated
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Copyright 2018 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.
|
||||
//
|
||||
|
||||
#ifndef ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace debugging_internal {
|
||||
|
||||
// Type of function used for printing in stack trace dumping, etc.
|
||||
// We avoid closures to keep things simple.
|
||||
typedef void OutputWriter(const char*, void*);
|
||||
|
||||
// RegisterDebugStackTraceHook() allows to register a single routine
|
||||
// `hook` that is called each time DumpStackTrace() is called.
|
||||
// `hook` may be called from a signal handler.
|
||||
typedef void (*SymbolizeUrlEmitter)(void* const stack[], int depth,
|
||||
OutputWriter* writer, void* writer_arg);
|
||||
|
||||
// Registration of SymbolizeUrlEmitter for use inside of a signal handler.
|
||||
// This is inherently unsafe and must be signal safe code.
|
||||
void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook);
|
||||
SymbolizeUrlEmitter GetDebugStackTraceHook();
|
||||
|
||||
// Returns the program counter from signal context, or nullptr if
|
||||
// unknown. `vuc` is a ucontext_t*. We use void* to avoid the use of
|
||||
// ucontext_t on non-POSIX systems.
|
||||
void* GetProgramCounter(void* const vuc);
|
||||
|
||||
// Uses `writer` to dump the program counter, stack trace, and stack
|
||||
// frame sizes.
|
||||
void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[],
|
||||
int frame_sizes[], int depth,
|
||||
int min_dropped_frames,
|
||||
bool symbolize_stacktrace,
|
||||
OutputWriter* writer, void* writer_arg);
|
||||
|
||||
// Dump current stack trace omitting the topmost `min_dropped_frames` stack
|
||||
// frames.
|
||||
void DumpStackTrace(int min_dropped_frames, int max_num_frames,
|
||||
bool symbolize_stacktrace, OutputWriter* writer,
|
||||
void* writer_arg);
|
||||
|
||||
} // namespace debugging_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
|
||||
@@ -89,6 +89,8 @@ struct StackInfo {
|
||||
|
||||
static bool InsideSignalStack(void** ptr, const StackInfo* stack_info) {
|
||||
uintptr_t comparable_ptr = reinterpret_cast<uintptr_t>(ptr);
|
||||
if (stack_info->sig_stack_high == kUnknownStackEnd)
|
||||
return false;
|
||||
return (comparable_ptr >= stack_info->sig_stack_low &&
|
||||
comparable_ptr < stack_info->sig_stack_high);
|
||||
}
|
||||
@@ -122,13 +124,6 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
|
||||
if (pre_signal_frame_pointer >= old_frame_pointer) {
|
||||
new_frame_pointer = pre_signal_frame_pointer;
|
||||
}
|
||||
// Check that alleged frame pointer is actually readable. This is to
|
||||
// prevent "double fault" in case we hit the first fault due to e.g.
|
||||
// stack corruption.
|
||||
if (!absl::debugging_internal::AddressIsReadable(
|
||||
new_frame_pointer))
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -136,6 +131,14 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
|
||||
if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 7) != 0)
|
||||
return nullptr;
|
||||
|
||||
// Check that alleged frame pointer is actually readable. This is to
|
||||
// prevent "double fault" in case we hit the first fault due to e.g.
|
||||
// stack corruption.
|
||||
if (!absl::debugging_internal::AddressIsReadable(
|
||||
new_frame_pointer))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Only check the size if both frames are in the same stack.
|
||||
if (InsideSignalStack(new_frame_pointer, stack_info) ==
|
||||
InsideSignalStack(old_frame_pointer, stack_info)) {
|
||||
|
||||
70
Pods/abseil/absl/debugging/internal/utf8_for_code_point.cc
generated
Normal file
70
Pods/abseil/absl/debugging/internal/utf8_for_code_point.cc
generated
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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/utf8_for_code_point.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace debugging_internal {
|
||||
namespace {
|
||||
|
||||
// UTF-8 encoding bounds.
|
||||
constexpr uint32_t kMinSurrogate = 0xd800, kMaxSurrogate = 0xdfff;
|
||||
constexpr uint32_t kMax1ByteCodePoint = 0x7f;
|
||||
constexpr uint32_t kMax2ByteCodePoint = 0x7ff;
|
||||
constexpr uint32_t kMax3ByteCodePoint = 0xffff;
|
||||
constexpr uint32_t kMaxCodePoint = 0x10ffff;
|
||||
|
||||
} // namespace
|
||||
|
||||
Utf8ForCodePoint::Utf8ForCodePoint(uint64_t code_point) {
|
||||
if (code_point <= kMax1ByteCodePoint) {
|
||||
length = 1;
|
||||
bytes[0] = static_cast<char>(code_point);
|
||||
return;
|
||||
}
|
||||
|
||||
if (code_point <= kMax2ByteCodePoint) {
|
||||
length = 2;
|
||||
bytes[0] = static_cast<char>(0xc0 | (code_point >> 6));
|
||||
bytes[1] = static_cast<char>(0x80 | (code_point & 0x3f));
|
||||
return;
|
||||
}
|
||||
|
||||
if (kMinSurrogate <= code_point && code_point <= kMaxSurrogate) return;
|
||||
|
||||
if (code_point <= kMax3ByteCodePoint) {
|
||||
length = 3;
|
||||
bytes[0] = static_cast<char>(0xe0 | (code_point >> 12));
|
||||
bytes[1] = static_cast<char>(0x80 | ((code_point >> 6) & 0x3f));
|
||||
bytes[2] = static_cast<char>(0x80 | (code_point & 0x3f));
|
||||
return;
|
||||
}
|
||||
|
||||
if (code_point > kMaxCodePoint) return;
|
||||
|
||||
length = 4;
|
||||
bytes[0] = static_cast<char>(0xf0 | (code_point >> 18));
|
||||
bytes[1] = static_cast<char>(0x80 | ((code_point >> 12) & 0x3f));
|
||||
bytes[2] = static_cast<char>(0x80 | ((code_point >> 6) & 0x3f));
|
||||
bytes[3] = static_cast<char>(0x80 | (code_point & 0x3f));
|
||||
}
|
||||
|
||||
} // namespace debugging_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
47
Pods/abseil/absl/debugging/internal/utf8_for_code_point.h
generated
Normal file
47
Pods/abseil/absl/debugging/internal/utf8_for_code_point.h
generated
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
#ifndef ABSL_DEBUGGING_INTERNAL_UTF8_FOR_CODE_POINT_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_UTF8_FOR_CODE_POINT_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace debugging_internal {
|
||||
|
||||
struct Utf8ForCodePoint {
|
||||
// Converts a Unicode code point to the corresponding UTF-8 byte sequence.
|
||||
// Async-signal-safe to support use in symbolizing stack traces from a signal
|
||||
// handler.
|
||||
explicit Utf8ForCodePoint(uint64_t code_point);
|
||||
|
||||
// Returns true if the constructor's code_point argument was valid.
|
||||
bool ok() const { return length != 0; }
|
||||
|
||||
// If code_point was in range, then 1 <= length <= 4, and the UTF-8 encoding
|
||||
// is found in bytes[0 .. (length - 1)]. If code_point was invalid, then
|
||||
// length == 0. In either case, the contents of bytes[length .. 3] are
|
||||
// unspecified.
|
||||
char bytes[4] = {};
|
||||
uint32_t length = 0;
|
||||
};
|
||||
|
||||
} // namespace debugging_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_UTF8_FOR_CODE_POINT_H_
|
||||
Reference in New Issue
Block a user