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
//! inline 関数辞書
//!
//! is_target なヘッダーファイルに含まれる inline 関数を収集し、
//! 型推論と Rust コード生成に活用する。
use std::collections::{HashMap, HashSet};
use crate::apidoc_patches::ApidocPatchSet;
use crate::ast::FunctionDef;
use crate::intern::{InternedStr, StringInterner};
use crate::macro_infer::{convert_assert_calls_in_compound_stmt, MacroInferContext};
/// inline 関数辞書
///
/// FunctionDef をそのまま保持し、型情報は AST から直接取得する。
/// 各 inline 関数の呼び出し先(called_functions)と利用可能性も追跡する。
#[derive(Debug, Default)]
pub struct InlineFnDict {
fns: HashMap<InternedStr, FunctionDef>,
/// 各 inline 関数の呼び出し先
called_functions: HashMap<InternedStr, HashSet<InternedStr>>,
/// 利用不可関数の呼び出しを含む inline 関数の集合
calls_unavailable: HashSet<InternedStr>,
/// apidoc patches / skip-list で skip_codegen 指定された inline 関数の集合
///
/// 直接の skip 対象にしか入れない。伝播では `is_unavailable_for_codegen()`
/// で `calls_unavailable` と OR を取って参照する。
apidoc_suppressed: HashSet<InternedStr>,
}
impl InlineFnDict {
/// 新しい辞書を作成
pub fn new() -> Self {
Self::default()
}
/// inline 関数を登録
pub fn insert(&mut self, name: InternedStr, func_def: FunctionDef) {
self.fns.insert(name, func_def);
}
/// inline 関数を取得
pub fn get(&self, name: InternedStr) -> Option<&FunctionDef> {
self.fns.get(&name)
}
/// 全ての inline 関数を走査
pub fn iter(&self) -> impl Iterator<Item = (&InternedStr, &FunctionDef)> {
self.fns.iter()
}
/// inline 関数の数
pub fn len(&self) -> usize {
self.fns.len()
}
/// 辞書が空かどうか
pub fn is_empty(&self) -> bool {
self.fns.is_empty()
}
/// inline 関数の呼び出し先を取得
pub fn get_called_functions(&self, name: InternedStr) -> Option<&HashSet<InternedStr>> {
self.called_functions.get(&name)
}
/// 利用不可関数を呼び出すかどうか
pub fn is_calls_unavailable(&self, name: InternedStr) -> bool {
self.calls_unavailable.contains(&name)
}
/// 利用不可フラグを設定
pub fn set_calls_unavailable(&mut self, name: InternedStr) {
self.calls_unavailable.insert(name);
}
/// apidoc skip_codegen 対象かどうか
pub fn is_apidoc_suppressed(&self, name: InternedStr) -> bool {
self.apidoc_suppressed.contains(&name)
}
/// apidoc skip_codegen フラグを設定
pub fn set_apidoc_suppressed(&mut self, name: InternedStr) {
self.apidoc_suppressed.insert(name);
}
/// 出力可否の総合判定
///
/// `calls_unavailable`(不在関数を呼ぶ/推移的)または
/// `apidoc_suppressed`(自分が skip_codegen 対象)のいずれかが立っていれば
/// codegen 対象外。
pub fn is_unavailable_for_codegen(&self, name: InternedStr) -> bool {
self.is_calls_unavailable(name) || self.is_apidoc_suppressed(name)
}
/// apidoc skip_codegen を `apidoc_suppressed` 集合に反映
///
/// `patches.skip_codegen` の各エントリ名を interner で解決し、
/// 該当する inline 関数が辞書に登録されていれば `apidoc_suppressed`
/// に追加する。マッチした関数数を返す(マクロ側のマッチは
/// `MacroInferContext::apply_apidoc_suppressions` が別途扱う)。
pub fn apply_apidoc_suppressions(
&mut self,
patches: &ApidocPatchSet,
interner: &StringInterner,
) -> usize {
let mut count = 0usize;
for name_str in patches.skip_codegen.keys() {
if let Some(interned) = interner.lookup(name_str) {
if self.fns.contains_key(&interned) {
self.apidoc_suppressed.insert(interned);
count += 1;
}
}
}
count
}
/// called_functions の全エントリを走査
pub fn called_functions_iter(&self) -> impl Iterator<Item = (&InternedStr, &HashSet<InternedStr>)> {
self.called_functions.iter()
}
/// FunctionDef から inline 関数を収集
///
/// `inline` または `static`(内部リンケージ)の関数を対象とする。
/// `STATIC` (= `static`) のみで `inline` でない関数も翻訳単位ローカルなため
/// Rust 側に独自に持つ意味論的問題はない(`bodies_by_type` 配列と同じ理屈)。
/// 例: `perlstatic.h` の `Perl_croak_memory_wrap` (STATIC void) を取り込む
/// ことで、これを呼ぶ inline 関数 (`Perl_newSV_type` 等) のカスケード解消に
/// 寄与する。
///
/// assert/assert_ 呼び出しを Assert 式に変換してから保存する。
/// 関数呼び出し先(called_functions)も同時に収集する。
pub fn collect_from_function_def(&mut self, func_def: &FunctionDef, interner: &StringInterner) {
let is_static = func_def.specs.storage == Some(crate::ast::StorageClass::Static);
if !func_def.specs.is_inline && !is_static {
return;
}
let name = match func_def.declarator.name {
Some(n) => n,
None => return,
};
// クローンして assert 呼び出しを変換
let mut func_def = func_def.clone();
convert_assert_calls_in_compound_stmt(&mut func_def.body, interner);
// 関数呼び出し先を収集
let mut calls = HashSet::new();
MacroInferContext::collect_function_calls_from_block_items(
&func_def.body.items,
&mut calls,
);
self.called_functions.insert(name, calls);
self.insert(name, func_def);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inline_fn_dict_new() {
let dict = InlineFnDict::new();
assert!(dict.is_empty());
assert_eq!(dict.len(), 0);
}
}