Skip to main content

zerodds_ast/
refs.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Every reference a node holds, in one enumeration. Spec §2.
4//!
5//! Resolution needs three things from a node: which references it has,
6//! what each one may point at, and how to write one back. Spelling that
7//! out per node kind at every call site is how a reference gets
8//! forgotten — a constant named in an array dimension stayed pending
9//! forever because only member and type references were ever indexed.
10//!
11//! The slot is a reference's position in this list. The undo log names
12//! references by it, so the order has to be stable for a given node.
13
14use crate::model::Ast;
15use crate::node::AnyId;
16use crate::node::type_::TypeKind;
17use alloc::vec::Vec;
18use zerodds_ast_core::{BreakCause, PendingRef, Ref};
19
20/// What a reference is allowed to point at.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub enum RefKind {
23    /// A type.
24    Type,
25    /// An interface.
26    Interface,
27    /// An exception.
28    Exception,
29    /// A constant.
30    Const,
31    /// An enumerator.
32    ///
33    /// Distinct from [`RefKind::Const`]: an enumerator is admitted
34    /// wherever a `<const_expr>` is — OMG IDL 4.2 §7.4.1.4.2 — but it is
35    /// not a constant node and does not live in the constants arena.
36    Enumerator,
37    /// An annotation declaration.
38    AnnotationDef,
39}
40
41impl RefKind {
42    /// Every kind a reference may have.
43    pub const ALL: [Self; 6] = [
44        Self::Type,
45        Self::Interface,
46        Self::Exception,
47        Self::Const,
48        Self::Enumerator,
49        Self::AnnotationDef,
50    ];
51
52    /// The name a diagnostic uses.
53    #[must_use]
54    pub const fn as_str(self) -> &'static str {
55        match self {
56            Self::Type => "type",
57            Self::Interface => "interface",
58            Self::Exception => "exception",
59            Self::Const => "constant",
60            Self::Enumerator => "enumerator",
61            Self::AnnotationDef => "annotation",
62        }
63    }
64
65    /// Whether `any` is a node this kind of reference may point at.
66    #[must_use]
67    pub const fn accepts(self, any: AnyId) -> bool {
68        matches!(
69            (self, any),
70            (Self::Type, AnyId::Type(..))
71                | (Self::Interface, AnyId::Interface(..))
72                | (Self::Exception, AnyId::Exception(..))
73                | (Self::Const, AnyId::Const(..))
74                | (Self::Enumerator, AnyId::Enumerator(..))
75                | (Self::AnnotationDef, AnyId::AnnotationDef(..))
76        )
77    }
78}
79
80/// One pending reference of a node.
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct PendingSlot {
83    /// Position in the node's reference list.
84    pub slot: usize,
85    /// What it waits on.
86    pub key: PendingRef,
87    /// What it may point at.
88    pub kind: RefKind,
89}
90
91impl Ast {
92    /// Every pending reference a node holds.
93    ///
94    /// The one place that knows where references live, so adding a
95    /// reference to a node kind is one edit rather than four.
96    #[must_use]
97    pub fn pending_slots(&self, any: AnyId) -> Vec<PendingSlot> {
98        let mut out = Vec::new();
99        let mut slot = 0usize;
100        let mut push = |reference: Option<&PendingRef>, kind: RefKind, at: usize| {
101            if let Some(key) = reference {
102                out.push(PendingSlot {
103                    slot: at,
104                    key: key.clone(),
105                    kind,
106                });
107            }
108        };
109
110        match any {
111            AnyId::Type(id, _) => {
112                let Ok(node) = self.types.get(id) else {
113                    return out;
114                };
115                for reference in node.kind.type_refs() {
116                    push(reference.pending_key(), RefKind::Type, slot);
117                    slot += 1;
118                }
119                for reference in interface_refs_of_kind(&node.kind) {
120                    push(reference.pending_key(), RefKind::Interface, slot);
121                    slot += 1;
122                }
123            }
124            AnyId::Member(id, _) => {
125                if let Ok(node) = self.members.get(id) {
126                    push(node.type_.pending_key(), RefKind::Type, slot);
127                }
128            }
129            // A union case holds its member by handle; the type
130            // reference lives on that member and is indexed there.
131            AnyId::Case(..) => {}
132            AnyId::Bitfield(id, _) => {
133                if let Ok(node) = self.bitfields.get(id) {
134                    push(
135                        node.holder_type.as_ref().and_then(Ref::pending_key),
136                        RefKind::Type,
137                        slot,
138                    );
139                }
140            }
141            AnyId::Const(id, _) => {
142                if let Ok(node) = self.constants.get(id) {
143                    push(node.type_.pending_key(), RefKind::Type, slot);
144                }
145            }
146            AnyId::ConstExpr(id, _) => {
147                if let Ok(node) = self.exprs.get(id) {
148                    match &node.expr {
149                        crate::ConstExpr::NameRef(reference) => {
150                            push(reference.pending_key(), RefKind::Const, slot);
151                        }
152                        crate::ConstExpr::Literal(crate::Literal::Enumerator(reference)) => {
153                            push(reference.pending_key(), RefKind::Enumerator, slot);
154                        }
155                        _ => {}
156                    }
157                }
158            }
159            AnyId::Interface(id, _) => {
160                let Ok(node) = self.interfaces.get(id) else {
161                    return out;
162                };
163                for base in &node.bases {
164                    push(base.pending_key(), RefKind::Interface, slot);
165                    slot += 1;
166                }
167            }
168            AnyId::Operation(id, _) => {
169                let Ok(node) = self.operations.get(id) else {
170                    return out;
171                };
172                push(
173                    node.return_type.as_ref().and_then(Ref::pending_key),
174                    RefKind::Type,
175                    slot,
176                );
177                slot += 1;
178                for raise in &node.raises {
179                    push(raise.pending_key(), RefKind::Exception, slot);
180                    slot += 1;
181                }
182            }
183            AnyId::Param(id, _) => {
184                if let Ok(node) = self.params.get(id) {
185                    push(node.type_.pending_key(), RefKind::Type, slot);
186                }
187            }
188            AnyId::Attribute(id, _) => {
189                let Ok(node) = self.attributes.get(id) else {
190                    return out;
191                };
192                push(node.type_.pending_key(), RefKind::Type, slot);
193                slot += 1;
194                for raise in node.get_raises.iter().chain(node.set_raises.iter()) {
195                    push(raise.pending_key(), RefKind::Exception, slot);
196                    slot += 1;
197                }
198            }
199            AnyId::AnnotationMember(id, _) => {
200                if let Ok(node) = self.annotation_members.get(id) {
201                    push(node.type_.pending_key(), RefKind::Type, slot);
202                }
203            }
204            AnyId::Module(..)
205            | AnyId::Enumerator(..)
206            | AnyId::BitValue(..)
207            | AnyId::Exception(..)
208            | AnyId::AnnotationDef(..)
209            | AnyId::Domain(..)
210            | AnyId::Topic(..)
211            | AnyId::QosProfile(..)
212            | AnyId::Participant(..)
213            | AnyId::Endpoint(..)
214            | AnyId::Library(..) => {}
215        }
216
217        // The CORBA facet holds references of its own — what a home
218        // manages, the interface or event type behind a port, a template
219        // parameter's constraint. They were unreachable from here, so
220        // they were never indexed and never resolved: `manages C
221        // primarykey K` sat `Pending` for the life of the model however
222        // many resolution passes ran over it.
223        if let Some(corba) = self.facets(any).and_then(|facets| facets.corba.as_ref()) {
224            for (index, reference) in corba_refs(corba).into_iter().enumerate() {
225                let (key, kind) = match reference {
226                    CorbaRef::Interface(reference) => (reference.pending_key(), RefKind::Interface),
227                    CorbaRef::Type(reference) => (reference.pending_key(), RefKind::Type),
228                };
229                if let Some(key) = key {
230                    out.push(PendingSlot {
231                        slot: CORBA_SLOT_BASE + index,
232                        key: key.clone(),
233                        kind,
234                    });
235                }
236            }
237        }
238
239        // Applied annotations wait on their declaration, whatever kind of
240        // node they sit on.
241        let base = slot.max(FACET_SLOT_BASE);
242        if let Some(facets) = self.facets(any) {
243            for (index, applied) in facets.annotations.iter().enumerate() {
244                if let Some(key) = applied.definition.pending_key() {
245                    out.push(PendingSlot {
246                        slot: base + index,
247                        key: key.clone(),
248                        kind: RefKind::AnnotationDef,
249                    });
250                }
251            }
252        }
253        out
254    }
255
256    /// Write a reference back, by slot.
257    ///
258    /// Returns whether the slot existed and changed.
259    pub(crate) fn set_slot(&mut self, any: AnyId, slot: usize, target: AnyId) -> bool {
260        if slot >= FACET_SLOT_BASE {
261            return self.set_applied_annotation(any, slot - FACET_SLOT_BASE, target);
262        }
263        if slot >= CORBA_SLOT_BASE {
264            let index = slot - CORBA_SLOT_BASE;
265            let Some(corba) = self.facets_mut(any).and_then(|f| f.corba.as_mut()) else {
266                return false;
267            };
268            return match (corba_refs_mut(corba).into_iter().nth(index), target) {
269                (Some(CorbaRefMut::Interface(reference)), AnyId::Interface(id, _)) => {
270                    replace(reference, id)
271                }
272                (Some(CorbaRefMut::Type(reference)), AnyId::Type(id, _)) => replace(reference, id),
273                _ => false,
274            };
275        }
276        match (any, target) {
277            (AnyId::Type(id, _), AnyId::Type(target, _)) => self
278                .types
279                .get_mut(id)
280                .is_ok_and(|node| set_type_ref_in_kind(&mut node.kind, slot, target)),
281            (AnyId::Type(id, _), AnyId::Interface(target, _)) => {
282                self.types.get_mut(id).is_ok_and(|node| {
283                    let offset = node.kind.type_refs().len();
284                    set_interface_ref_in_kind(&mut node.kind, slot.wrapping_sub(offset), target)
285                })
286            }
287            (AnyId::Member(id, _), AnyId::Type(target, _)) => self
288                .members
289                .get_mut(id)
290                .is_ok_and(|node| replace(&mut node.type_, target)),
291            (AnyId::Bitfield(id, _), AnyId::Type(target, _)) => {
292                self.bitfields.get_mut(id).is_ok_and(|node| {
293                    node.holder_type
294                        .as_mut()
295                        .is_some_and(|reference| replace(reference, target))
296                })
297            }
298            (AnyId::Const(id, _), AnyId::Type(target, _)) => self
299                .constants
300                .get_mut(id)
301                .is_ok_and(|node| replace(&mut node.type_, target)),
302            (AnyId::ConstExpr(id, _), AnyId::Const(target, _)) => {
303                self.exprs.get_mut(id).is_ok_and(|node| {
304                    if let crate::ConstExpr::NameRef(reference) = &mut node.expr {
305                        replace(reference, target)
306                    } else {
307                        false
308                    }
309                })
310            }
311            (AnyId::ConstExpr(id, _), AnyId::Enumerator(target, _)) => {
312                self.exprs.get_mut(id).is_ok_and(|node| {
313                    if let crate::ConstExpr::Literal(crate::Literal::Enumerator(reference)) =
314                        &mut node.expr
315                    {
316                        replace(reference, target)
317                    } else {
318                        false
319                    }
320                })
321            }
322            (AnyId::Interface(id, _), AnyId::Interface(target, _)) => self
323                .interfaces
324                .get_mut(id)
325                .is_ok_and(|node| node.bases.get_mut(slot).is_some_and(|b| replace(b, target))),
326            (AnyId::Operation(id, _), AnyId::Type(target, _)) => {
327                self.operations.get_mut(id).is_ok_and(|node| {
328                    slot == 0
329                        && node
330                            .return_type
331                            .as_mut()
332                            .is_some_and(|reference| replace(reference, target))
333                })
334            }
335            (AnyId::Operation(id, _), AnyId::Exception(target, _)) => {
336                self.operations.get_mut(id).is_ok_and(|node| {
337                    node.raises
338                        .get_mut(slot.wrapping_sub(1))
339                        .is_some_and(|reference| replace(reference, target))
340                })
341            }
342            (AnyId::Param(id, _), AnyId::Type(target, _)) => self
343                .params
344                .get_mut(id)
345                .is_ok_and(|node| replace(&mut node.type_, target)),
346            (AnyId::Attribute(id, _), AnyId::Type(target, _)) => self
347                .attributes
348                .get_mut(id)
349                .is_ok_and(|node| slot == 0 && replace(&mut node.type_, target)),
350            (AnyId::Attribute(id, _), AnyId::Exception(target, _)) => {
351                self.attributes.get_mut(id).is_ok_and(|node| {
352                    let at = slot.wrapping_sub(1);
353                    let getters = node.get_raises.len();
354                    if at < getters {
355                        node.get_raises
356                            .get_mut(at)
357                            .is_some_and(|reference| replace(reference, target))
358                    } else {
359                        node.set_raises
360                            .get_mut(at - getters)
361                            .is_some_and(|reference| replace(reference, target))
362                    }
363                })
364            }
365            (AnyId::AnnotationMember(id, _), AnyId::Type(target, _)) => self
366                .annotation_members
367                .get_mut(id)
368                .is_ok_and(|node| replace(&mut node.type_, target)),
369            _ => false,
370        }
371    }
372
373    /// Put a reference into the broken state, by slot.
374    pub(crate) fn break_slot(&mut self, any: AnyId, slot: usize, cause: BreakCause) -> bool {
375        if slot >= FACET_SLOT_BASE {
376            let index = slot - FACET_SLOT_BASE;
377            let Some(facets) = self.facets_mut(any) else {
378                return false;
379            };
380            return facets
381                .annotations
382                .get_mut(index)
383                .is_some_and(|applied| break_ref(&mut applied.definition, cause));
384        }
385        if slot >= CORBA_SLOT_BASE {
386            let index = slot - CORBA_SLOT_BASE;
387            let Some(corba) = self.facets_mut(any).and_then(|f| f.corba.as_mut()) else {
388                return false;
389            };
390            return match corba_refs_mut(corba).into_iter().nth(index) {
391                Some(CorbaRefMut::Interface(reference)) => break_ref(reference, cause),
392                Some(CorbaRefMut::Type(reference)) => break_ref(reference, cause),
393                None => false,
394            };
395        }
396        match any {
397            AnyId::Type(id, _) => self.types.get_mut(id).is_ok_and(|node| {
398                let types = node.kind.type_refs_mut().len();
399                if slot < types {
400                    node.kind
401                        .type_refs_mut()
402                        .into_iter()
403                        .nth(slot)
404                        .is_some_and(|reference| break_ref(reference, cause))
405                } else {
406                    break_interface_ref_in_kind(&mut node.kind, slot - types, cause)
407                }
408            }),
409            AnyId::Member(id, _) => self
410                .members
411                .get_mut(id)
412                .is_ok_and(|node| break_ref(&mut node.type_, cause)),
413            AnyId::Bitfield(id, _) => self.bitfields.get_mut(id).is_ok_and(|node| {
414                node.holder_type
415                    .as_mut()
416                    .is_some_and(|reference| break_ref(reference, cause))
417            }),
418            AnyId::Const(id, _) => self
419                .constants
420                .get_mut(id)
421                .is_ok_and(|node| break_ref(&mut node.type_, cause)),
422            AnyId::ConstExpr(id, _) => {
423                self.exprs
424                    .get_mut(id)
425                    .is_ok_and(|node| match &mut node.expr {
426                        crate::ConstExpr::NameRef(reference) => break_ref(reference, cause),
427                        crate::ConstExpr::Literal(crate::Literal::Enumerator(reference)) => {
428                            break_ref(reference, cause)
429                        }
430                        _ => false,
431                    })
432            }
433            AnyId::Interface(id, _) => self.interfaces.get_mut(id).is_ok_and(|node| {
434                node.bases
435                    .get_mut(slot)
436                    .is_some_and(|reference| break_ref(reference, cause))
437            }),
438            AnyId::Operation(id, _) => self.operations.get_mut(id).is_ok_and(|node| {
439                if slot == 0 {
440                    node.return_type
441                        .as_mut()
442                        .is_some_and(|reference| break_ref(reference, cause))
443                } else {
444                    node.raises
445                        .get_mut(slot - 1)
446                        .is_some_and(|reference| break_ref(reference, cause))
447                }
448            }),
449            AnyId::Param(id, _) => self
450                .params
451                .get_mut(id)
452                .is_ok_and(|node| break_ref(&mut node.type_, cause)),
453            AnyId::Attribute(id, _) => self.attributes.get_mut(id).is_ok_and(|node| {
454                if slot == 0 {
455                    return break_ref(&mut node.type_, cause);
456                }
457                let at = slot - 1;
458                let getters = node.get_raises.len();
459                if at < getters {
460                    node.get_raises
461                        .get_mut(at)
462                        .is_some_and(|reference| break_ref(reference, cause))
463                } else {
464                    node.set_raises
465                        .get_mut(at - getters)
466                        .is_some_and(|reference| break_ref(reference, cause))
467                }
468            }),
469            AnyId::AnnotationMember(id, _) => self
470                .annotation_members
471                .get_mut(id)
472                .is_ok_and(|node| break_ref(&mut node.type_, cause)),
473            _ => false,
474        }
475    }
476
477    fn set_applied_annotation(&mut self, any: AnyId, index: usize, target: AnyId) -> bool {
478        let AnyId::AnnotationDef(definition, _) = target else {
479            return false;
480        };
481        let Some(facets) = self.facets_mut(any) else {
482            return false;
483        };
484        facets
485            .annotations
486            .get_mut(index)
487            .is_some_and(|applied| replace(&mut applied.definition, definition))
488    }
489}
490
491/// Where the applied-annotation slots begin.
492///
493/// Far above any node's own reference count, so a facet slot can never
494/// be mistaken for a structural one. Node reference lists are short —
495/// the widest is a valuetype's base list — and this leaves room.
496const FACET_SLOT_BASE: usize = 1 << 20;
497
498/// Where the CORBA-facet slots begin.
499///
500/// Its own region below [`FACET_SLOT_BASE`], for the same reason: a
501/// facet reference must never be mistaken for a structural one, and the
502/// two facet regions must not be mistaken for each other.
503const CORBA_SLOT_BASE: usize = 1 << 19;
504
505/// One reference held by a [`crate::CorbaFacet`], borrowed.
506enum CorbaRef<'a> {
507    /// Names an interface.
508    Interface(&'a Ref<crate::InterfaceNode>),
509    /// Names a type.
510    Type(&'a Ref<crate::TypeNode>),
511}
512
513/// The same, mutably.
514enum CorbaRefMut<'a> {
515    /// Names an interface.
516    Interface(&'a mut Ref<crate::InterfaceNode>),
517    /// Names a type.
518    Type(&'a mut Ref<crate::TypeNode>),
519}
520
521/// Every reference a CORBA facet holds, in slot order.
522///
523/// The order is the slot numbering, so a reference is appended here
524/// rather than inserted; [`corba_refs_mut`] walks it identically and the
525/// two have to stay in step.
526fn corba_refs(corba: &crate::CorbaFacet) -> Vec<CorbaRef<'_>> {
527    let mut out = Vec::new();
528    match corba.component_role.as_ref() {
529        Some(crate::ComponentRole::Facet { interface, .. }) => {
530            out.push(CorbaRef::Interface(interface));
531        }
532        Some(crate::ComponentRole::Receptacle { interface, .. }) => {
533            out.push(CorbaRef::Interface(interface));
534        }
535        Some(crate::ComponentRole::EventSource { event, .. }) => {
536            out.push(CorbaRef::Type(event));
537        }
538        Some(crate::ComponentRole::EventSink { event, .. }) => {
539            out.push(CorbaRef::Type(event));
540        }
541        Some(
542            crate::ComponentRole::HomeFactory
543            | crate::ComponentRole::HomeFinder
544            | crate::ComponentRole::PortTypeRole
545            | crate::ComponentRole::ConnectorRole,
546        )
547        | None => {}
548    }
549    if let Some(manages) = corba.manages.as_ref() {
550        out.push(CorbaRef::Interface(&manages.component));
551        if let Some(key) = manages.primary_key.as_ref() {
552            out.push(CorbaRef::Type(key));
553        }
554    }
555    for interface in &corba.supports {
556        out.push(CorbaRef::Interface(interface));
557    }
558    for param in &corba.template_params {
559        if let Some(constraint) = param.constraint.as_ref() {
560            out.push(CorbaRef::Type(constraint));
561        }
562    }
563    for arg in &corba.template_args {
564        if let crate::TemplateArg::Type(reference) = arg {
565            out.push(CorbaRef::Type(reference));
566        }
567    }
568    out
569}
570
571/// The same references, mutably and in the same order.
572fn corba_refs_mut(corba: &mut crate::CorbaFacet) -> Vec<CorbaRefMut<'_>> {
573    let mut out = Vec::new();
574    match corba.component_role.as_mut() {
575        Some(crate::ComponentRole::Facet { interface, .. }) => {
576            out.push(CorbaRefMut::Interface(interface));
577        }
578        Some(crate::ComponentRole::Receptacle { interface, .. }) => {
579            out.push(CorbaRefMut::Interface(interface));
580        }
581        Some(crate::ComponentRole::EventSource { event, .. }) => {
582            out.push(CorbaRefMut::Type(event));
583        }
584        Some(crate::ComponentRole::EventSink { event, .. }) => {
585            out.push(CorbaRefMut::Type(event));
586        }
587        Some(
588            crate::ComponentRole::HomeFactory
589            | crate::ComponentRole::HomeFinder
590            | crate::ComponentRole::PortTypeRole
591            | crate::ComponentRole::ConnectorRole,
592        )
593        | None => {}
594    }
595    if let Some(manages) = corba.manages.as_mut() {
596        out.push(CorbaRefMut::Interface(&mut manages.component));
597        if let Some(key) = manages.primary_key.as_mut() {
598            out.push(CorbaRefMut::Type(key));
599        }
600    }
601    for interface in &mut corba.supports {
602        out.push(CorbaRefMut::Interface(interface));
603    }
604    for param in &mut corba.template_params {
605        if let Some(constraint) = param.constraint.as_mut() {
606            out.push(CorbaRefMut::Type(constraint));
607        }
608    }
609    for arg in &mut corba.template_args {
610        if let crate::TemplateArg::Type(reference) = arg {
611            out.push(CorbaRefMut::Type(reference));
612        }
613    }
614    out
615}
616
617fn replace<T>(reference: &mut Ref<T>, target: zerodds_ast_core::Id<T>) -> bool {
618    if reference.is_pending() {
619        *reference = Ref::Resolved(target);
620        return true;
621    }
622    false
623}
624
625fn break_ref<T>(reference: &mut Ref<T>, cause: BreakCause) -> bool {
626    if reference.is_pending() {
627        *reference = Ref::Broken(cause);
628        return true;
629    }
630    false
631}
632
633fn break_interface_ref_in_kind(kind: &mut TypeKind, slot: usize, cause: BreakCause) -> bool {
634    match kind {
635        TypeKind::Valuetype { supported, .. } => supported
636            .get_mut(slot)
637            .is_some_and(|reference| break_ref(reference, cause)),
638        // The generic `Object` names no interface — rule (117) — so
639        // there is no slot zero to break.
640        TypeKind::ObjectRef {
641            interface: Some(interface),
642        } => slot == 0 && break_ref(interface, cause),
643        _ => false,
644    }
645}
646
647/// The interface references a type kind holds, after its type ones.
648fn interface_refs_of_kind(kind: &TypeKind) -> Vec<&Ref<crate::InterfaceNode>> {
649    match kind {
650        TypeKind::Valuetype { supported, .. } => supported.iter().collect(),
651        TypeKind::ObjectRef {
652            interface: Some(interface),
653        } => alloc::vec![interface],
654        _ => Vec::new(),
655    }
656}
657
658fn set_type_ref_in_kind(
659    kind: &mut TypeKind,
660    slot: usize,
661    target: zerodds_ast_core::Id<crate::TypeNode>,
662) -> bool {
663    kind.type_refs_mut()
664        .into_iter()
665        .nth(slot)
666        .is_some_and(|reference| replace(reference, target))
667}
668
669fn set_interface_ref_in_kind(
670    kind: &mut TypeKind,
671    slot: usize,
672    target: zerodds_ast_core::Id<crate::InterfaceNode>,
673) -> bool {
674    match kind {
675        TypeKind::Valuetype { supported, .. } => supported
676            .get_mut(slot)
677            .is_some_and(|reference| replace(reference, target)),
678        TypeKind::ObjectRef {
679            interface: Some(interface),
680        } => slot == 0 && replace(interface, target),
681        _ => false,
682    }
683}