1use std::collections::{BTreeMap, BTreeSet};
2
3use crate::parser::{AstExpression, BinaryOp, SourceSpan, UnaryOp};
4use regex::{Regex, RegexBuilder};
5
6use crate::sema::profile::{BodyNeedSummary, SecurityProfile};
7use crate::sema::schema::{CapabilityMeta, CapabilityTicket, RegexFlavor};
8
9#[derive(Debug, Clone)]
10pub struct VerifiedProgram {
11 ast: AstExpression,
12 root: VerifiedExpression,
13 profile: SecurityProfile,
14 body_need: BodyNeedSummary,
15 static_cost_upper_bound: u64,
16 regex_literals: Vec<RegexLiteral>,
17 regex_cache: CompiledRegexCache,
18 required_capabilities: BTreeSet<CapabilityTicket>,
19 required_capability_metadata: BTreeMap<CapabilityTicket, CapabilityMeta>,
20}
21
22impl VerifiedProgram {
23 pub(crate) fn new(parts: VerifiedProgramParts) -> Self {
24 Self {
25 ast: parts.ast,
26 root: parts.root,
27 profile: parts.profile,
28 body_need: parts.body_need,
29 static_cost_upper_bound: parts.static_cost_upper_bound,
30 regex_literals: parts.regex_literals,
31 regex_cache: parts.regex_cache,
32 required_capabilities: parts.required_capabilities,
33 required_capability_metadata: parts.required_capability_metadata,
34 }
35 }
36
37 pub fn ast(&self) -> &AstExpression {
38 &self.ast
39 }
40
41 pub fn root(&self) -> &VerifiedExpression {
42 &self.root
43 }
44
45 pub fn profile(&self) -> &SecurityProfile {
46 &self.profile
47 }
48
49 pub fn body_need(&self) -> BodyNeedSummary {
50 self.body_need
51 }
52
53 pub fn static_cost_upper_bound(&self) -> u64 {
54 self.static_cost_upper_bound
55 }
56
57 pub fn regex_literals(&self) -> &[RegexLiteral] {
58 &self.regex_literals
59 }
60
61 pub fn regex_cache(&self) -> &CompiledRegexCache {
62 &self.regex_cache
63 }
64
65 pub fn required_capabilities(&self) -> &BTreeSet<CapabilityTicket> {
66 &self.required_capabilities
67 }
68
69 pub fn required_capability_metadata(&self) -> &BTreeMap<CapabilityTicket, CapabilityMeta> {
70 &self.required_capability_metadata
71 }
72}
73
74pub(crate) struct VerifiedProgramParts {
75 pub ast: AstExpression,
76 pub root: VerifiedExpression,
77 pub profile: SecurityProfile,
78 pub body_need: BodyNeedSummary,
79 pub static_cost_upper_bound: u64,
80 pub regex_literals: Vec<RegexLiteral>,
81 pub regex_cache: CompiledRegexCache,
82 pub required_capabilities: BTreeSet<CapabilityTicket>,
83 pub required_capability_metadata: BTreeMap<CapabilityTicket, CapabilityMeta>,
84}
85
86#[derive(Debug, Clone)]
87pub struct CompiledExpression {
88 verified: VerifiedProgram,
89}
90
91impl CompiledExpression {
92 pub(crate) fn new(verified: VerifiedProgram) -> Self {
93 Self { verified }
94 }
95
96 pub fn ast(&self) -> &AstExpression {
97 self.verified.ast()
98 }
99
100 pub fn into_ast(self) -> AstExpression {
101 self.verified.ast
102 }
103
104 pub fn verified_program(&self) -> &VerifiedProgram {
105 &self.verified
106 }
107
108 pub fn into_verified_program(self) -> VerifiedProgram {
109 self.verified
110 }
111}
112
113#[derive(Debug, Clone)]
114pub struct VerifiedExpression {
115 kind: VerifiedExprKind,
116 span: SourceSpan,
117 capability_ticket: Option<CapabilityTicket>,
118}
119
120impl VerifiedExpression {
121 pub(crate) fn new(kind: VerifiedExprKind, span: SourceSpan) -> Self {
122 Self {
123 kind,
124 span,
125 capability_ticket: None,
126 }
127 }
128
129 pub(crate) fn with_capability_ticket(mut self, ticket: CapabilityTicket) -> Self {
130 self.capability_ticket = Some(ticket);
131 self
132 }
133
134 pub fn span(&self) -> SourceSpan {
135 self.span
136 }
137
138 pub fn capability_ticket(&self) -> Option<&CapabilityTicket> {
139 self.capability_ticket.as_ref()
140 }
141
142 pub fn kind(&self) -> VerifiedExprKindRef<'_> {
143 match &self.kind {
144 VerifiedExprKind::Null => VerifiedExprKindRef::Null,
145 VerifiedExprKind::Bool(value) => VerifiedExprKindRef::Bool(*value),
146 VerifiedExprKind::Int(value) => VerifiedExprKindRef::Int(*value),
147 VerifiedExprKind::Float(value) => VerifiedExprKindRef::Float(*value),
148 VerifiedExprKind::String(value) => VerifiedExprKindRef::String(value),
149 VerifiedExprKind::Array(items) => VerifiedExprKindRef::Array(items),
150 VerifiedExprKind::Identifier(name) => VerifiedExprKindRef::Identifier(name),
151 VerifiedExprKind::Member { receiver, name } => VerifiedExprKindRef::Member { receiver, name },
152 VerifiedExprKind::FunctionCall { name, args } => {
153 VerifiedExprKindRef::FunctionCall { name, args }
154 }
155 VerifiedExprKind::ExpressionFunctionCall {
156 name,
157 params,
158 args,
159 body,
160 } => VerifiedExprKindRef::ExpressionFunctionCall {
161 name,
162 params,
163 args,
164 body,
165 },
166 VerifiedExprKind::MethodCall {
167 receiver,
168 name,
169 args,
170 } => VerifiedExprKindRef::MethodCall {
171 receiver,
172 name,
173 args,
174 },
175 VerifiedExprKind::Unary { op, expr } => VerifiedExprKindRef::Unary { op: *op, expr },
176 VerifiedExprKind::Binary { left, op, right } => VerifiedExprKindRef::Binary {
177 left,
178 op: *op,
179 right,
180 },
181 }
182 }
183}
184
185#[derive(Debug, Clone)]
186pub(crate) enum VerifiedExprKind {
187 Null,
188 Bool(bool),
189 Int(i64),
190 Float(f64),
191 String(String),
192 Array(Vec<VerifiedExpression>),
193 Identifier(String),
194 Member {
195 receiver: Box<VerifiedExpression>,
196 name: String,
197 },
198 FunctionCall {
199 name: String,
200 args: Vec<VerifiedExpression>,
201 },
202 ExpressionFunctionCall {
203 name: String,
204 params: Vec<String>,
205 args: Vec<VerifiedExpression>,
206 body: Box<VerifiedExpression>,
207 },
208 MethodCall {
209 receiver: Box<VerifiedExpression>,
210 name: String,
211 args: Vec<VerifiedExpression>,
212 },
213 Unary {
214 op: UnaryOp,
215 expr: Box<VerifiedExpression>,
216 },
217 Binary {
218 left: Box<VerifiedExpression>,
219 op: BinaryOp,
220 right: Box<VerifiedExpression>,
221 },
222}
223
224pub enum VerifiedExprKindRef<'a> {
225 Null,
226 Bool(bool),
227 Int(i64),
228 Float(f64),
229 String(&'a str),
230 Array(&'a [VerifiedExpression]),
231 Identifier(&'a str),
232 Member {
233 receiver: &'a VerifiedExpression,
234 name: &'a str,
235 },
236 FunctionCall {
237 name: &'a str,
238 args: &'a [VerifiedExpression],
239 },
240 ExpressionFunctionCall {
241 name: &'a str,
242 params: &'a [String],
243 args: &'a [VerifiedExpression],
244 body: &'a VerifiedExpression,
245 },
246 MethodCall {
247 receiver: &'a VerifiedExpression,
248 name: &'a str,
249 args: &'a [VerifiedExpression],
250 },
251 Unary {
252 op: UnaryOp,
253 expr: &'a VerifiedExpression,
254 },
255 Binary {
256 left: &'a VerifiedExpression,
257 op: BinaryOp,
258 right: &'a VerifiedExpression,
259 },
260}
261
262#[derive(Debug, Clone, Eq, PartialEq)]
263pub struct RegexLiteral {
264 pub pattern: String,
265 pub flavor: RegexFlavor,
266 pub span: SourceSpan,
267}
268
269#[derive(Debug, Clone, Default)]
270pub struct CompiledRegexCache {
271 default: BTreeMap<String, Regex>,
272 header_name: BTreeMap<String, Regex>,
273}
274
275impl CompiledRegexCache {
276 pub fn insert(&mut self, literal: &RegexLiteral) -> Result<(), regex::Error> {
277 let target = match literal.flavor {
278 RegexFlavor::Default => &mut self.default,
279 RegexFlavor::HeaderName => &mut self.header_name,
280 };
281 if target.contains_key(&literal.pattern) {
282 return Ok(());
283 }
284 target.insert(literal.pattern.clone(), compile_regex(literal)?);
285 Ok(())
286 }
287
288 pub fn get(&self, flavor: RegexFlavor, pattern: &str) -> Option<&Regex> {
289 match flavor {
290 RegexFlavor::Default => self.default.get(pattern),
291 RegexFlavor::HeaderName => self.header_name.get(pattern),
292 }
293 }
294
295 pub fn is_match(&self, flavor: RegexFlavor, pattern: &str, haystack: &str) -> Option<bool> {
296 self
297 .get(flavor, pattern)
298 .map(|regex| regex.is_match(haystack))
299 }
300
301 pub fn len(&self) -> usize {
302 self.default.len() + self.header_name.len()
303 }
304
305 pub fn is_empty(&self) -> bool {
306 self.len() == 0
307 }
308}
309
310fn compile_regex(literal: &RegexLiteral) -> Result<Regex, regex::Error> {
311 match literal.flavor {
312 RegexFlavor::Default => Regex::new(&literal.pattern),
313 RegexFlavor::HeaderName => RegexBuilder::new(&literal.pattern)
314 .case_insensitive(true)
315 .build(),
316 }
317}