use std::ffi::CString;
use std::os::raw::c_void;
use std::sync::Once;
use vala_sys as ffi;
use crate::object::RawWrapper;
use crate::{
Class, CodeNode, Constant, Delegate, Enum, Field, Interface, MemberAccess, Method, MethodCall,
Namespace, Property, Signal, Struct,
};
pub struct Walker {
visitor: *mut ffi::ValaCodeVisitor,
}
impl Walker {
pub fn walk_children<N>(&self, node: &N)
where
N: AsRef<CodeNode>,
{
unsafe {
ffi::vala_code_node_accept_children(node.as_ref().as_raw(), self.visitor);
}
}
}
#[allow(unused_variables)]
pub trait Visitor {
fn visit_namespace(&mut self, w: &Walker, node: &Namespace) {
w.walk_children(node);
}
fn visit_class(&mut self, w: &Walker, node: &Class) {
w.walk_children(node);
}
fn visit_struct(&mut self, w: &Walker, node: &Struct) {
w.walk_children(node);
}
fn visit_interface(&mut self, w: &Walker, node: &Interface) {
w.walk_children(node);
}
fn visit_enum(&mut self, w: &Walker, node: &Enum) {
w.walk_children(node);
}
fn visit_method(&mut self, w: &Walker, node: &Method) {
w.walk_children(node);
}
fn visit_field(&mut self, w: &Walker, node: &Field) {
w.walk_children(node);
}
fn visit_property(&mut self, w: &Walker, node: &Property) {
w.walk_children(node);
}
fn visit_signal(&mut self, w: &Walker, node: &Signal) {
w.walk_children(node);
}
fn visit_constant(&mut self, w: &Walker, node: &Constant) {
w.walk_children(node);
}
fn visit_delegate(&mut self, w: &Walker, node: &Delegate) {
w.walk_children(node);
}
fn visit_member_access(&mut self, w: &Walker, node: &MemberAccess) {
w.walk_children(node);
}
fn visit_method_call(&mut self, w: &Walker, node: &MethodCall) {
w.walk_children(node);
}
}
pub fn walk<V, N>(visitor: &mut V, node: &N)
where
V: Visitor,
N: AsRef<CodeNode>,
{
unsafe {
let inst = new_instance(visitor as &mut dyn Visitor);
ffi::vala_code_node_accept(node.as_ref().as_raw(), inst);
free_instance(inst);
}
}
pub fn walk_source_file<V: Visitor>(visitor: &mut V, file: &crate::SourceFile) {
unsafe {
let inst = new_instance(visitor as &mut dyn Visitor);
ffi::vala_source_file_accept(file.as_raw(), inst);
free_instance(inst);
}
}
type HandlerPtr = *mut dyn Visitor;
static REGISTER: Once = Once::new();
static mut VISITOR_TYPE: ffi::GType = 0;
static mut PARENT_INSTANCE_SIZE: u32 = 0;
unsafe fn registered_type() -> ffi::GType {
REGISTER.call_once(|| {
let parent = ffi::vala_code_visitor_get_type();
let mut query = std::mem::zeroed::<gobject_sys::GTypeQuery>();
gobject_sys::g_type_query(parent, &mut query);
PARENT_INSTANCE_SIZE = query.instance_size;
let name = CString::new("ValaRsVisitor").unwrap();
VISITOR_TYPE = gobject_sys::g_type_register_static_simple(
parent,
name.as_ptr(),
query.class_size,
Some(class_init),
query.instance_size + std::mem::size_of::<HandlerPtr>() as u32,
None,
0,
);
});
VISITOR_TYPE
}
unsafe fn handler_slot(inst: *mut ffi::ValaCodeVisitor) -> *mut HandlerPtr {
let base = inst as *mut u8;
base.add(PARENT_INSTANCE_SIZE as usize) as *mut HandlerPtr
}
unsafe fn new_instance(handler: &mut (dyn Visitor + '_)) -> *mut ffi::ValaCodeVisitor {
let ty = registered_type();
let obj = ffi::vala_code_visitor_construct(ty);
let erased: HandlerPtr = std::mem::transmute::<*mut dyn Visitor, HandlerPtr>(handler);
*handler_slot(obj) = erased;
obj
}
unsafe fn free_instance(inst: *mut ffi::ValaCodeVisitor) {
ffi::vala_code_visitor_unref(inst as ffi::gpointer);
}
unsafe fn dispatch<'a>(self_: *mut ffi::ValaCodeVisitor) -> (&'a mut dyn Visitor, Walker) {
let handler = &mut **handler_slot(self_);
(handler, Walker { visitor: self_ })
}
macro_rules! trampoline {
($fn_name:ident, $ffi_node:ty, $wrapper:ty, $method:ident) => {
unsafe extern "C" fn $fn_name(self_: *mut ffi::ValaCodeVisitor, node: *mut $ffi_node) {
let (handler, walker) = dispatch(self_);
if let Some(n) = <$wrapper>::from_raw_none(node) {
handler.$method(&walker, &n);
}
}
};
}
trampoline!(tr_namespace, ffi::ValaNamespace, Namespace, visit_namespace);
trampoline!(tr_class, ffi::ValaClass, Class, visit_class);
trampoline!(tr_struct, ffi::ValaStruct, Struct, visit_struct);
trampoline!(tr_interface, ffi::ValaInterface, Interface, visit_interface);
trampoline!(tr_enum, ffi::ValaEnum, Enum, visit_enum);
trampoline!(tr_method, ffi::ValaMethod, Method, visit_method);
trampoline!(tr_field, ffi::ValaField, Field, visit_field);
trampoline!(tr_property, ffi::ValaProperty, Property, visit_property);
trampoline!(tr_signal, ffi::ValaSignal, Signal, visit_signal);
trampoline!(tr_constant, ffi::ValaConstant, Constant, visit_constant);
trampoline!(tr_delegate, ffi::ValaDelegate, Delegate, visit_delegate);
trampoline!(
tr_member_access,
ffi::ValaMemberAccess,
MemberAccess,
visit_member_access
);
trampoline!(
tr_method_call,
ffi::ValaMethodCall,
MethodCall,
visit_method_call
);
unsafe extern "C" fn recurse(self_: *mut ffi::ValaCodeVisitor, node: *mut ffi::ValaCodeNode) {
ffi::vala_code_node_accept_children(node, self_);
}
unsafe extern "C" fn recurse_source_file(
self_: *mut ffi::ValaCodeVisitor,
file: *mut ffi::ValaSourceFile,
) {
ffi::vala_source_file_accept_children(file, self_);
}
type SlotFn = unsafe extern "C" fn(*mut ffi::ValaCodeVisitor, *mut ffi::ValaCodeNode);
unsafe extern "C" fn class_init(klass: *mut c_void, _data: *mut c_void) {
let vtable = klass as *mut ffi::ValaCodeVisitorClass;
let r: SlotFn = recurse;
fill_all_slots(vtable, r);
(*vtable).visit_source_file = Some(recurse_source_file);
(*vtable).visit_namespace = Some(tr_namespace);
(*vtable).visit_class = Some(tr_class);
(*vtable).visit_struct = Some(tr_struct);
(*vtable).visit_interface = Some(tr_interface);
(*vtable).visit_enum = Some(tr_enum);
(*vtable).visit_method = Some(tr_method);
(*vtable).visit_field = Some(tr_field);
(*vtable).visit_property = Some(tr_property);
(*vtable).visit_signal = Some(tr_signal);
(*vtable).visit_constant = Some(tr_constant);
(*vtable).visit_delegate = Some(tr_delegate);
(*vtable).visit_member_access = Some(tr_member_access);
(*vtable).visit_method_call = Some(tr_method_call);
}
#[allow(clippy::missing_transmute_annotations)]
unsafe fn fill_all_slots(vtable: *mut ffi::ValaCodeVisitorClass, r: SlotFn) {
macro_rules! set {
($($slot:ident),* $(,)?) => {
$( (*vtable).$slot = Some(std::mem::transmute::<SlotFn, _>(r)); )*
};
}
set!(
visit_namespace,
visit_class,
visit_struct,
visit_interface,
visit_enum,
visit_enum_value,
visit_error_domain,
visit_error_code,
visit_delegate,
visit_constant,
visit_field,
visit_method,
visit_creation_method,
visit_formal_parameter,
visit_property,
visit_property_accessor,
visit_signal,
visit_constructor,
visit_destructor,
visit_type_parameter,
visit_using_directive,
visit_data_type,
visit_block,
visit_empty_statement,
visit_declaration_statement,
visit_local_variable,
visit_initializer_list,
visit_expression_statement,
visit_if_statement,
visit_switch_statement,
visit_switch_section,
visit_switch_label,
visit_loop_statement,
visit_while_statement,
visit_do_statement,
visit_for_statement,
visit_foreach_statement,
visit_break_statement,
visit_continue_statement,
visit_return_statement,
visit_yield_statement,
visit_throw_statement,
visit_try_statement,
visit_catch_clause,
visit_lock_statement,
visit_unlock_statement,
visit_delete_statement,
visit_array_creation_expression,
visit_boolean_literal,
visit_character_literal,
visit_integer_literal,
visit_real_literal,
visit_regex_literal,
visit_string_literal,
visit_template,
visit_tuple,
visit_null_literal,
visit_member_access,
visit_method_call,
visit_element_access,
visit_base_access,
visit_postfix_expression,
visit_object_creation_expression,
visit_sizeof_expression,
visit_typeof_expression,
visit_unary_expression,
visit_cast_expression,
visit_named_argument,
visit_pointer_indirection,
visit_addressof_expression,
visit_reference_transfer_expression,
visit_binary_expression,
visit_type_check,
visit_conditional_expression,
visit_lambda_expression,
visit_assignment,
visit_with_statement,
visit_slice_expression,
);
}