sui_display/v2/
interpreter.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::borrow::Cow;
5use std::mem;
6use std::sync::Arc;
7
8use dashmap::DashMap;
9use futures::future::OptionFuture;
10use futures::future::join_all;
11use futures::join;
12use move_core_types::account_address::AccountAddress;
13use sui_types::dynamic_field::DynamicFieldType;
14use sui_types::dynamic_field::visitor as DFV;
15use sui_types::dynamic_field::visitor::FieldVisitor;
16
17use crate::v2::error::FormatError;
18use crate::v2::parser as P;
19use crate::v2::value as V;
20use crate::v2::visitor::extractor::Extractor;
21
22/// The interpreter is responsible for evaluating expressions inside format strings into values.
23pub struct Interpreter<S: V::Store> {
24    store: S,
25
26    /// Cache of the objects that have been fetched so far. This cache is never evicted -- it is
27    /// used to keep objects alive for the lifetime of the interpreter.
28    cache: DashMap<AccountAddress, Option<Arc<V::OwnedSlice>>>,
29
30    root: V::OwnedSlice,
31}
32
33impl<S: V::Store> Interpreter<S> {
34    /// Create a new interpreter instance. `root` is the contents (bytes and layout) of an object
35    /// that acts as the root of all field accesses. `store` is used to fetch additional objects as
36    /// needed.
37    pub fn new(root: V::OwnedSlice, store: S) -> Self {
38        Self {
39            store,
40            cache: DashMap::new(),
41            root,
42        }
43    }
44
45    /// Entrypoint to evaluate a single format string, represented as a sequence of its strands.
46    /// Returns evaluated strands that can then be formatted.
47    pub(crate) async fn eval_strands<'s>(
48        &'s self,
49        strands: &'s [P::Strand<'s>],
50    ) -> Result<Option<Vec<V::Strand<'s>>>, FormatError> {
51        join_all(strands.iter().map(|strand| async move {
52            match strand {
53                P::Strand::Text(s) => Ok(Some(V::Strand::Text(s))),
54                P::Strand::Expr(P::Expr {
55                    offset,
56                    alternates,
57                    transform,
58                }) => {
59                    let transform = transform.unwrap_or_default();
60                    Ok(self
61                        .eval_alts(alternates)
62                        .await?
63                        .map(move |value| V::Strand::Value {
64                            value,
65                            transform,
66                            offset: *offset,
67                        }))
68                }
69            }
70        }))
71        .await
72        .into_iter()
73        .collect()
74    }
75
76    /// Evaluate each `chain` in turn until one succeeds (produces a non-`None` value).
77    ///
78    /// Returns the result from the first chain that produces a value, or `Ok(None)` if none do.
79    /// Propagates any errors encountered during evaluation.
80    async fn eval_alts<'s>(
81        &'s self,
82        alts: &'s [P::Chain<'s>],
83    ) -> Result<Option<V::Value<'s>>, FormatError> {
84        for chain in alts {
85            if let Some(v) = self.eval_chain(chain).await? {
86                return Ok(Some(v));
87            }
88        }
89
90        Ok(None)
91    }
92
93    /// Evaluate a chain of field accesses against a root expression.
94    ///
95    /// If the chain does not have a root expression, the object being displayed is used as the
96    /// root. The root is evaluated first, and then each successive accessor is applied to it.
97    ///
98    /// This function returns `Ok(Some(value))` if all nested accesses succeed. An access succeeds
99    /// when the accessor evaluates to `Ok(Some(access))` and the part of the value it is
100    /// describing exists.
101    ///
102    /// Any errors encountered during evaluation are propagated.
103    pub(crate) async fn eval_chain<'s>(
104        &'s self,
105        chain: &'s P::Chain<'s>,
106    ) -> Result<Option<V::Value<'s>>, FormatError> {
107        use V::Accessor as A;
108        use V::Value as VV;
109
110        // Evaluate the root (if it is provided) and the accessors, concurrently.
111        let root: OptionFuture<_> = chain
112            .root
113            .as_ref()
114            .map(|literal| self.eval_literal(literal))
115            .into();
116
117        let accessors = join_all(chain.accessors.iter().map(|a| self.eval_accessor(a)));
118        let (root, accessors) = join!(root, accessors,);
119
120        let mut root = match root {
121            Some(Ok(Some(root))) => root,
122            Some(Ok(None)) => return Ok(None),
123            Some(Err(e)) => return Err(e),
124
125            // If a root was not provided, the object being displayed is the root.
126            None => VV::Slice(self.root.as_slice()),
127        };
128
129        let Some(mut accessors) = accessors
130            .into_iter()
131            .collect::<Result<Option<Vec<_>>, _>>()?
132        else {
133            return Ok(None);
134        };
135
136        accessors.reverse();
137        while let Some(accessor) = accessors.last() {
138            match (root, accessor) {
139                (VV::Address(a), A::DFIndex(i)) => {
140                    let df_id = i.derive_dynamic_field_id(a)?.into();
141                    let Some(slice) = self.object(df_id).await? else {
142                        return Ok(None);
143                    };
144
145                    let field = match FieldVisitor::deserialize(slice.bytes, slice.layout) {
146                        Ok(f) => f,
147                        Err(DFV::Error::Visitor(e)) => return Err(FormatError::Visitor(e)),
148                        Err(_) => return Ok(None),
149                    };
150
151                    if field.kind != DynamicFieldType::DynamicField {
152                        return Ok(None);
153                    }
154
155                    accessors.pop();
156                    root = VV::Slice(V::Slice {
157                        bytes: field.value_bytes,
158                        layout: field.value_layout,
159                    });
160                }
161
162                (VV::Address(a), A::DOFIndex(i)) => {
163                    let df_id = i.derive_dynamic_object_field_id(a)?.into();
164                    let Some(slice) = self.object(df_id).await? else {
165                        return Ok(None);
166                    };
167
168                    let field = match FieldVisitor::deserialize(slice.bytes, slice.layout) {
169                        Ok(f) => f,
170                        Err(DFV::Error::Visitor(e)) => return Err(FormatError::Visitor(e)),
171                        Err(_) => return Ok(None),
172                    };
173
174                    if field.kind != DynamicFieldType::DynamicObject {
175                        return Ok(None);
176                    }
177
178                    let Ok(id) = AccountAddress::from_bytes(field.value_bytes) else {
179                        return Ok(None);
180                    };
181
182                    let Some(value_slice) = self.object(id).await? else {
183                        return Ok(None);
184                    };
185
186                    accessors.pop();
187                    root = VV::Slice(value_slice);
188                }
189
190                (VV::Address(a), A::Derived(i)) => {
191                    let id = i.derive_object_id(a)?.into();
192                    let Some(slice) = self.object(id).await? else {
193                        return Ok(None);
194                    };
195
196                    accessors.pop();
197                    root = VV::Slice(slice);
198                }
199
200                // Fetch a single byte from a byte array, as long as the accessor evaluates to a
201                // numeric index.
202                (VV::Bytes(bs), accessor) => {
203                    let Some(&b) = accessor.as_numeric_index().and_then(|i| bs.get(i as usize))
204                    else {
205                        return Ok(None);
206                    };
207
208                    accessors.pop();
209                    root = VV::U8(b);
210                }
211
212                // `V::String` corresponds to `std::string::String` in Move, which contains a
213                // single `bytes` field.
214                (VV::String(s), A::Field(f)) if *f == "bytes" => {
215                    accessors.pop();
216                    root = VV::Bytes(s)
217                }
218
219                // Fetch an element from a vector literal, as long as the accessor evaluates to a
220                // numeric index.
221                (VV::Vector(mut xs), accessor) => {
222                    let Some(i) = accessor.as_numeric_index() else {
223                        return Ok(None);
224                    };
225
226                    accessors.pop();
227                    root = if i as usize >= xs.elements.len() {
228                        return Ok(None);
229                    } else {
230                        xs.elements.swap_remove(i as usize)
231                    };
232                }
233
234                // Fetch a field from a struct or enum literal.
235                (VV::Struct(V::Struct { fields, .. }) | VV::Enum(V::Enum { fields, .. }), a) => {
236                    let Some(value) = fields.get(a) else {
237                        return Ok(None);
238                    };
239
240                    accessors.pop();
241                    root = value;
242                }
243
244                // Use the remaining accessors to extract a value from a slice of a serialized
245                // value. This can consume multiple accessors, but will pause if it encounters a
246                // dynamic (object) field access.
247                (VV::Slice(slice), _) => {
248                    let Some(value) = Extractor::deserialize_slice(slice, &mut accessors)? else {
249                        return Ok(None);
250                    };
251
252                    root = value;
253                }
254
255                // Scalar values do not support accessors.
256                (
257                    VV::Address(_)
258                    | VV::Bool(_)
259                    | VV::String(_)
260                    | VV::U8(_)
261                    | VV::U16(_)
262                    | VV::U32(_)
263                    | VV::U64(_)
264                    | VV::U128(_)
265                    | VV::U256(_),
266                    _,
267                ) => return Ok(None),
268            }
269        }
270
271        Ok(Some(root))
272    }
273
274    /// Evaluates the contents of an accessor to a value.
275    ///
276    /// Returns `Ok(Some(value))` if the accessor evaluates to a value, otherwise it propagates
277    /// errors or `None` values.
278    async fn eval_accessor<'s>(
279        &'s self,
280        acc: &'s P::Accessor<'s>,
281    ) -> Result<Option<V::Accessor<'s>>, FormatError> {
282        use P::Accessor as PA;
283        use V::Accessor as VA;
284
285        Ok(match acc {
286            PA::Field(f) => Some(VA::Field(f.as_str())),
287            PA::Positional(i) => Some(VA::Positional(*i)),
288            PA::Index(chain) => Box::pin(self.eval_chain(chain)).await?.map(VA::Index),
289            PA::DFIndex(chain) => Box::pin(self.eval_chain(chain)).await?.map(VA::DFIndex),
290            PA::DOFIndex(chain) => Box::pin(self.eval_chain(chain)).await?.map(VA::DOFIndex),
291            PA::Derived(chain) => Box::pin(self.eval_chain(chain)).await?.map(VA::Derived),
292        })
293    }
294
295    /// Evaluate literals to values.
296    ///
297    /// Returns `Ok(Some(value))` if all parts of the literal evaluate to `Ok(Some(value))`,
298    /// otherwise it propagates errors or `None` values.
299    pub(crate) async fn eval_literal<'s>(
300        &'s self,
301        lit: &'s P::Literal<'s>,
302    ) -> Result<Option<V::Value<'s>>, FormatError> {
303        use P::Literal as L;
304        use V::Value as VV;
305
306        Ok(match lit {
307            L::Self_ => Some(VV::Slice(self.root.as_slice())),
308            L::Address(a) => Some(VV::Address(*a)),
309            L::Bool(b) => Some(VV::Bool(*b)),
310            L::U8(n) => Some(VV::U8(*n)),
311            L::U16(n) => Some(VV::U16(*n)),
312            L::U32(n) => Some(VV::U32(*n)),
313            L::U64(n) => Some(VV::U64(*n)),
314            L::U128(n) => Some(VV::U128(*n)),
315            L::U256(n) => Some(VV::U256(*n)),
316            L::ByteArray(bs) => Some(VV::Bytes(bs.into())),
317
318            L::String(s) => match s.clone() {
319                Cow::Borrowed(s) => Some(VV::String(Cow::Borrowed(s.as_bytes()))),
320                Cow::Owned(s) => Some(VV::String(Cow::Owned(s.into_bytes()))),
321            },
322
323            L::Vector(v) => self.eval_chains(&v.elements).await.and_then(|elements| {
324                let Some(elements) = elements else {
325                    return Ok(None);
326                };
327
328                // Evaluate the vector's element type and check that it is consistent across all
329                // elements.
330                let type_ = if let Some(explicit) = &v.type_ {
331                    Cow::Borrowed(explicit)
332                } else if let Some(first) = elements.first() {
333                    Cow::Owned(first.type_())
334                } else {
335                    return Err(FormatError::VectorNoType);
336                };
337
338                for e in &elements {
339                    let element_type = e.type_();
340                    if element_type != *type_ {
341                        return Err(FormatError::VectorTypeMismatch {
342                            offset: v.offset,
343                            this: type_.into_owned(),
344                            that: element_type,
345                        });
346                    }
347                }
348
349                Ok(Some(VV::Vector(V::Vector { type_, elements })))
350            })?,
351
352            L::Struct(s) => self.eval_fields(&s.fields).await?.map(|fields| {
353                VV::Struct(V::Struct {
354                    type_: &s.type_,
355                    fields,
356                })
357            }),
358
359            L::Enum(e) => self.eval_fields(&e.fields).await?.map(|fields| {
360                VV::Enum(V::Enum {
361                    type_: &e.type_,
362                    variant_name: e.variant_name,
363                    variant_index: e.variant_index,
364                    fields,
365                })
366            }),
367        })
368    }
369
370    /// Evaluate the fields of a struct or enum literal, concurrently.
371    ///
372    /// Returns `Ok(Some(fields))` if all the field values evaluate to `Ok(Some(value))`, otherwise
373    /// it propagates errors or `None` values.
374    async fn eval_fields<'s>(
375        &'s self,
376        field: &'s P::Fields<'s>,
377    ) -> Result<Option<V::Fields<'s>>, FormatError> {
378        Ok(match field {
379            P::Fields::Positional(fs) => self.eval_chains(fs).await?.map(V::Fields::Positional),
380            P::Fields::Named(fs) => self
381                .eval_chains(fs.iter().map(|(_, f)| f))
382                .await?
383                .map(|vs| V::Fields::Named(fs.iter().map(|(n, _)| *n).zip(vs).collect())),
384        })
385    }
386
387    /// Evaluate multiple chains concurrently.
388    ///
389    /// If all chains evaluate to `Ok(Some(value))`, returns `Some(vec![value, ...])`, otherwise it
390    /// propagates errors or `None` values.
391    async fn eval_chains<'s>(
392        &'s self,
393        chains: impl IntoIterator<Item = &'s P::Chain<'s>>,
394    ) -> Result<Option<Vec<V::Value<'s>>>, FormatError> {
395        let values = chains
396            .into_iter()
397            .map(|chain| Box::pin(self.eval_chain(chain)));
398
399        join_all(values).await.into_iter().collect()
400    }
401
402    /// Fetch an object from the store, caching the result.
403    ///
404    /// Returns a `Slice<'s>` that borrows from the cached data. The returned slice is guaranteed
405    /// to remain valid for the lifetime 's because the cache (and the Arc it contains) lives for
406    /// the entire lifetime of the Interpreter.
407    async fn object<'s>(&'s self, id: AccountAddress) -> Result<Option<V::Slice<'s>>, FormatError> {
408        let owned = if let Some(cached) = self.cache.get(&id) {
409            cached.clone()
410        } else {
411            let loaded = self
412                .store
413                .object(id)
414                .await
415                .map_err(|e| FormatError::Store(Arc::new(e)))?
416                .map(Arc::new);
417
418            self.cache.entry(id).or_insert(loaded).clone()
419        };
420
421        let Some(owned) = owned.as_ref() else {
422            return Ok(None);
423        };
424
425        // SAFETY: Extending the lifetime of the slice from the reference (local to this
426        // scope), to the lifetime of the interpreter.  This is safe because the reference is
427        // pointing into an Arc that is owned by the interpreter.
428        let slice = owned.as_slice();
429        Ok(Some(unsafe {
430            mem::transmute::<V::Slice<'_>, V::Slice<'s>>(slice)
431        }))
432    }
433}