Skip to main content

xamlx/emit/
xaml_emit_context.rs

1//! Port of `Emit/XamlEmitContext.cs`.
2//!
3//! The upstream abstract class `XamlEmitContext<TBackendEmitter, TEmitResult>` is split into
4//! [`XamlEmitContextBase`] (its state) and the [`XamlEmitContext`] trait (its virtual and
5//! abstract members). The non-virtual public members are inherent methods of
6//! `dyn XamlEmitContext<_, _>`.
7
8use std::any::Any;
9use std::cell::RefCell;
10use std::ops::Deref;
11use std::rc::Rc;
12
13use crate::ast::{IXamlAstNode, IXamlPropertySetter, IXamlWrappedMethod};
14use crate::exceptions::{XamlError, XamlResult};
15use crate::extensions::{
16    query_node_interface, query_property_setter_interface, query_wrapped_method_interface,
17};
18use crate::transform::{TransformerConfiguration, XamlContextBase};
19use crate::type_system::{IFileSource, IXamlLocal, IXamlMethod, IXamlType, IXamlTypeBuilder};
20
21use super::{
22    IHasLocalsPool, IXamlAstLocalsNodeEmitter, IXamlWrappedMethodEmitterWithLocals,
23    XamlLanguageEmitMappings, XamlRuntimeContext,
24};
25
26pub trait IXamlEmitResult {
27    fn return_type(&self) -> Option<Rc<dyn IXamlType>>;
28    fn valid(&self) -> bool;
29}
30
31/// An entry of the `Emitters` list (`List<object>` upstream). An emitter object implements
32/// one or more of the emitter interfaces and exposes them through the `as_*` accessors.
33pub trait IXamlEmitter<TBackendEmitter, TEmitResult: IXamlEmitResult>: 'static {
34    /// `this as IXamlAstNodeEmitter<TBackendEmitter, TEmitResult>`.
35    fn as_node_emitter(&self) -> Option<&dyn IXamlAstNodeEmitter<TBackendEmitter, TEmitResult>> {
36        None
37    }
38    /// `this as IXamlPropertySetterEmitter<TBackendEmitter>`.
39    fn as_property_setter_emitter(
40        &self,
41    ) -> Option<&dyn IXamlPropertySetterEmitter<TBackendEmitter>> {
42        None
43    }
44    /// `this as IXamlWrappedMethodEmitter<TBackendEmitter, TEmitResult>`.
45    fn as_wrapped_method_emitter(
46        &self,
47    ) -> Option<&dyn IXamlWrappedMethodEmitter<TBackendEmitter, TEmitResult>> {
48        None
49    }
50    /// `this as IXamlAstLocalsNodeEmitter<TBackendEmitter, TEmitResult>`.
51    fn as_locals_node_emitter(
52        &self,
53    ) -> Option<&dyn IXamlAstLocalsNodeEmitter<TBackendEmitter, TEmitResult>>
54    where
55        TBackendEmitter: IHasLocalsPool,
56    {
57        None
58    }
59    /// `this as IXamlWrappedMethodEmitterWithLocals<TBackendEmitter, TEmitResult>`.
60    fn as_wrapped_method_emitter_with_locals(
61        &self,
62    ) -> Option<&dyn IXamlWrappedMethodEmitterWithLocals<TBackendEmitter, TEmitResult>>
63    where
64        TBackendEmitter: IHasLocalsPool,
65    {
66        None
67    }
68    fn as_any(&self) -> &dyn Any;
69}
70
71pub trait IXamlAstNodeEmitter<TBackendEmitter, TEmitResult: IXamlEmitResult> {
72    /// Returns `Ok(None)` (or an invalid result) when this emitter does not handle the node.
73    fn emit(
74        &self,
75        node: &Rc<dyn IXamlAstNode>,
76        context: &dyn XamlEmitContext<TBackendEmitter, TEmitResult>,
77        code_gen: &TBackendEmitter,
78    ) -> XamlResult<Option<TEmitResult>>;
79}
80
81/// A node that knows how to emit itself for a given backend.
82/// Exposed by nodes through `IXamlAstNode::query_interface`.
83pub trait IXamlAstEmitableNode<TBackendEmitter, TEmitResult: IXamlEmitResult> {
84    fn emit(
85        &self,
86        context: &dyn XamlEmitContext<TBackendEmitter, TEmitResult>,
87        code_gen: &TBackendEmitter,
88    ) -> XamlResult<TEmitResult>;
89}
90
91pub trait IXamlCustomEmitMethod<TBackendEmitter>: IXamlMethod {
92    fn emit_call(&self, emitter: &TBackendEmitter) -> XamlResult<()>;
93}
94
95pub trait IXamlCustomEmitMethodWithContext<TBackendEmitter, TEmitResult: IXamlEmitResult>:
96    IXamlMethod
97{
98    fn emit_call(
99        &self,
100        context: &dyn XamlEmitContext<TBackendEmitter, TEmitResult>,
101        emitter: &TBackendEmitter,
102    ) -> XamlResult<()>;
103}
104
105pub trait IXamlPropertySetterEmitter<TBackendEmitter> {
106    fn emit_call(
107        &self,
108        setter: &Rc<dyn IXamlPropertySetter>,
109        emitter: &TBackendEmitter,
110    ) -> XamlResult<bool>;
111}
112
113/// A property setter that knows how to emit itself for a given backend.
114/// Exposed by setters through `IXamlPropertySetter::query_interface`.
115pub trait IXamlEmitablePropertySetter<TBackendEmitter>: IXamlPropertySetter {
116    fn emit(&self, emitter: &TBackendEmitter) -> XamlResult<()>;
117}
118
119pub trait IXamlWrappedMethodEmitter<TBackendEmitter, TEmitResult: IXamlEmitResult> {
120    fn emit_call(
121        &self,
122        context: &dyn XamlEmitContext<TBackendEmitter, TEmitResult>,
123        method: &Rc<dyn IXamlWrappedMethod>,
124        emitter: &TBackendEmitter,
125        swallow_result: bool,
126    ) -> XamlResult<bool>;
127}
128
129/// A wrapped method that knows how to emit itself for a given backend.
130/// Exposed by wrapped methods through `IXamlWrappedMethod::query_interface`.
131pub trait IXamlEmitableWrappedMethod<TBackendEmitter, TEmitResult: IXamlEmitResult>:
132    IXamlWrappedMethod
133{
134    fn emit(
135        &self,
136        context: &dyn XamlEmitContext<TBackendEmitter, TEmitResult>,
137        emitter: &TBackendEmitter,
138        swallow_result: bool,
139    ) -> XamlResult<()>;
140}
141
142type AfterEmitCallback = Box<dyn FnOnce() -> XamlResult<()>>;
143
144/// The state of the upstream abstract `XamlEmitContext` class.
145pub struct XamlEmitContextBase<TBackendEmitter, TEmitResult: IXamlEmitResult> {
146    base: XamlContextBase,
147    context_local: Option<Rc<dyn IXamlLocal>>,
148    pub file: Option<Rc<dyn IFileSource>>,
149    pub emitters: Vec<Rc<dyn IXamlEmitter<TBackendEmitter, TEmitResult>>>,
150    after_emit_callbacks: RefCell<Vec<AfterEmitCallback>>,
151    current_node: RefCell<Option<Rc<dyn IXamlAstNode>>>,
152    pub configuration: Rc<TransformerConfiguration>,
153    pub emit_mappings: Rc<XamlLanguageEmitMappings<TBackendEmitter, TEmitResult>>,
154    pub runtime_context: Rc<XamlRuntimeContext<TBackendEmitter, TEmitResult>>,
155    pub declaring_type: Rc<dyn IXamlTypeBuilder<TBackendEmitter>>,
156    pub emitter: TBackendEmitter,
157}
158
159/// Gives access to the `XamlContextBase` members (the C# base class).
160impl<TBackendEmitter, TEmitResult: IXamlEmitResult> Deref
161    for XamlEmitContextBase<TBackendEmitter, TEmitResult>
162{
163    type Target = XamlContextBase;
164
165    fn deref(&self) -> &XamlContextBase {
166        &self.base
167    }
168}
169
170impl<TBackendEmitter, TEmitResult: IXamlEmitResult>
171    XamlEmitContextBase<TBackendEmitter, TEmitResult>
172{
173    #[allow(clippy::too_many_arguments)]
174    pub fn new(
175        emitter: TBackendEmitter,
176        configuration: Rc<TransformerConfiguration>,
177        emit_mappings: Rc<XamlLanguageEmitMappings<TBackendEmitter, TEmitResult>>,
178        runtime_context: Rc<XamlRuntimeContext<TBackendEmitter, TEmitResult>>,
179        context_local: Option<Rc<dyn IXamlLocal>>,
180        declaring_type: Rc<dyn IXamlTypeBuilder<TBackendEmitter>>,
181        file: Option<Rc<dyn IFileSource>>,
182        emitters: Vec<Rc<dyn IXamlEmitter<TBackendEmitter, TEmitResult>>>,
183    ) -> Self {
184        Self {
185            base: XamlContextBase::new(),
186            context_local,
187            file,
188            emitters,
189            after_emit_callbacks: RefCell::new(Vec::new()),
190            current_node: RefCell::new(None),
191            configuration,
192            emit_mappings,
193            runtime_context,
194            declaring_type,
195            emitter,
196        }
197    }
198
199    pub fn context_local(&self) -> XamlResult<Rc<dyn IXamlLocal>> {
200        self.context_local.clone().ok_or_else(|| {
201            XamlError::invalid_operation(
202                "The current emit context doesn't supply a runtime context",
203            )
204        })
205    }
206
207    pub fn add_after_emit_callbacks(&self, callback: impl FnOnce() -> XamlResult<()> + 'static) {
208        self.after_emit_callbacks
209            .borrow_mut()
210            .push(Box::new(callback));
211    }
212
213    pub fn execute_after_emit_callbacks(&self) -> XamlResult<()> {
214        let callbacks = std::mem::take(&mut *self.after_emit_callbacks.borrow_mut());
215        for callback in callbacks {
216            callback()?;
217        }
218        Ok(())
219    }
220}
221
222/// The virtual and abstract members of the upstream `XamlEmitContext` class.
223///
224/// A backend implements this trait on its context type, embedding an [`XamlEmitContextBase`].
225/// The provided methods are the upstream base implementations; "overriding" one means
226/// implementing it and, where the override calls `base.Method(...)`, delegating to the matching
227/// `default_*` function of this module.
228pub trait XamlEmitContext<TBackendEmitter: 'static, TEmitResult: IXamlEmitResult + 'static>:
229    'static
230{
231    /// The base class state.
232    fn base(&self) -> &XamlEmitContextBase<TBackendEmitter, TEmitResult>;
233
234    /// `this`, as the base class type.
235    fn as_emit_context(&self) -> &dyn XamlEmitContext<TBackendEmitter, TEmitResult>;
236
237    fn as_any(&self) -> &dyn Any;
238
239    /// `protected abstract void EmitConvert(...)`.
240    fn emit_convert(
241        &self,
242        value: &Rc<dyn IXamlAstNode>,
243        code_gen: &TBackendEmitter,
244        expected_type: &Rc<dyn IXamlType>,
245        returned_type: &Rc<dyn IXamlType>,
246    ) -> XamlResult<()>;
247
248    /// `protected virtual bool EmitCore(IXamlPropertySetter setter, TBackendEmitter codeGen)`.
249    fn emit_core_property_setter(
250        &self,
251        setter: &Rc<dyn IXamlPropertySetter>,
252        code_gen: &TBackendEmitter,
253    ) -> XamlResult<bool> {
254        default_emit_core_property_setter(self.as_emit_context(), setter, code_gen)
255    }
256
257    /// `protected virtual bool EmitCore(IXamlWrappedMethod wrapped, TBackendEmitter codeGen, bool swallowResult)`.
258    fn emit_core_wrapped_method(
259        &self,
260        wrapped: &Rc<dyn IXamlWrappedMethod>,
261        code_gen: &TBackendEmitter,
262        swallow_result: bool,
263    ) -> XamlResult<bool> {
264        default_emit_core_wrapped_method(self.as_emit_context(), wrapped, code_gen, swallow_result)
265    }
266
267    /// `protected virtual TEmitResult EmitNode(IXamlAstNode value, TBackendEmitter codeGen)`.
268    fn emit_node(
269        &self,
270        value: &Rc<dyn IXamlAstNode>,
271        code_gen: &TBackendEmitter,
272    ) -> XamlResult<TEmitResult> {
273        default_emit_node(self.as_emit_context(), value, code_gen)
274    }
275
276    /// `protected virtual TEmitResult? EmitNodeCore(IXamlAstNode value, TBackendEmitter codeGen, out bool foundEmitter)`.
277    /// Returns the result together with `foundEmitter`.
278    fn emit_node_core(
279        &self,
280        value: &Rc<dyn IXamlAstNode>,
281        code_gen: &TBackendEmitter,
282    ) -> XamlResult<(Option<TEmitResult>, bool)> {
283        default_emit_node_core(self.as_emit_context(), value, code_gen)
284    }
285}
286
287/// Base implementation of `EmitCore(IXamlPropertySetter, TBackendEmitter)`.
288pub fn default_emit_core_property_setter<B: 'static, R: IXamlEmitResult + 'static>(
289    context: &dyn XamlEmitContext<B, R>,
290    setter: &Rc<dyn IXamlPropertySetter>,
291    code_gen: &B,
292) -> XamlResult<bool> {
293    let mut found_emitter = false;
294    for e in &context.base().emitters {
295        if let Some(pse) = e.as_property_setter_emitter() {
296            found_emitter = pse.emit_call(setter, code_gen)?;
297            if found_emitter {
298                break;
299            }
300        }
301    }
302
303    if !found_emitter {
304        if let Some(eps) =
305            query_property_setter_interface::<dyn IXamlEmitablePropertySetter<B>>(setter)
306        {
307            found_emitter = true;
308            eps.emit(code_gen)?;
309        }
310    }
311
312    Ok(found_emitter)
313}
314
315/// Base implementation of `EmitCore(IXamlWrappedMethod, TBackendEmitter, bool)`.
316pub fn default_emit_core_wrapped_method<B: 'static, R: IXamlEmitResult + 'static>(
317    context: &dyn XamlEmitContext<B, R>,
318    wrapped: &Rc<dyn IXamlWrappedMethod>,
319    code_gen: &B,
320    swallow_result: bool,
321) -> XamlResult<bool> {
322    let mut found_emitter = false;
323    for e in &context.base().emitters {
324        if let Some(wme) = e.as_wrapped_method_emitter() {
325            found_emitter = wme.emit_call(context, wrapped, code_gen, swallow_result)?;
326            if found_emitter {
327                break;
328            }
329        }
330    }
331
332    if !found_emitter {
333        if let Some(ewm) =
334            query_wrapped_method_interface::<dyn IXamlEmitableWrappedMethod<B, R>>(wrapped)
335        {
336            found_emitter = true;
337            ewm.emit(context, code_gen, swallow_result)?;
338        }
339    }
340
341    Ok(found_emitter)
342}
343
344/// Base implementation of `EmitNode`.
345pub fn default_emit_node<B: 'static, R: IXamlEmitResult + 'static>(
346    context: &dyn XamlEmitContext<B, R>,
347    value: &Rc<dyn IXamlAstNode>,
348    code_gen: &B,
349) -> XamlResult<R> {
350    let result = context
351        .emit_node_core(value, code_gen)
352        .and_then(|(res, found_emitter)| {
353            if !found_emitter {
354                return Err(XamlError::load_exception(
355                    format!(
356                        "Unable to find emitter for node type: {}",
357                        value.type_name()
358                    ),
359                    Some(&**value),
360                ));
361            }
362            res.ok_or_else(|| {
363                XamlError::internal(
364                    "NullReferenceException",
365                    "The node emitter didn't produce an emit result",
366                )
367            })
368        });
369
370    match result {
371        Ok(res) => Ok(res),
372        Err(e) if e.is_xml_exception() => Err(e),
373        Err(e) => Err(XamlError::load_exception(
374            format!(
375                "Internal compiler error while emitting node {}:\n{}",
376                value.to_node_string(),
377                e
378            ),
379            Some(&**value),
380        )),
381    }
382}
383
384/// Base implementation of `EmitNodeCore`.
385pub fn default_emit_node_core<B: 'static, R: IXamlEmitResult + 'static>(
386    context: &dyn XamlEmitContext<B, R>,
387    value: &Rc<dyn IXamlAstNode>,
388    code_gen: &B,
389) -> XamlResult<(Option<R>, bool)> {
390    let mut res: Option<R> = None;
391    for e in &context.base().emitters {
392        if let Some(ve) = e.as_node_emitter() {
393            res = ve.emit(value, context, code_gen)?;
394            if res.as_ref().is_some_and(|r| r.valid()) {
395                return Ok((res, true));
396            }
397        }
398    }
399
400    if let Some(en) = query_node_interface::<dyn IXamlAstEmitableNode<B, R>>(value) {
401        return Ok((Some(en.emit(context, code_gen)?), true));
402    }
403
404    Ok((res, false))
405}
406
407/// The non-virtual public members of the upstream `XamlEmitContext` class.
408impl<B: 'static, R: IXamlEmitResult + 'static> dyn XamlEmitContext<B, R> {
409    /// `TEmitResult Emit(IXamlAstNode value, TBackendEmitter codeGen, IXamlType? expectedType)`.
410    pub fn emit(
411        &self,
412        value: &Rc<dyn IXamlAstNode>,
413        code_gen: &B,
414        expected_type: Option<&Rc<dyn IXamlType>>,
415    ) -> XamlResult<R> {
416        let base = self.base();
417        let previous = base.current_node.borrow().clone();
418        match previous {
419            Some(current) => {
420                base.push_parent(current);
421                *base.current_node.borrow_mut() = Some(value.clone());
422                let res = self.emit_core(value, code_gen, expected_type);
423                *base.current_node.borrow_mut() = base.pop_parent();
424                res
425            }
426            None => {
427                *base.current_node.borrow_mut() = Some(value.clone());
428                let res = self.emit_core(value, code_gen, expected_type);
429                *base.current_node.borrow_mut() = None;
430                res
431            }
432        }
433    }
434
435    /// `void Emit(IXamlPropertySetter setter, TBackendEmitter codeGen)`.
436    pub fn emit_property_setter(
437        &self,
438        setter: &Rc<dyn IXamlPropertySetter>,
439        code_gen: &B,
440    ) -> XamlResult<()> {
441        if !self.emit_core_property_setter(setter, code_gen)? {
442            return Err(XamlError::invalid_operation(format!(
443                "Unable to find emitter for property setter type: {}",
444                setter.type_name()
445            )));
446        }
447        Ok(())
448    }
449
450    /// `void Emit(IXamlWrappedMethod wrapped, TBackendEmitter codeGen, bool swallowResult)`.
451    pub fn emit_wrapped_method(
452        &self,
453        wrapped: &Rc<dyn IXamlWrappedMethod>,
454        code_gen: &B,
455        swallow_result: bool,
456    ) -> XamlResult<()> {
457        if !self.emit_core_wrapped_method(wrapped, code_gen, swallow_result)? {
458            return Err(XamlError::invalid_operation(format!(
459                "Unable to find emitter for wrapped method type: {}",
460                wrapped.type_name()
461            )));
462        }
463        Ok(())
464    }
465
466    fn emit_core(
467        &self,
468        value: &Rc<dyn IXamlAstNode>,
469        code_gen: &B,
470        expected_type: Option<&Rc<dyn IXamlType>>,
471    ) -> XamlResult<R> {
472        let res = self.emit_node(value, code_gen)?;
473        let returned_type = res.return_type();
474
475        match (&returned_type, expected_type) {
476            (None, None) => {}
477            (Some(returned_type), None) => {
478                return Err(XamlError::load_exception(
479                    format!(
480                        "Emit of node {} resulted in {} while caller expected void",
481                        value.to_node_string(),
482                        returned_type.get_fqn()
483                    ),
484                    Some(&**value),
485                ));
486            }
487            (None, Some(expected_type)) => {
488                return Err(XamlError::load_exception(
489                    format!(
490                        "Emit of node {} resulted in void while caller expected {}",
491                        value.to_node_string(),
492                        expected_type.get_fqn()
493                    ),
494                    Some(&**value),
495                ));
496            }
497            (Some(returned_type), Some(expected_type)) => {
498                if !returned_type.equals(&**expected_type) {
499                    self.emit_convert(value, code_gen, expected_type, returned_type)?;
500                }
501            }
502        }
503
504        Ok(res)
505    }
506
507    pub fn context_local(&self) -> XamlResult<Rc<dyn IXamlLocal>> {
508        self.base().context_local()
509    }
510
511    pub fn add_after_emit_callbacks(&self, callback: impl FnOnce() -> XamlResult<()> + 'static) {
512        self.base().add_after_emit_callbacks(callback)
513    }
514
515    pub fn execute_after_emit_callbacks(&self) -> XamlResult<()> {
516        self.base().execute_after_emit_callbacks()
517    }
518}