1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
//! Module-internal structures used while generating nanotransaction variants of user functions.
use std::collections::HashMap;

use nando_support::nando_metadata::NandoKind;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

/// Pair of nanotransaction parameter and corresponding wrapped function argument.
#[derive(Clone, Debug)]
pub(crate) struct ArgumentPair {
    /// Mutability of original function parameter.
    pub is_mutable: bool,
    /// Expression to be used as an argument to the wrapped function.
    pub function_argument: TokenStream2,
    /// Expression to be used as the parameter in the nanotransaction signature that will
    /// eventually be resolved into the corresponding wrapped function argument.
    pub nando_parameter: TokenStream2,

    pub holds_references: bool,

    pub type_str: String,
}

impl Default for ArgumentPair {
    fn default() -> Self {
        Self {
            is_mutable: false,
            function_argument: TokenStream2::default(),
            nando_parameter: TokenStream2::default(),
            holds_references: false,
            type_str: TokenStream2::default().to_string(),
        }
    }
}

/// Information for logging all (potentially) modified values of a given object.
#[derive(Clone, Debug)]
pub(crate) struct LoggingInformation {
    /// List of effectful expressions for the current object.
    pub exprs: Vec<syn::Expr>,
    /// List of [`IPtr`] instances generated for logging.
    ///
    /// [`IPtr`]: ../object_lib/object/struct.IPtr.html
    pub field_iptr_idents: Vec<syn::Ident>,
    /// Instantiation blocks for generated [`IPtr`] instances.
    ///
    /// [`IPtr`]: ../object_lib/object/struct.IPtr.html
    pub field_iptr_instantiations: Vec<TokenStream2>,
}

impl Default for LoggingInformation {
    fn default() -> Self {
        Self {
            exprs: vec![],
            field_iptr_idents: vec![],
            field_iptr_instantiations: vec![],
        }
    }
}

/// Context for invariant pointer resolution and data logging.
#[derive(Clone, Debug)]
pub(crate) struct ReferenceArgMappingContext {
    /// The original parameter identifier.
    pub ident: syn::Ident,
    /// The original parameter's declared type.
    pub ty: syn::Type,
    /// Identifier of the object that will be used to resolve the concrete argument.
    pub object_ident: syn::Ident,
    /// Invariant pointer that will be used to instantiate the object identified by
    /// `object_ident`.
    #[allow(dead_code)]
    pub iptr_ident: syn::Ident,
    pub logging_info: Option<LoggingInformation>,
    pub argument_pair: ArgumentPair,
}

impl ReferenceArgMappingContext {
    pub(crate) fn new(
        ident: syn::Ident,
        ty: syn::Type,
        object_ident: syn::Ident,
        iptr_ident: syn::Ident,
    ) -> Self {
        Self {
            ident,
            ty,
            object_ident,
            iptr_ident,
            logging_info: None,
            argument_pair: ArgumentPair::default(),
        }
    }
}

#[derive(Clone, Debug)]
pub(crate) enum ArgumentMapping {
    Value(ArgumentPair),
    // The associated value is a key into the owning `ArgumentMappingContext`'s reference map.
    Reference(String),
}

/// Parameter mapping context.
///
/// For each function being nando-ized, this struct maintains the mapping between the arguments of
/// the function that is being wrapped with a transactional context and the parameters of the
/// nanotransaction wrapper. For non-reference types, we effectively map via passthrough using the
/// [`ArgumentMapping::Value`] enum.
///
/// [`ArgumentMapping::Value`]: enum.ArgumentMapping.html
#[derive(Clone, Debug)]
pub(crate) struct ArgumentMappingContext {
    /// Order-preserving argument mapping.
    pub mapping: Vec<ArgumentMapping>,
    pub reference_map: HashMap<String, ReferenceArgMappingContext>,

    pub function_kind: NandoKind,
}

impl ArgumentMappingContext {
    pub(crate) fn new() -> Self {
        Self {
            mapping: vec![],
            reference_map: HashMap::new(),
            function_kind: NandoKind::ReadWrite,
        }
    }

    #[allow(dead_code)]
    pub fn is_read_only(&self) -> bool {
        self.function_kind.is_read_only()
    }
}

