Skip to main content

rune/compile/
context.rs

1use core::fmt;
2
3use crate as rune;
4use crate::alloc::prelude::*;
5use crate::alloc::{self, BTreeSet, Box, HashMap, HashSet, String, Vec};
6#[cfg(feature = "emit")]
7use crate::compile::MetaInfo;
8use crate::compile::{self, ContextError, Names};
9use crate::compile::{meta, Docs};
10use crate::function::{Function, Plain};
11use crate::function_meta::{AssociatedName, ToInstance};
12use crate::hash;
13use crate::item::{ComponentRef, IntoComponent};
14use crate::macros::{MacroContext, TokenStream};
15use crate::module::{
16    DocFunction, Fields, Module, ModuleAssociated, ModuleAssociatedKind, ModuleFunction,
17    ModuleItem, ModuleItemCommon, ModuleReexport, ModuleTrait, ModuleTraitImpl, ModuleType,
18    TypeSpecification,
19};
20use crate::runtime::{
21    Address, AnyTypeInfo, ConstConstructImpl, ConstContext, ConstValue, FunctionHandler, Memory,
22    Output, Protocol, Rtti, RttiKind, RuntimeContext, TypeInfo, VmError,
23};
24use crate::sync::Arc;
25use crate::{Hash, Item, ItemBuf};
26
27crate::declare_dyn_fn! {
28    struct MacroHandlerVtable;
29
30    /// A (type erased) macro handler.
31    pub struct MacroHandler {
32        fn call(cx: &mut MacroContext<'_, '_, '_>, input: &TokenStream) -> compile::Result<TokenStream>;
33    }
34}
35
36crate::declare_dyn_fn! {
37    struct TraitHandlerVtable;
38
39    /// Invoked when types implement a trait.
40    pub struct TraitHandler {
41        fn call(cx: &mut TraitContext<'_>) -> Result<(), ContextError>;
42    }
43}
44
45crate::declare_dyn_fn! {
46    struct AttributeMacroHandlerVtable;
47
48    /// A (type erased) attribute macro handler.
49    pub struct AttributeMacroHandler {
50        fn call(cx: &mut MacroContext<'_, '_, '_>, input: &TokenStream, attributes: &TokenStream) -> compile::Result<TokenStream>;
51    }
52}
53
54/// Type used to install traits.
55pub struct TraitContext<'a> {
56    /// The context the trait function are being installed into.
57    cx: &'a mut Context,
58    /// The item being installed.
59    item: &'a Item,
60    /// The hash of the item being installed.
61    hash: Hash,
62    /// Type info of the type being installed.
63    type_info: &'a TypeInfo,
64    /// The trait being implemented for.
65    trait_item: &'a Item,
66    /// Hash of the trait being impleemnted.
67    trait_hash: Hash,
68}
69
70impl TraitContext<'_> {
71    /// Return the item the trait is being installed for.
72    pub fn item(&self) -> &Item {
73        self.item
74    }
75
76    /// Return the hash the trait is being installed for.
77    pub fn hash(&self) -> Hash {
78        self.hash
79    }
80
81    /// Find the given protocol function for the current type.
82    ///
83    /// This requires that the function is defined.
84    pub fn find(&mut self, protocol: &'static Protocol) -> Result<FunctionHandler, ContextError> {
85        let name = protocol.to_instance()?;
86
87        let hash = name
88            .kind
89            .hash(self.hash)
90            .with_function_parameters(name.function_parameters);
91
92        let Some(handler) = self.cx.functions.get(&hash) else {
93            return Err(ContextError::MissingTraitFunction {
94                name: name.kind.try_to_string()?,
95                item: self.item.try_to_owned()?,
96                hash,
97                trait_item: self.trait_item.try_to_owned()?,
98                trait_hash: self.trait_hash,
99            });
100        };
101
102        let handler = handler.clone();
103
104        if let Some(method) = protocol.method {
105            self.function_handler(method, &handler)?;
106        }
107
108        Ok(handler)
109    }
110
111    /// Try to find the given associated function.
112    ///
113    /// This does not require that the function is defined.
114    pub fn try_find(&self, name: impl ToInstance) -> Result<Option<FunctionHandler>, ContextError> {
115        let name = name.to_instance()?;
116
117        let hash = name
118            .kind
119            .hash(self.hash)
120            .with_function_parameters(name.function_parameters);
121
122        Ok(self.cx.functions.get(&hash).cloned())
123    }
124
125    /// Find or define a protocol function.
126    pub fn find_or_define<A, F>(
127        &mut self,
128        protocol: &'static Protocol,
129        function: F,
130    ) -> Result<FunctionHandler, ContextError>
131    where
132        F: Function<A, Plain>,
133    {
134        let function = if let Some(function) = self.try_find(protocol)? {
135            function
136        } else {
137            self.function(protocol, function)?
138        };
139
140        if let Some(method) = protocol.method {
141            self.function_handler(method, &function)?;
142        }
143
144        Ok(function)
145    }
146
147    /// Define a new associated function for the current type.
148    pub fn function<F, A>(
149        &mut self,
150        name: impl ToInstance,
151        handler: F,
152    ) -> Result<FunctionHandler, ContextError>
153    where
154        F: Function<A, Plain>,
155    {
156        let handler = FunctionHandler::new(move |memory, addr, len, out| {
157            handler.call(memory, addr, len, out)
158        })?;
159        self.function_handler(name, &handler)?;
160        Ok(handler)
161    }
162
163    /// Define a new associated raw function for the current type.
164    pub fn raw_function<F>(
165        &mut self,
166        name: impl ToInstance,
167        handler: F,
168    ) -> Result<FunctionHandler, ContextError>
169    where
170        F: 'static
171            + Fn(&mut dyn Memory, Address, usize, Output) -> Result<(), VmError>
172            + Send
173            + Sync,
174    {
175        let handler = FunctionHandler::new(handler)?;
176        self.function_handler(name, &handler)?;
177        Ok(handler)
178    }
179
180    /// Define a new associated function for the current type using a raw
181    /// handler.
182    fn function_handler(
183        &mut self,
184        name: impl ToInstance,
185        handler: &FunctionHandler,
186    ) -> Result<(), ContextError> {
187        let name = name.to_instance()?;
188        self.function_inner(name, handler)
189    }
190
191    fn function_inner(
192        &mut self,
193        name: AssociatedName,
194        handler: &FunctionHandler,
195    ) -> Result<(), ContextError> {
196        let function = ModuleFunction {
197            handler: handler.clone(),
198            trait_hash: Some(self.trait_hash),
199            doc: DocFunction {
200                #[cfg(feature = "doc")]
201                is_async: false,
202                #[cfg(feature = "doc")]
203                args: None,
204                #[cfg(feature = "doc")]
205                argument_types: Box::default(),
206                #[cfg(feature = "doc")]
207                return_type: meta::DocType::empty(),
208            },
209        };
210
211        let assoc = ModuleAssociated {
212            container: self.hash,
213            container_type_info: self.type_info.try_clone()?,
214            name,
215            common: ModuleItemCommon {
216                docs: Docs::EMPTY,
217                deprecated: None,
218            },
219            kind: ModuleAssociatedKind::Function(function),
220        };
221
222        self.cx.install_associated(&assoc)?;
223        Ok(())
224    }
225}
226
227/// Context metadata.
228#[derive(Debug)]
229#[non_exhaustive]
230pub(crate) struct ContextMeta {
231    /// Type hash for the given meta item.
232    pub(crate) hash: Hash,
233    /// The item of the returned compile meta.
234    pub(crate) item: Option<ItemBuf>,
235    /// The kind of the compile meta.
236    pub(crate) kind: meta::Kind,
237    /// Deprecation notice.
238    #[cfg(feature = "doc")]
239    pub(crate) deprecated: Option<Box<str>>,
240    /// Documentation associated with a context meta.
241    #[cfg(feature = "doc")]
242    pub(crate) docs: Docs,
243}
244
245impl ContextMeta {
246    #[cfg(feature = "emit")]
247    pub(crate) fn info(&self) -> alloc::Result<MetaInfo> {
248        MetaInfo::new(&self.kind, self.hash, self.item.as_deref())
249    }
250}
251
252/// Information on a specific type.
253#[derive(Debug, TryClone)]
254#[non_exhaustive]
255pub(crate) struct ContextType {
256    /// Item of the type.
257    item: ItemBuf,
258    /// Type hash.
259    hash: Hash,
260    /// Complete detailed information on the hash.
261    type_info: TypeInfo,
262    /// Type parameters.
263    type_parameters: Hash,
264}
265
266impl fmt::Display for ContextType {
267    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
268        write!(fmt, "{} => {}", self.item, self.type_info)?;
269        Ok(())
270    }
271}
272
273/// [Context] used for the Rune language.
274///
275/// See [Build::with_context][crate::Build::with_context].
276///
277/// At runtime this needs to be converted into a [RuntimeContext] when used with
278/// a [Vm][crate::runtime::Vm]. This is done through [Context::runtime].
279///
280/// A [Context] contains:
281/// * Native functions.
282/// * Native instance functions.
283/// * And native type definitions.
284#[derive(Default)]
285pub struct Context {
286    /// Unique modules installed in the context.
287    unique: HashSet<&'static str>,
288    /// Whether or not to include the prelude when constructing a new unit.
289    has_default_modules: bool,
290    /// Registered metadata, in the order that it was registered.
291    meta: Vec<ContextMeta>,
292    /// Item metadata in the context.
293    hash_to_meta: hash::Map<Vec<usize>>,
294    /// Store item to hash mapping.
295    item_to_hash: HashMap<ItemBuf, BTreeSet<Hash>>,
296    /// Registered native function handlers.
297    functions: hash::Map<FunctionHandler>,
298    /// Registered deprecation mesages for native functions.
299    deprecations: hash::Map<String>,
300    /// Information on associated types.
301    #[cfg(feature = "doc")]
302    associated: hash::Map<Vec<Hash>>,
303    /// Traits implemented by the given hash.
304    #[cfg(feature = "doc")]
305    implemented_traits: hash::Map<Vec<Hash>>,
306    /// Registered native macro handlers.
307    macros: hash::Map<MacroHandler>,
308    /// Handlers for realising traits.
309    traits: hash::Map<Option<TraitHandler>>,
310    /// Registered native attribute macro handlers.
311    attribute_macros: hash::Map<AttributeMacroHandler>,
312    /// Registered types.
313    types: hash::Map<ContextType>,
314    /// All available names in the context.
315    names: Names,
316    /// Registered crates.
317    crates: HashSet<Box<str>>,
318    /// Constants visible in this context
319    constants: hash::Map<ConstValue>,
320    /// Constant constructor.
321    construct: hash::Map<ConstConstructImpl>,
322}
323
324impl Context {
325    /// Construct a new empty [Context].
326    #[inline]
327    pub fn new() -> Self {
328        Self::default()
329    }
330
331    /// Construct a [Context] containing the default set of modules with the
332    /// given configuration.
333    ///
334    /// `stdio` determines if we include I/O functions that interact with stdout
335    /// and stderr by default, like `dbg`, `print`, and `println`. If this is
336    /// `false` all the corresponding low-level I/O functions have to be
337    /// provided through a different module.
338    ///
339    /// These are:
340    ///
341    /// * `::std::io::dbg`
342    /// * `::std::io::print`
343    /// * `::std::io::println`
344    pub fn with_config(#[allow(unused)] stdio: bool) -> Result<Self, ContextError> {
345        let mut this = Self::new();
346
347        // NB: Order is important, since later modules might use types defined
348        // in previous modules.
349
350        this.install(crate::modules::iter::module()?)?;
351        this.install(crate::modules::core::module()?)?;
352        this.install(crate::modules::cmp::module()?)?;
353        this.install(crate::modules::any::module()?)?;
354        this.install(crate::modules::clone::module()?)?;
355        this.install(crate::modules::num::module()?)?;
356        this.install(crate::modules::hash::module()?)?;
357
358        this.install(crate::modules::string::module()?)?;
359        this.install(crate::modules::bytes::module()?)?;
360
361        this.install(crate::modules::collections::module()?)?;
362        this.install(crate::modules::collections::hash_map::module()?)?;
363        this.install(crate::modules::collections::hash_set::module()?)?;
364        this.install(crate::modules::collections::vec_deque::module()?)?;
365
366        this.install(crate::modules::char::module()?)?;
367        this.install(crate::modules::f64::module()?)?;
368        this.install(crate::modules::f64::consts::module()?)?;
369        this.install(crate::modules::tuple::module()?)?;
370        this.install(crate::modules::fmt::module()?)?;
371        this.install(crate::modules::future::module()?)?;
372        this.install(crate::modules::i64::module()?)?;
373        this.install(crate::modules::u64::module()?)?;
374        this.install(crate::modules::io::module(stdio)?)?;
375        this.install(crate::modules::macros::module()?)?;
376        this.install(crate::modules::macros::builtin::module()?)?;
377        this.install(crate::modules::mem::module()?)?;
378        this.install(crate::modules::object::module()?)?;
379        this.install(crate::modules::ops::module()?)?;
380        this.install(crate::modules::ops::generator::module()?)?;
381        this.install(crate::modules::option::module()?)?;
382        this.install(crate::modules::result::module()?)?;
383        this.install(crate::modules::stream::module()?)?;
384        this.install(crate::modules::test::module()?)?;
385        this.install(crate::modules::vec::module()?)?;
386        this.install(crate::modules::slice::module()?)?;
387        this.has_default_modules = true;
388        Ok(this)
389    }
390
391    /// Construct a new collection of functions with default packages installed.
392    pub fn with_default_modules() -> Result<Self, ContextError> {
393        Self::with_config(true)
394    }
395
396    /// Construct a runtime context used when executing the virtual machine.
397    ///
398    /// This is not a cheap operation, since it requires cloning things out of
399    /// the build-time [Context] which are necessary at runtime.
400    ///
401    /// ```no_run
402    /// use rune::{Context, Vm, Unit};
403    /// use rune::sync::Arc;
404    ///
405    /// let context = Context::with_default_modules()?;
406    ///
407    /// let runtime = Arc::try_new(context.runtime()?)?;
408    /// let unit = Arc::try_new(Unit::default())?;
409    ///
410    /// let vm = Vm::new(runtime, unit);
411    /// # Ok::<_, rune::support::Error>(())
412    /// ```
413    pub fn runtime(&self) -> alloc::Result<RuntimeContext> {
414        Ok(RuntimeContext::new(
415            self.functions.try_clone()?,
416            self.constants.try_clone()?,
417            self.construct.try_clone()?,
418            self.deprecations.try_clone()?,
419        ))
420    }
421
422    /// Install the specified module.
423    ///
424    /// This installs everything that has been declared in the given [Module]
425    /// and ensures that they are compatible with the overall context, like
426    /// ensuring that a given type is only declared once.
427    #[tracing::instrument(skip_all, fields(item = ?module.as_ref().item))]
428    pub fn install<M>(&mut self, module: M) -> Result<(), ContextError>
429    where
430        M: AsRef<Module>,
431    {
432        let module = module.as_ref();
433        tracing::trace!("installing");
434
435        if let Some(id) = module.unique {
436            if !self.unique.try_insert(id)? {
437                return Ok(());
438            }
439        }
440
441        if let Some(ComponentRef::Crate(name)) = module.item.first() {
442            self.crates.try_insert(name.try_into()?)?;
443        }
444
445        tracing::trace!("module");
446        self.install_module(module)?;
447
448        tracing::trace!(types = module.types.len(), "types");
449        for ty in &module.types {
450            self.install_type(ty)?;
451        }
452
453        tracing::trace!(traits = module.traits.len(), "traits");
454        for t in &module.traits {
455            self.install_trait(t)?;
456        }
457
458        tracing::trace!(items = module.items.len(), "items");
459        for item in &module.items {
460            self.install_item(item)?;
461        }
462
463        tracing::trace!(associated = module.associated.len(), "associated");
464        for assoc in &module.associated {
465            self.install_associated(assoc)?;
466        }
467
468        tracing::trace!(trait_impls = module.trait_impls.len(), "trait impls");
469        for t in &module.trait_impls {
470            self.install_trait_impl(t)?;
471        }
472
473        tracing::trace!(reexports = module.reexports.len(), "reexports");
474        for r in &module.reexports {
475            self.install_reexport(r)?;
476        }
477
478        tracing::trace!(construct = module.construct.len(), "construct");
479        for (hash, type_info, construct) in &module.construct {
480            self.install_construct(*hash, type_info, construct)?;
481        }
482
483        Ok(())
484    }
485
486    /// Iterate over all available functions in the [Context].
487    #[cfg(any(feature = "cli", feature = "languageserver"))]
488    pub(crate) fn iter_functions(&self) -> impl Iterator<Item = (&ContextMeta, &meta::Signature)> {
489        self.meta.iter().flat_map(|meta| {
490            let signature = meta.kind.as_signature()?;
491            Some((meta, signature))
492        })
493    }
494
495    /// Iterate over all available types in the [Context].
496    #[cfg(feature = "cli")]
497    pub(crate) fn iter_types(&self) -> impl Iterator<Item = (Hash, &Item)> {
498        use core::iter;
499
500        let mut it = self.types.iter();
501
502        iter::from_fn(move || {
503            let (hash, ty) = it.next()?;
504            Some((*hash, ty.item.as_ref()))
505        })
506    }
507
508    /// Iterate over known child components of the given name.
509    pub(crate) fn iter_components<'a, I>(
510        &'a self,
511        iter: I,
512    ) -> alloc::Result<impl Iterator<Item = ComponentRef<'a>> + 'a>
513    where
514        I: 'a + IntoIterator<Item: IntoComponent>,
515    {
516        self.names.iter_components(iter)
517    }
518
519    /// Access the context meta for the given item.
520    ///
521    /// If this returns `Some`, at least one context meta is guaranteed to be
522    /// available.
523    pub(crate) fn lookup_meta(
524        &self,
525        item: &Item,
526    ) -> Option<impl Iterator<Item = &ContextMeta> + Clone> {
527        let hashes = self.item_to_hash.get(item)?;
528
529        Some(hashes.iter().flat_map(|hash| {
530            let indexes = self
531                .hash_to_meta
532                .get(hash)
533                .map(Vec::as_slice)
534                .unwrap_or_default();
535            indexes.iter().map(|&i| &self.meta[i])
536        }))
537    }
538
539    /// Lookup meta by its hash.
540    #[cfg(any(feature = "cli", feature = "languageserver", feature = "emit"))]
541    pub(crate) fn lookup_meta_by_hash(
542        &self,
543        hash: Hash,
544    ) -> impl ExactSizeIterator<Item = &ContextMeta> + Clone {
545        let indexes = self
546            .hash_to_meta
547            .get(&hash)
548            .map(Vec::as_slice)
549            .unwrap_or_default();
550
551        indexes.iter().map(|&i| &self.meta[i])
552    }
553
554    /// Lookup deprecation by function hash.
555    pub fn lookup_deprecation(&self, hash: Hash) -> Option<&str> {
556        self.deprecations.get(&hash).map(|s| s.as_str())
557    }
558
559    /// Check if unit contains the given name by prefix.
560    pub(crate) fn contains_prefix(&self, item: &Item) -> alloc::Result<bool> {
561        self.names.contains_prefix(item)
562    }
563
564    /// Lookup the given native function handler in the context.
565    pub(crate) fn lookup_function(&self, hash: Hash) -> Option<&FunctionHandler> {
566        self.functions.get(&hash)
567    }
568
569    /// Get all associated types for the given hash.
570    #[cfg(all(feature = "doc", feature = "cli"))]
571    pub(crate) fn associated(&self, hash: Hash) -> impl Iterator<Item = Hash> + '_ {
572        self.associated
573            .get(&hash)
574            .map(Vec::as_slice)
575            .unwrap_or_default()
576            .iter()
577            .copied()
578    }
579
580    /// Get all traits implemented for the given hash.
581    #[cfg(all(feature = "doc", feature = "cli"))]
582    pub(crate) fn traits(&self, hash: Hash) -> impl Iterator<Item = Hash> + '_ {
583        self.implemented_traits
584            .get(&hash)
585            .map(Vec::as_slice)
586            .unwrap_or_default()
587            .iter()
588            .copied()
589    }
590
591    /// Lookup the given macro handler.
592    pub(crate) fn lookup_macro(&self, hash: Hash) -> Option<&MacroHandler> {
593        self.macros.get(&hash)
594    }
595
596    /// Lookup the given attribute macro handler.
597    pub(crate) fn lookup_attribute_macro(&self, hash: Hash) -> Option<&AttributeMacroHandler> {
598        self.attribute_macros.get(&hash)
599    }
600
601    /// Iterate over available crates.
602    #[cfg(feature = "cli")]
603    pub(crate) fn iter_crates(&self) -> impl Iterator<Item = &str> {
604        self.crates.iter().map(|s| s.as_ref())
605    }
606
607    /// Check if context contains the given crate.
608    pub(crate) fn contains_crate(&self, name: &str) -> bool {
609        self.crates.contains(name)
610    }
611
612    /// Test if the context has the default modules installed.
613    ///
614    /// This determines among other things whether a prelude should be used or
615    /// not.
616    pub(crate) fn has_default_modules(&self) -> bool {
617        self.has_default_modules
618    }
619
620    /// Try to find an existing module.
621    fn find_existing_module(&self, hash: Hash) -> Option<usize> {
622        let indexes = self.hash_to_meta.get(&hash)?;
623
624        for &index in indexes {
625            let Some(m) = self.meta.get(index) else {
626                continue;
627            };
628
629            if matches!(m.kind, meta::Kind::Module) {
630                return Some(index);
631            }
632        }
633
634        None
635    }
636
637    /// Install the given meta.
638    #[tracing::instrument(skip_all)]
639    fn install_meta(&mut self, meta: ContextMeta) -> Result<(), ContextError> {
640        if let Some(item) = &meta.item {
641            tracing::trace!(?item);
642
643            self.names.insert(item)?;
644
645            self.item_to_hash
646                .entry(item.try_clone()?)
647                .or_try_default()?
648                .try_insert(meta.hash)?;
649        }
650
651        #[cfg(feature = "doc")]
652        if let Some(h) = meta.kind.associated_container() {
653            let assoc = self.associated.entry(h).or_try_default()?;
654            assoc.try_push(meta.hash)?;
655        }
656
657        let hash = meta.hash;
658
659        let index = self.meta.len();
660
661        self.meta.try_push(meta)?;
662
663        self.hash_to_meta
664            .entry(hash)
665            .or_try_default()?
666            .try_push(index)?;
667
668        Ok(())
669    }
670
671    /// Install a module, ensuring that its meta is defined.
672    fn install_module(&mut self, m: &Module) -> Result<(), ContextError> {
673        self.names.insert(&m.item)?;
674
675        let mut current = Some((m.item.as_ref(), Some(&m.common)));
676
677        #[allow(unused)]
678        while let Some((item, common)) = current.take() {
679            let hash = Hash::type_hash(item);
680
681            if let Some(index) = self.find_existing_module(hash) {
682                #[cfg(feature = "doc")]
683                if let Some(common) = common {
684                    let meta = &mut self.meta[index];
685                    meta.deprecated = common.deprecated.try_clone()?;
686                    meta.docs = common.docs.try_clone()?;
687                }
688            } else {
689                self.install_meta(ContextMeta {
690                    hash,
691                    item: Some(item.try_to_owned()?),
692                    kind: meta::Kind::Module,
693                    #[cfg(feature = "doc")]
694                    deprecated: common
695                        .map(|c| c.deprecated.as_ref().try_cloned())
696                        .transpose()?
697                        .flatten(),
698                    #[cfg(feature = "doc")]
699                    docs: common
700                        .map(|c| c.docs.try_clone())
701                        .transpose()?
702                        .unwrap_or_default(),
703                })?;
704            }
705
706            current = item.parent().map(|item| (item, None));
707        }
708
709        Ok(())
710    }
711
712    /// Install a single type.
713    fn install_type(&mut self, ty: &ModuleType) -> Result<(), ContextError> {
714        self.install_type_info(ContextType {
715            item: ty.item.try_to_owned()?,
716            hash: ty.hash,
717            type_info: ty.type_info.try_clone()?,
718            type_parameters: ty.type_parameters,
719        })?;
720
721        let parameters = Hash::EMPTY.with_type_parameters(ty.type_parameters);
722
723        let kind = if let Some(spec) = &ty.spec {
724            match spec {
725                TypeSpecification::Struct(fields) => {
726                    let constructor = match &ty.constructor {
727                        Some(c) => {
728                            let signature = meta::Signature {
729                                #[cfg(feature = "doc")]
730                                is_async: false,
731                                #[cfg(feature = "doc")]
732                                arguments: Some(fields_to_arguments(fields)?),
733                                #[cfg(feature = "doc")]
734                                return_type: meta::DocType::new(ty.hash),
735                            };
736
737                            if c.args != fields.len() {
738                                return Err(ContextError::ConstructorArgumentsMismatch {
739                                    type_info: ty.type_info.try_clone()?,
740                                    expected: fields.len(),
741                                    actual: c.args,
742                                });
743                            }
744
745                            self.insert_native_fn(&ty.type_info, ty.hash, &c.handler, None)?;
746                            Some(signature)
747                        }
748                        None => None,
749                    };
750
751                    meta::Kind::Struct {
752                        fields: match fields {
753                            Fields::Named(fields) => meta::Fields::Named(meta::FieldsNamed {
754                                fields: fields
755                                    .iter()
756                                    .copied()
757                                    .enumerate()
758                                    .map(|(position, name)| {
759                                        Ok(meta::FieldMeta {
760                                            name: name.try_into()?,
761                                            position,
762                                        })
763                                    })
764                                    .try_collect::<alloc::Result<_>>()??,
765                            }),
766                            Fields::Unnamed(args) => meta::Fields::Unnamed(*args),
767                            Fields::Empty => meta::Fields::Empty,
768                        },
769                        constructor,
770                        parameters,
771                        enum_hash: Hash::EMPTY,
772                    }
773                }
774                TypeSpecification::Enum(en) => {
775                    for variant in &en.variants {
776                        let Some(fields) = &variant.fields else {
777                            continue;
778                        };
779
780                        let kind = match fields {
781                            Fields::Empty => RttiKind::Empty,
782                            Fields::Unnamed(..) => RttiKind::Tuple,
783                            Fields::Named(..) => RttiKind::Struct,
784                        };
785
786                        let item = ty.item.extended(variant.name)?;
787                        let hash = Hash::type_hash(&item);
788
789                        self.install_type_info(ContextType {
790                            item: item.try_clone()?,
791                            hash,
792                            type_info: TypeInfo::rtti(Arc::try_new(Rtti {
793                                kind,
794                                hash: ty.hash,
795                                variant_hash: hash,
796                                item: item.try_clone()?,
797                                fields: fields.to_fields()?,
798                            })?),
799                            type_parameters: Hash::EMPTY,
800                        })?;
801
802                        let constructor = if let Some(c) = &variant.constructor {
803                            let signature = meta::Signature {
804                                #[cfg(feature = "doc")]
805                                is_async: false,
806                                #[cfg(feature = "doc")]
807                                arguments: Some(fields_to_arguments(fields)?),
808                                #[cfg(feature = "doc")]
809                                return_type: meta::DocType::new(ty.hash),
810                            };
811
812                            if c.args != fields.len() {
813                                return Err(ContextError::VariantConstructorArgumentsMismatch {
814                                    type_info: ty.type_info.try_clone()?,
815                                    name: variant.name,
816                                    expected: fields.len(),
817                                    actual: c.args,
818                                });
819                            }
820
821                            self.insert_native_fn(
822                                &item,
823                                hash,
824                                &c.handler,
825                                variant.deprecated.as_deref(),
826                            )?;
827                            Some(signature)
828                        } else {
829                            None
830                        };
831
832                        self.install_meta(ContextMeta {
833                            hash,
834                            item: Some(item),
835                            kind: meta::Kind::Struct {
836                                fields: match fields {
837                                    Fields::Named(names) => {
838                                        meta::Fields::Named(meta::FieldsNamed {
839                                            fields: names
840                                                .iter()
841                                                .copied()
842                                                .enumerate()
843                                                .map(|(position, name)| {
844                                                    Ok(meta::FieldMeta {
845                                                        name: name.try_into()?,
846                                                        position,
847                                                    })
848                                                })
849                                                .try_collect::<alloc::Result<_>>()??,
850                                        })
851                                    }
852                                    Fields::Unnamed(args) => meta::Fields::Unnamed(*args),
853                                    Fields::Empty => meta::Fields::Empty,
854                                },
855                                constructor,
856                                parameters: Hash::EMPTY,
857                                enum_hash: ty.hash,
858                            },
859                            #[cfg(feature = "doc")]
860                            deprecated: variant.deprecated.try_clone()?,
861                            #[cfg(feature = "doc")]
862                            docs: variant.docs.try_clone()?,
863                        })?;
864                    }
865
866                    meta::Kind::Enum { parameters }
867                }
868            }
869        } else {
870            meta::Kind::Type { parameters }
871        };
872
873        self.install_meta(ContextMeta {
874            hash: ty.hash,
875            item: Some(ty.item.try_to_owned()?),
876            kind,
877            #[cfg(feature = "doc")]
878            deprecated: ty.common.deprecated.try_clone()?,
879            #[cfg(feature = "doc")]
880            docs: ty.common.docs.try_clone()?,
881        })?;
882
883        Ok(())
884    }
885
886    fn install_trait(&mut self, t: &ModuleTrait) -> Result<(), ContextError> {
887        if self.traits.try_insert(t.hash, t.handler.clone())?.is_some() {
888            return Err(ContextError::ConflictingTrait {
889                item: t.item.try_clone()?,
890                hash: t.hash,
891            });
892        }
893
894        self.install_meta(ContextMeta {
895            hash: t.hash,
896            item: Some(t.item.try_clone()?),
897            kind: meta::Kind::Trait,
898            #[cfg(feature = "doc")]
899            deprecated: t.common.deprecated.try_clone()?,
900            #[cfg(feature = "doc")]
901            docs: t.common.docs.try_clone()?,
902        })?;
903
904        for f in &t.functions {
905            let signature = meta::Signature::from_context(&f.doc, &f.common)?;
906
907            let kind = meta::Kind::Function {
908                associated: Some(f.name.kind.try_clone()?),
909                trait_hash: None,
910                signature,
911                is_test: false,
912                is_bench: false,
913                parameters: Hash::EMPTY.with_function_parameters(f.name.function_parameters),
914                #[cfg(feature = "doc")]
915                container: Some(t.hash),
916                #[cfg(feature = "doc")]
917                parameter_types: f.name.parameter_types.try_clone()?,
918            };
919
920            let hash = f
921                .name
922                .kind
923                .hash(t.hash)
924                .with_function_parameters(f.name.function_parameters);
925
926            let item = if let meta::AssociatedKind::Instance(name) = &f.name.kind {
927                let item = t.item.extended(name.as_ref())?;
928                let hash = Hash::type_hash(&item);
929                Some((hash, item))
930            } else {
931                None
932            };
933
934            self.install_meta(ContextMeta {
935                hash,
936                item: item.map(|(_, item)| item),
937                kind,
938                #[cfg(feature = "doc")]
939                deprecated: f.common.deprecated.try_clone()?,
940                #[cfg(feature = "doc")]
941                docs: f.common.docs.try_clone()?,
942            })?;
943        }
944
945        Ok(())
946    }
947
948    fn install_trait_impl(&mut self, i: &ModuleTraitImpl) -> Result<(), ContextError> {
949        if !self.types.contains_key(&i.hash) {
950            return Err(ContextError::MissingType {
951                item: i.item.try_to_owned()?,
952                type_info: i.type_info.try_clone()?,
953            });
954        };
955
956        let Some(handler) = self.traits.get(&i.trait_hash).cloned() else {
957            return Err(ContextError::MissingTrait {
958                item: i.trait_item.try_clone()?,
959                hash: i.hash,
960                impl_item: i.item.try_to_owned()?,
961                impl_hash: i.hash,
962            });
963        };
964
965        if let Some(handler) = handler {
966            handler.call(&mut TraitContext {
967                cx: self,
968                item: &i.item,
969                hash: i.hash,
970                type_info: &i.type_info,
971                trait_item: &i.trait_item,
972                trait_hash: i.trait_hash,
973            })?;
974        }
975
976        #[cfg(feature = "doc")]
977        self.implemented_traits
978            .entry(i.hash)
979            .or_try_default()?
980            .try_push(i.trait_hash)?;
981
982        Ok(())
983    }
984
985    fn install_reexport(&mut self, r: &ModuleReexport) -> Result<(), ContextError> {
986        self.install_meta(ContextMeta {
987            hash: r.hash,
988            item: Some(r.item.try_clone()?),
989            kind: meta::Kind::Alias(meta::Alias {
990                to: r.to.try_clone()?,
991            }),
992            #[cfg(feature = "doc")]
993            deprecated: None,
994            #[cfg(feature = "doc")]
995            docs: Docs::EMPTY,
996        })?;
997
998        Ok(())
999    }
1000
1001    /// Install a constant constructor.
1002    fn install_construct(
1003        &mut self,
1004        hash: Hash,
1005        type_info: &AnyTypeInfo,
1006        construct: &ConstConstructImpl,
1007    ) -> Result<(), ContextError> {
1008        let old = self.construct.try_insert(hash, construct.clone())?;
1009
1010        if old.is_some() {
1011            return Err(ContextError::ConflictingConstConstruct {
1012                type_info: TypeInfo::from(*type_info),
1013                hash,
1014            });
1015        }
1016
1017        Ok(())
1018    }
1019
1020    fn install_type_info(&mut self, ty: ContextType) -> Result<(), ContextError> {
1021        let item_hash = Hash::type_hash(&ty.item).with_type_parameters(ty.type_parameters);
1022
1023        if ty.hash != item_hash {
1024            return Err(ContextError::TypeHashMismatch {
1025                type_info: ty.type_info,
1026                item: ty.item,
1027                hash: ty.hash,
1028                item_hash,
1029            });
1030        }
1031
1032        self.constants.try_insert(
1033            Hash::associated_function(ty.hash, &Protocol::INTO_TYPE_NAME),
1034            ConstValue::try_from(ty.item.try_to_string()?)?,
1035        )?;
1036
1037        if let Some(old) = self.types.try_insert(ty.hash, ty)? {
1038            return Err(ContextError::ConflictingType {
1039                item: old.item,
1040                type_info: old.type_info,
1041                hash: old.hash,
1042            });
1043        }
1044
1045        Ok(())
1046    }
1047
1048    /// Install a function and check for duplicates.
1049    fn install_item(&mut self, m: &ModuleItem) -> Result<(), ContextError> {
1050        self.names.insert(&m.item)?;
1051
1052        let kind = match &m.kind {
1053            rune::module::ModuleItemKind::Constant(value) => {
1054                self.constants.try_insert(m.hash, value.try_clone()?)?;
1055                meta::Kind::Const
1056            }
1057            rune::module::ModuleItemKind::Function(f) => {
1058                self.constants.try_insert(
1059                    Hash::associated_function(m.hash, &Protocol::INTO_TYPE_NAME),
1060                    ConstValue::try_from(m.item.try_to_string()?)?,
1061                )?;
1062
1063                let signature = meta::Signature::from_context(&f.doc, &m.common)?;
1064
1065                self.insert_native_fn(&m.item, m.hash, &f.handler, m.common.deprecated.as_deref())?;
1066
1067                meta::Kind::Function {
1068                    associated: None,
1069                    trait_hash: f.trait_hash,
1070                    signature,
1071                    is_test: false,
1072                    is_bench: false,
1073                    parameters: Hash::EMPTY,
1074                    #[cfg(feature = "doc")]
1075                    container: None,
1076                    #[cfg(feature = "doc")]
1077                    parameter_types: Vec::new(),
1078                }
1079            }
1080            rune::module::ModuleItemKind::Macro(macro_) => {
1081                self.macros.try_insert(m.hash, macro_.handler.clone())?;
1082                meta::Kind::Macro
1083            }
1084            rune::module::ModuleItemKind::AttributeMacro(macro_) => {
1085                self.attribute_macros
1086                    .try_insert(m.hash, macro_.handler.clone())?;
1087                meta::Kind::AttributeMacro
1088            }
1089        };
1090
1091        self.install_meta(ContextMeta {
1092            hash: m.hash,
1093            item: Some(m.item.try_to_owned()?),
1094            kind,
1095            #[cfg(feature = "doc")]
1096            deprecated: m.common.deprecated.try_clone()?,
1097            #[cfg(feature = "doc")]
1098            docs: m.common.docs.try_clone()?,
1099        })?;
1100
1101        Ok(())
1102    }
1103
1104    fn install_associated(&mut self, assoc: &ModuleAssociated) -> Result<(), ContextError> {
1105        let Some(info) = self.types.get(&assoc.container).try_cloned()? else {
1106            return Err(ContextError::MissingContainer {
1107                container: assoc.container_type_info.try_clone()?,
1108            });
1109        };
1110
1111        let hash = assoc
1112            .name
1113            .kind
1114            .hash(assoc.container)
1115            .with_function_parameters(assoc.name.function_parameters);
1116
1117        // If the associated function is a named instance function - register it
1118        // under the name of the item it corresponds to unless it's a field
1119        // function.
1120        //
1121        // The other alternatives are protocol functions (which are not free)
1122        // and plain hashes.
1123        let item = if let meta::AssociatedKind::Instance(name) = &assoc.name.kind {
1124            let item = info.item.extended(name.as_ref())?;
1125
1126            let hash = Hash::type_hash(&item)
1127                .with_type_parameters(info.type_parameters)
1128                .with_function_parameters(assoc.name.function_parameters);
1129
1130            Some((hash, item))
1131        } else {
1132            None
1133        };
1134
1135        let kind = match &assoc.kind {
1136            ModuleAssociatedKind::Constant(value) => {
1137                if let Some((hash, ..)) = item {
1138                    self.constants.try_insert(hash, value.try_clone()?)?;
1139                }
1140
1141                self.constants.try_insert(hash, value.try_clone()?)?;
1142                meta::Kind::Const
1143            }
1144            ModuleAssociatedKind::Function(f) => {
1145                let signature = meta::Signature::from_context(&f.doc, &assoc.common)?;
1146
1147                if let Some((hash, item)) = &item {
1148                    self.constants.try_insert(
1149                        Hash::associated_function(*hash, &Protocol::INTO_TYPE_NAME),
1150                        ConstValue::try_from(item.try_to_string()?)?,
1151                    )?;
1152
1153                    self.insert_native_fn(
1154                        &assoc.container_type_info,
1155                        *hash,
1156                        &f.handler,
1157                        assoc.common.deprecated.as_deref(),
1158                    )?;
1159                }
1160
1161                self.insert_native_fn(
1162                    &assoc.container_type_info,
1163                    hash,
1164                    &f.handler,
1165                    assoc.common.deprecated.as_deref(),
1166                )?;
1167
1168                meta::Kind::Function {
1169                    associated: Some(assoc.name.kind.try_clone()?),
1170                    trait_hash: f.trait_hash,
1171                    signature,
1172                    is_test: false,
1173                    is_bench: false,
1174                    parameters: Hash::EMPTY
1175                        .with_type_parameters(info.type_parameters)
1176                        .with_function_parameters(assoc.name.function_parameters),
1177                    #[cfg(feature = "doc")]
1178                    container: Some(assoc.container),
1179                    #[cfg(feature = "doc")]
1180                    parameter_types: assoc.name.parameter_types.try_clone()?,
1181                }
1182            }
1183        };
1184
1185        self.install_meta(ContextMeta {
1186            hash,
1187            item: item.map(|(_, item)| item),
1188            kind,
1189            #[cfg(feature = "doc")]
1190            deprecated: assoc.common.deprecated.try_clone()?,
1191            #[cfg(feature = "doc")]
1192            docs: assoc.common.docs.try_clone()?,
1193        })?;
1194
1195        Ok(())
1196    }
1197
1198    fn insert_native_fn(
1199        &mut self,
1200        display: &dyn fmt::Display,
1201        hash: Hash,
1202        handler: &FunctionHandler,
1203        deprecation: Option<&str>,
1204    ) -> Result<(), ContextError> {
1205        if self.functions.contains_key(&hash) {
1206            return Err(ContextError::ConflictingFunction {
1207                part: display.try_to_string()?.try_into()?,
1208                hash,
1209            });
1210        }
1211
1212        self.functions.try_insert(hash, handler.clone())?;
1213
1214        if let Some(msg) = deprecation {
1215            self.deprecations.try_insert(hash, msg.try_to_owned()?)?;
1216        }
1217
1218        Ok(())
1219    }
1220
1221    /// Get a constant value.
1222    pub(crate) fn get_const_value(&self, hash: Hash) -> Option<&ConstValue> {
1223        self.constants.get(&hash)
1224    }
1225}
1226
1227impl fmt::Debug for Context {
1228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1229        write!(f, "Context")
1230    }
1231}
1232
1233impl ConstContext for Context {
1234    #[inline]
1235    fn get(&self, hash: Hash) -> Option<&ConstConstructImpl> {
1236        self.construct.get(&hash)
1237    }
1238}
1239
1240#[cfg(feature = "doc")]
1241fn fields_to_arguments(fields: &Fields) -> alloc::Result<Box<[meta::DocArgument]>> {
1242    match *fields {
1243        Fields::Named(fields) => {
1244            let mut out = Vec::try_with_capacity(fields.len())?;
1245
1246            for &name in fields {
1247                out.try_push(meta::DocArgument {
1248                    name: meta::DocName::Name(Box::try_from(name)?),
1249                    base: Hash::EMPTY,
1250                    generics: Box::default(),
1251                })?;
1252            }
1253
1254            Box::try_from(out)
1255        }
1256        Fields::Unnamed(args) => {
1257            let mut out = Vec::try_with_capacity(args)?;
1258
1259            for n in 0..args {
1260                out.try_push(meta::DocArgument {
1261                    name: meta::DocName::Index(n),
1262                    base: Hash::EMPTY,
1263                    generics: Box::default(),
1264                })?;
1265            }
1266
1267            Box::try_from(out)
1268        }
1269        Fields::Empty => Ok(Box::default()),
1270    }
1271}
1272
1273#[cfg(test)]
1274static_assertions::assert_impl_all!(Context: Send, Sync);