use std::mem::size_of;
use std::rc::Rc;
use itertools::Itertools;
use rustc_hash::FxHashSet;
use yara_x_parser::Span;
use yara_x_parser::ast::{Ident, WithSpan};
use crate::compiler::errors::{CompileError, UnknownPattern};
use crate::compiler::ir::{IR, PatternIdx};
use crate::compiler::report::ReportBuilder;
use crate::compiler::{Warnings, ir};
use crate::errors::{UnknownField, UnknownIdentifier};
use crate::modules::BUILTIN_MODULES;
use crate::symbols::{StackedSymbolTable, Symbol, SymbolLookup};
use crate::types::Type;
use crate::wasm;
pub(crate) struct CompileContext<'a, 'src> {
pub report_builder: &'a ReportBuilder,
pub ir: &'a mut IR,
pub symbol_table: &'a mut StackedSymbolTable,
pub one_shot_symbol_table: Option<Rc<dyn SymbolLookup + 'a>>,
pub current_rule_patterns: &'a mut Vec<ir::PatternInRule<'src>>,
pub warnings: &'a mut Warnings,
pub features: &'a FxHashSet<String>,
pub vars: VarStack,
pub relaxed_re_syntax: bool,
pub error_on_slow_loop: bool,
pub for_of_depth: usize,
pub loop_iteration_multiplier: i64,
}
impl<'src> CompileContext<'_, 'src> {
pub fn get_pattern_mut(
&mut self,
ident: &Ident,
) -> Result<(PatternIdx, &mut ir::PatternInRule<'src>), CompileError> {
debug_assert!(
"$#@!".contains(
ident
.name
.chars()
.next()
.expect("identifier must be at least 1 character long")
)
);
self.current_rule_patterns
.iter_mut()
.find_position(|p| p.identifier().name[1..] == ident.name[1..])
.map(|(pos, pattern)| (PatternIdx::from(pos), pattern))
.ok_or_else(|| {
UnknownPattern::build(
self.report_builder,
ident.name.to_string(),
self.report_builder.span_to_code_loc(ident.span()),
)
})
}
pub fn lookup(&mut self, ident: &Ident) -> Result<Symbol, CompileError> {
let symbol_table = self.one_shot_symbol_table.take();
let symbol = if let Some(symbol_table) = &symbol_table {
symbol_table.lookup(ident.name)
} else {
self.symbol_table.lookup(ident.name)
};
if symbol.is_none() {
return if symbol_table.is_none() {
let mut err = UnknownIdentifier::build(
self.report_builder,
ident.name.to_string(),
self.report_builder.span_to_code_loc(ident.span()),
if BUILTIN_MODULES.contains_key(ident.name) {
Some(format!(
"there is a module named `{}`, but the `import \"{}\"` statement is missing",
ident.name, ident.name
))
} else {
None
},
);
if BUILTIN_MODULES.contains_key(ident.name) {
err.report_mut().patch(
self.report_builder.span_to_code_loc(Span(0..0)),
format!("import \"{}\"\n", ident.name),
);
}
Err(err)
} else {
Err(UnknownField::build(
self.report_builder,
ident.name.to_string(),
self.report_builder.span_to_code_loc(ident.span()),
))
};
}
Ok(symbol.unwrap())
}
}
pub(crate) struct VarStack {
frame_id: usize,
used: i32,
}
impl VarStack {
pub const OF_FRAME_SIZE: i32 = 5;
pub const FOR_OF_FRAME_SIZE: i32 = 5;
pub const FOR_IN_FRAME_SIZE: i32 = 7;
pub fn new() -> Self {
Self { used: 0, frame_id: 0 }
}
#[cfg(test)]
pub fn used(&self) -> i32 {
self.used
}
pub fn new_frame(&mut self, capacity: i32) -> VarStackFrame {
let start = self.used;
self.used += capacity;
self.frame_id += 1;
if self.used > wasm::MAX_VARS {
panic!("variables stack overflow");
}
VarStackFrame { frame_id: self.frame_id, start, capacity, used: 0 }
}
pub fn unwind(&mut self, frame: &VarStackFrame) {
if self.used < frame.start {
panic!("double-free in VarStack")
}
self.used = frame.start;
}
}
#[derive(Clone, Debug)]
pub(crate) struct VarStackFrame {
frame_id: usize,
start: i32,
capacity: i32,
used: i32,
}
impl VarStackFrame {
pub fn new_var(&mut self, ty: Type) -> Var {
if self.used == self.capacity {
panic!("VarStack exceeding its capacity: {}", self.capacity);
}
let index = self.used + self.start;
self.used += 1;
Var { frame_id: self.frame_id, ty, index }
}
}
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
pub(crate) struct Var {
frame_id: usize,
ty: Type,
index: i32,
}
impl Var {
pub fn new(frame_id: usize, ty: Type, index: i32) -> Self {
Self { frame_id, ty, index }
}
pub const fn mem_size() -> i32 {
size_of::<i64>() as i32
}
pub fn shift(&mut self, from_index: i32, shift_amount: i32) {
if self.index >= from_index {
self.index += shift_amount;
}
if self.index >= wasm::MAX_VARS {
panic!("variables stack overflow during shift");
}
}
#[cfg(test)]
pub fn frame_id(&self) -> usize {
self.frame_id
}
#[inline]
pub fn ty(&self) -> Type {
self.ty
}
#[inline]
pub fn index(&self) -> i32 {
self.index
}
}