Skip to content
File

Blob: src/workerd/tools/param-extractor.rs

rust161 lines
1use std::ffi::OsStr;
2use std::fs::File;
3use std::io::BufRead;
4use std::io::BufReader;
5use std::io::BufWriter;
6use std::io::Write;
7use std::path::Path;
8 
9use anyhow::Result;
10use flate2::read::GzDecoder;
11use serde::Deserialize;
12use serde::Serialize;
13 
14/// Contains the declarations we care about
15#[derive(Deserialize, PartialEq, Debug)]
16enum Clang {
17 NamespaceDecl { name: Option<String> },
18 
19 // Function-like -- direct parents of parameters
20 FunctionDecl { name: Option<String> },
21 CXXMethodDecl { name: Option<String> },
22 CXXRecordDecl { name: Option<String> },
23 CXXConstructorDecl,
24 
25 // Parameter names
26 ParmVarDecl { name: Option<String> },
27 
28 // Everything else
29 Other { name: Option<String> },
30}
31 
32impl Clang {
33 fn is_function_like(&self) -> bool {
34 matches!(
35 *self,
36 Self::FunctionDecl { .. } | Self::CXXMethodDecl { .. } | Self::CXXConstructorDecl
37 )
38 }
39 
40 fn name(&self) -> Option<&str> {
41 match self {
42 Self::NamespaceDecl { name }
43 | Self::FunctionDecl { name }
44 | Self::CXXMethodDecl { name }
45 | Self::CXXRecordDecl { name }
46 | Self::ParmVarDecl { name }
47 | Self::Other { name } => name.as_ref().map(AsRef::as_ref),
48 Self::CXXConstructorDecl => Some("constructor"),
49 }
50 }
51}
52 
53type ClangNode = clang_ast::Node<Clang>;
54 
55fn main() -> Result<()> {
56 let mut args = pico_args::Arguments::from_env();
57 
58 let clang_ast = args.value_from_os_str("--input", |path_str| {
59 let path = Path::new(path_str);
60 let file = File::open(path)?;
61 let serde = {
62 let reader: &mut dyn BufRead = {
63 if Some("gz") == path.extension().and_then(OsStr::to_str) {
64 &mut BufReader::new(GzDecoder::new(file))
65 } else {
66 &mut BufReader::new(file)
67 }
68 };
69 
70 let mut deserializer = serde_json::Deserializer::from_reader(reader);
71 // Note: serde_json doesn't support custom recursion limits, only disabling.
72 // We disable the limit to handle deeply nested AST structures (default 128 is
73 // insufficient for the clang AST dump, which can be deeply nested).
74 deserializer.disable_recursion_limit();
75 ClangNode::deserialize(&mut deserializer)
76 };
77 serde.map_err(anyhow::Error::from)
78 })?;
79 
80 let value = get_parameter_names(clang_ast);
81 
82 let mut writer =
83 args.value_from_os_str("--output", |path| File::create(path).map(BufWriter::new))?;
84 
85 serde_json::to_writer(&mut writer, &value)?;
86 writer.flush()?;
87 
88 Ok(())
89}
90 
91fn get_parameter_names(clang_ast: ClangNode) -> Vec<Parameter> {
92 let workerd_namespace = Clang::NamespaceDecl {
93 name: Some("workerd".to_owned()),
94 };
95 
96 clang_ast
97 .inner
98 .into_iter()
99 .filter(|node| node.kind == workerd_namespace)
100 .flat_map(|node| traverse_disambiguous(node, &[]))
101 .collect()
102}
103 
104#[derive(Serialize, Debug)]
105struct Parameter {
106 fully_qualified_parent_name: Vec<String>,
107 function_like_name: String,
108 index: usize,
109 name: String,
110}
111 
112fn traverse_disambiguous(
113 disambiguous: ClangNode,
114 fully_qualified_parent_name: &[String],
115) -> Vec<Parameter> {
116 let disambiguous_name = disambiguous
117 .kind
118 .name()
119 .map(ToOwned::to_owned)
120 .unwrap_or_default();
121 
122 disambiguous
123 .inner
124 .into_iter()
125 .flat_map(|node| {
126 let mut qualified: Vec<_> = fully_qualified_parent_name.to_vec();
127 qualified.push(disambiguous_name.clone());
128 if node.kind.is_function_like() {
129 traverse_function_like(node, &qualified)
130 } else {
131 traverse_disambiguous(node, &qualified)
132 }
133 })
134 .collect()
135}
136 
137fn traverse_function_like(
138 node: ClangNode,
139 fully_qualified_parent_name: &[String],
140) -> Vec<Parameter> {
141 let function_like_name = node.kind.name().expect("missing name").to_owned();
142 
143 node.inner
144 .into_iter()
145 .filter_map(|child| {
146 if let Clang::ParmVarDecl { name: Some(name) } = child.kind {
147 Some(name)
148 } else {
149 None
150 }
151 })
152 .enumerate()
153 .map(|(i, param_name)| Parameter {
154 fully_qualified_parent_name: fully_qualified_parent_name.to_vec(),
155 function_like_name: function_like_name.clone(),
156 index: i,
157 name: param_name,
158 })
159 .collect()
160}