1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//! 用于符号的客户端内部。
//!
//! 这大致基于 `rustc_span` 的符号 interner 和 `rustc_arena` 的 DroplessArena。
//! 不幸的是,它是一个完整的 copy/re-implementation 而不是一个依赖项,因为在 `proc_macro` 中很难依赖 crates,因为它是与 `std` 同时构建的。
//!
//!
//! 如果在 future 中的某个时刻向 proc_macro 添加依赖项变得更容易,则可能应该删除或简化此模块。
//!
//!
//!

use std::cell::RefCell;
use std::num::NonZeroU32;
use std::str;

use super::*;

/// 存储在内部内部的符号字符串的句柄。
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Symbol(NonZeroU32);

impl !Send for Symbol {}
impl !Sync for Symbol {}

impl Symbol {
    /// 试用新的 `Symbol`
    pub(crate) fn new(string: &str) -> Self {
        INTERNER.with_borrow_mut(|i| i.intern(string))
    }

    /// 为标识符创建一个新的 `Symbol`。
    ///
    /// 在将其转换为符号之前对其进行验证和规范化。
    pub(crate) fn new_ident(string: &str, is_raw: bool) -> Self {
        // 快速路径: 检查这是否是有效的 ASCII 标识符
        if Self::is_valid_ascii_ident(string.as_bytes()) {
            if is_raw && !Self::can_be_raw(string) {
                panic!("`{}` cannot be a raw identifier", string);
            }
            return Self::new(string);
        }

        // 慢路径: 如果字符串已经是 ASCII,我们就完成了,否则请我们的服务器通过 RPC 为我们做这件事。
        //
        // 我们不需要在这里检查不能是原始的标识符,因为它们都是 ASCII。
        //
        if string.is_ascii() {
            Err(())
        } else {
            client::Symbol::normalize_and_validate_ident(string)
        }
        .unwrap_or_else(|_| panic!("`{:?}` is not a valid identifier", string))
    }

    /// 使用符号的字符串值运行回调。
    pub(crate) fn with<R>(self, f: impl FnOnce(&str) -> R) -> R {
        INTERNER.with_borrow(|i| f(i.get(self)))
    }

    /// 清除线程局部符号内部,使所有先前创建的符号无效,这样 `with` 会在调用它们时发生 panic。
    ///
    pub(crate) fn invalidate_all() {
        INTERNER.with_borrow_mut(|i| i.clear());
    }

    /// 检查 ident 是否是有效的 ASCII 标识符。
    ///
    /// 这是一个短路,在 proc - 宏客户端中实现起来很便宜,可以在创建简单标识时避免 RPC,但如果它包含非 ASCII 字符,则可能返回 `false` 作为有效标识符。
    ///
    ///
    ///
    fn is_valid_ascii_ident(bytes: &[u8]) -> bool {
        matches!(bytes.first(), Some(b'_' | b'a'..=b'z' | b'A'..=b'Z'))
            && bytes[1..]
                .iter()
                .all(|b| matches!(b, b'_' | b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9'))
    }

    // 从 `rustc_span` 模仿 `Symbol::can_be_raw` 的行为
    fn can_be_raw(string: &str) -> bool {
        match string {
            "_" | "super" | "self" | "Self" | "crate" => false,
            _ => true,
        }
    }
}

impl fmt::Debug for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.with(|s| fmt::Debug::fmt(s, f))
    }
}

impl ToString for Symbol {
    fn to_string(&self) -> String {
        self.with(|s| s.to_owned())
    }
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.with(|s| fmt::Display::fmt(s, f))
    }
}

impl<S> Encode<S> for Symbol {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.with(|sym| sym.encode(w, s))
    }
}

impl<S: server::Server> DecodeMut<'_, '_, client::HandleStore<server::MarkedTypes<S>>>
    for Marked<S::Symbol, Symbol>
{
    fn decode(r: &mut Reader<'_>, s: &mut client::HandleStore<server::MarkedTypes<S>>) -> Self {
        Mark::mark(S::intern_symbol(<&str>::decode(r, s)))
    }
}

impl<S: server::Server> Encode<client::HandleStore<server::MarkedTypes<S>>>
    for Marked<S::Symbol, Symbol>
{
    fn encode(self, w: &mut Writer, s: &mut client::HandleStore<server::MarkedTypes<S>>) {
        S::with_symbol_string(&self.unmark(), |sym| sym.encode(w, s))
    }
}

impl<S> DecodeMut<'_, '_, S> for Symbol {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        Symbol::new(<&str>::decode(r, s))
    }
}

thread_local! {
    static INTERNER: RefCell<Interner> = RefCell::new(Interner {
        arena: arena::Arena::new(),
        names: fxhash::FxHashMap::default(),
        strings: Vec::new(),
        // 从 1 开始,以确保 `NonZeroU32` 正常工作。
        sym_base: NonZeroU32::new(1).unwrap(),
    });
}

/// `Symbol` 的基本内部人员,灵感来自 `rustc_span` 中的内部人员。
struct Interner {
    arena: arena::Arena,
    // SAFETY: 这些 `'static` 生命周期实际上是引用到 Arena 拥有所有权的数据。
    // 这是安全的,因为我们从不将它们作为来自 `Interner` 的静态引用返回。
    //
    names: fxhash::FxHashMap<&'static str, Symbol>,
    strings: Vec<&'static str>,
    // 应用于存储在内部的符号名称的偏移量。
    // 这用于确保在内部清除后不会重新使用符号名称。
    //
    sym_base: NonZeroU32,
}

impl Interner {
    fn intern(&mut self, string: &str) -> Symbol {
        if let Some(&name) = self.names.get(string) {
            return name;
        }

        let name = Symbol(
            self.sym_base
                .checked_add(self.strings.len() as u32)
                .expect("`proc_macro` symbol name overflow"),
        );

        let string: &str = self.arena.alloc_str(string);

        // SAFETY: 我们可以将 arena 分配扩展到 `'static`,因为我们只在 arena 还活着的时候访问它们。
        //
        let string: &'static str = unsafe { &*(string as *const str) };
        self.strings.push(string);
        self.names.insert(string, name);
        name
    }

    /// 持有时从存储中读取符号的值。
    fn get(&self, symbol: Symbol) -> &str {
        // NOTE: 减去添加的偏移量以使符号非零并防止符号名称重用。
        //
        let name = symbol
            .0
            .get()
            .checked_sub(self.sym_base.get())
            .expect("use-after-free of `proc_macro` symbol");
        self.strings[name as usize]
    }

    /// 清除商店中的所有符号,使它们无效,这样如果在 future 中访问它们,`get` 就会 panic。
    ///
    fn clear(&mut self) {
        // NOTE: 请注意不要在这里恐慌,因为当未安装 `catch_unwind` 时,我们可能会在客户端上调用。
        //
        self.sym_base = self.sym_base.saturating_add(self.strings.len() as u32);
        self.names.clear();
        self.strings.clear();

        // SAFETY: 这在名称和字符串表被清除后被清除,因此不应保留对竞技场的引用。
        //
        self.arena = arena::Arena::new();
    }
}