#[derive(Clone, Debug)]
pub(crate) enum ResolverMappingKind {
    Object,
    RefList,
    Value,
}

impl ResolverMappingKind {
    pub(crate) fn is_ref_list(&self) -> bool {
        match self {
            Self::RefList => true,
            _ => false,
        }
    }
}

#[derive(Clone, Debug)]
pub(crate) struct ResolverMapping {
    pub argument_name: String,
    pub kind: ResolverMappingKind,
    pub type_str: String,
}

#[derive(Clone, Debug)]
pub(crate) struct ResolverMappingContext {
    /// Order-preserving argument mapping.
    pub mapping: Vec<ResolverMapping>,

    pub returns_result: bool,
    pub function_kind: NandoKind,
    pub result_type: String,

    pub spawns_nandos: bool,

    pub generic_mapping_context: Option<GenericVariableMappingContext>,

    pub mutable_argument_indices: Vec<usize>,
    pub cache_invalidations_on_completion: Vec<usize>,
}

impl ResolverMappingContext {
    pub fn is_read_only(&self) -> bool {
        match self.function_kind {
            NandoKind::ReadOnly => true,
            _ => false,
        }
    }
}

impl From<ArgumentMappingContext> for ResolverMappingContext {
    fn from(value: ArgumentMappingContext) -> Self {
        let mut mapping = vec![];
        let mut mutable_argument_indices = vec![];
        for (idx, argument_mapping) in value.mapping.iter().enumerate() {
            mapping.push(match argument_mapping {
                ArgumentMapping::Value(ap) => {
                    let mut kind = ResolverMappingKind::Value;
                    if ap.holds_references {
                        kind = ResolverMappingKind::RefList;
                        if ap.is_mutable {
                            mutable_argument_indices.push(idx);
                        }
                    }

                    ResolverMapping {
                        argument_name: ap.function_argument.to_string(),
                        kind,
                        type_str: ap.type_str.clone(),
                    }
                }
                ArgumentMapping::Reference(s) => {
                    let ref_mapping = value.reference_map.get(s).unwrap();

                    match ref_mapping.argument_pair.is_mutable {
                        false => {}
                        true => mutable_argument_indices.push(idx),
                    }

                    let ty = ref_mapping.ty.clone();
                    ResolverMapping {
                        argument_name: ref_mapping.ident.to_string(),
                        kind: ResolverMappingKind::Object,
                        type_str: quote! { #ty }.to_string(),
                    }
                }
            });
        }

        Self {
            mapping,
            returns_result: false,
            function_kind: value.function_kind,
            result_type: String::default(),
            generic_mapping_context: None,
            spawns_nandos: false,
            mutable_argument_indices,
            cache_invalidations_on_completion: vec![],
        }
    }
}

#[derive(Clone, Debug)]
pub(crate) struct GenericVariableMappingContext {
    name_mapping: HashMap<String, String>,
    pub positional_mapping: Vec<String>,
}

impl GenericVariableMappingContext {
    pub fn new() -> Self {
        Self {
            name_mapping: HashMap::new(),
            positional_mapping: vec![],
        }
    }

    pub fn get_replacement_string(&self) -> String {
        if self.positional_mapping.is_empty() {
            return String::default();
        }

        let mut res = "<".to_owned();
        res.push_str(&self.positional_mapping.join(","));
        res.push('>');

        res
    }

    pub fn insert_positional_mapping(&mut self, concrete_type: String) {
        self.positional_mapping.push(concrete_type);
    }

    pub fn get_mapping_at_position(&self, idx: usize) -> String {
        self.positional_mapping
            .get(idx)
            .expect(&format!(
                "invalid index {} for positional generic argument (max {})",
                idx,
                self.positional_mapping.len()
            ))
            .clone()
    }

    pub fn insert_named_mapping(&mut self, generic_type_var: String, concrete_type: String) {
        self.name_mapping.insert(generic_type_var, concrete_type);
    }

    pub fn get_named_mapping(&self, generic_type_var: &str) -> Option<String> {
        self.name_mapping.get(generic_type_var).cloned()
    }
}