rib/compiler/
byte_code.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Golem Source License v1.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://license.golem.cloud/LICENSE
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::compiler::byte_code::internal::ExprState;
16use crate::compiler::ir::RibIR;
17use crate::type_inference::TypeHint;
18use crate::{Expr, InferredExpr, InstructionId};
19use bincode::{Decode, Encode};
20use std::fmt::{Display, Formatter};
21
22#[derive(Debug, Clone, Default, PartialEq, Encode, Decode)]
23pub struct RibByteCode {
24    pub instructions: Vec<RibIR>,
25}
26
27#[derive(Debug, Clone, PartialEq)]
28pub enum RibByteCodeGenerationError {
29    CastError(String),
30    AnalysedTypeConversionError(String),
31    PatternMatchDesugarError,
32    RangeSelectionDesugarError(String),
33    UnexpectedTypeError {
34        expected: TypeHint,
35        actual: TypeHint,
36    },
37    UnresolvedWasmComponent {
38        function: String,
39    },
40    UnresolvedWorkerName,
41    UnresolvedResourceVariable,
42}
43
44impl std::error::Error for RibByteCodeGenerationError {}
45
46impl Display for RibByteCodeGenerationError {
47    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48        match self {
49            RibByteCodeGenerationError::CastError(msg) => write!(f, "cast error: {}", msg),
50            RibByteCodeGenerationError::AnalysedTypeConversionError(msg) => {
51                write!(f, "{}", msg)
52            }
53            RibByteCodeGenerationError::PatternMatchDesugarError => {
54                write!(f, "Pattern match desugar error")
55            }
56            RibByteCodeGenerationError::RangeSelectionDesugarError(msg) => {
57                write!(f, "Range selection desugar error: {}", msg)
58            }
59            RibByteCodeGenerationError::UnexpectedTypeError { expected, actual } => {
60                write!(
61                    f,
62                    "Expected type: {}, but got: {}",
63                    expected.get_type_kind(),
64                    actual.get_type_kind()
65                )
66            }
67            RibByteCodeGenerationError::UnresolvedWasmComponent { function } => {
68                write!(f, "Unresolved wasm component for function: {}", function)
69            }
70            RibByteCodeGenerationError::UnresolvedWorkerName => {
71                write!(f, "inline invocation of functions on a worker instance is currently not supported")
72            }
73            _ => {
74                write!(f, "inline invocation of methods on resource constructor instance is currently not supported")
75            }
76        }
77    }
78}
79
80impl RibByteCode {
81    pub fn len(&self) -> usize {
82        self.instructions.len()
83    }
84
85    pub fn is_empty(&self) -> bool {
86        self.instructions.is_empty()
87    }
88
89    // Convert expression to bytecode instructions
90    pub fn from_expr(
91        inferred_expr: &InferredExpr,
92    ) -> Result<RibByteCode, RibByteCodeGenerationError> {
93        let expr: &Expr = inferred_expr.get_expr();
94        let mut instructions = Vec::new();
95        let mut stack: Vec<ExprState> = Vec::new();
96        let mut instruction_id = InstructionId::init();
97        stack.push(ExprState::from_expr(expr));
98
99        while let Some(remaining) = stack.pop() {
100            match remaining {
101                ExprState::Expr(expr) => {
102                    internal::process_expr(
103                        &expr,
104                        &mut stack,
105                        &mut instructions,
106                        &mut instruction_id,
107                    )?;
108                }
109
110                ExprState::Instruction(instruction) => {
111                    instructions.push(instruction);
112                }
113            }
114        }
115
116        // Use VecDeque to avoid reversal, but ok as well since this is compilation
117        Ok(RibByteCode {
118            instructions: instructions.into_iter().rev().collect(),
119        })
120    }
121}
122
123#[cfg(feature = "protobuf")]
124mod protobuf {
125    use crate::RibByteCode;
126    use golem_api_grpc::proto::golem::rib::RibByteCode as ProtoRibByteCode;
127
128    impl TryFrom<ProtoRibByteCode> for RibByteCode {
129        type Error = String;
130
131        fn try_from(value: ProtoRibByteCode) -> Result<Self, Self::Error> {
132            let proto_instructions = value.instructions;
133            let mut instructions = Vec::new();
134
135            for proto_instruction in proto_instructions {
136                instructions.push(proto_instruction.try_into()?);
137            }
138
139            Ok(RibByteCode { instructions })
140        }
141    }
142
143    impl TryFrom<RibByteCode> for ProtoRibByteCode {
144        type Error = String;
145
146        fn try_from(value: RibByteCode) -> Result<Self, Self::Error> {
147            let mut instructions = Vec::new();
148            for instruction in value.instructions {
149                instructions.push(instruction.try_into()?);
150            }
151
152            Ok(ProtoRibByteCode { instructions })
153        }
154    }
155}
156
157mod internal {
158    use crate::compiler::desugar::{desugar_pattern_match, desugar_range_selection};
159    use crate::{
160        AnalysedTypeWithUnit, DynamicParsedFunctionReference, Expr, FunctionReferenceType,
161        InferredType, InstanceIdentifier, InstanceVariable, InstructionId, Range,
162        RibByteCodeGenerationError, RibIR, TypeInternal, VariableId,
163    };
164    use golem_wasm_ast::analysis::{AnalysedType, TypeFlags};
165    use std::collections::HashSet;
166
167    use crate::call_type::{CallType, InstanceCreationType};
168    use crate::type_inference::{GetTypeHint, TypeHint};
169    use golem_wasm_ast::analysis::analysed_type::bool;
170    use golem_wasm_rpc::{IntoValueAndType, Value, ValueAndType};
171    use std::ops::Deref;
172
173    pub(crate) fn process_expr(
174        expr: &Expr,
175        stack: &mut Vec<ExprState>,
176        instructions: &mut Vec<RibIR>,
177        instruction_id: &mut InstructionId,
178    ) -> Result<(), RibByteCodeGenerationError> {
179        match expr {
180            Expr::Unwrap { expr, .. } => {
181                stack.push(ExprState::from_expr(expr.deref()));
182                instructions.push(RibIR::Deconstruct);
183            }
184
185            Expr::GenerateWorkerName { variable_id, .. } => {
186                instructions.push(RibIR::GenerateWorkerName(variable_id.clone()));
187            }
188
189            Expr::Length { expr, .. } => {
190                stack.push(ExprState::from_expr(expr.deref()));
191                instructions.push(RibIR::Length);
192            }
193
194            Expr::Throw { message, .. } => {
195                instructions.push(RibIR::Throw(message.to_string()));
196            }
197            Expr::Identifier { variable_id, .. } => {
198                instructions.push(RibIR::LoadVar(variable_id.clone()));
199            }
200            Expr::Literal { value, .. } => {
201                let value_and_type = value.clone().into_value_and_type();
202                instructions.push(RibIR::PushLit(value_and_type));
203            }
204            Expr::Number {
205                number,
206                inferred_type,
207                ..
208            } => {
209                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
210
211                let value_and_type =
212                    number
213                        .to_val(&analysed_type)
214                        .ok_or(RibByteCodeGenerationError::CastError(format!(
215                            "internal error: cannot convert {} to a value of type {}",
216                            number.value,
217                            analysed_type.get_type_hint()
218                        )))?;
219
220                instructions.push(RibIR::PushLit(value_and_type));
221            }
222            Expr::EqualTo { lhs, rhs, .. } => {
223                stack.push(ExprState::from_expr(rhs.deref()));
224                stack.push(ExprState::from_expr(lhs.deref()));
225                instructions.push(RibIR::EqualTo);
226            }
227            Expr::GreaterThan { lhs, rhs, .. } => {
228                stack.push(ExprState::from_expr(rhs.deref()));
229                stack.push(ExprState::from_expr(lhs.deref()));
230                instructions.push(RibIR::GreaterThan);
231            }
232            Expr::LessThan { lhs, rhs, .. } => {
233                stack.push(ExprState::from_expr(rhs.deref()));
234                stack.push(ExprState::from_expr(lhs.deref()));
235                instructions.push(RibIR::LessThan);
236            }
237            Expr::GreaterThanOrEqualTo { lhs, rhs, .. } => {
238                stack.push(ExprState::from_expr(rhs.deref()));
239                stack.push(ExprState::from_expr(lhs.deref()));
240                instructions.push(RibIR::GreaterThanOrEqualTo);
241            }
242            Expr::LessThanOrEqualTo { lhs, rhs, .. } => {
243                stack.push(ExprState::from_expr(rhs.deref()));
244                stack.push(ExprState::from_expr(lhs.deref()));
245                instructions.push(RibIR::LessThanOrEqualTo);
246            }
247            Expr::Plus {
248                lhs,
249                rhs,
250                inferred_type,
251                ..
252            } => {
253                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
254                stack.push(ExprState::from_expr(rhs.deref()));
255                stack.push(ExprState::from_expr(lhs.deref()));
256                instructions.push(RibIR::Plus(analysed_type));
257            }
258            Expr::Minus {
259                lhs,
260                rhs,
261                inferred_type,
262                ..
263            } => {
264                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
265
266                stack.push(ExprState::from_expr(rhs.deref()));
267                stack.push(ExprState::from_expr(lhs.deref()));
268                instructions.push(RibIR::Minus(analysed_type));
269            }
270            Expr::Divide {
271                lhs,
272                rhs,
273                inferred_type,
274                ..
275            } => {
276                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
277
278                stack.push(ExprState::from_expr(rhs.deref()));
279                stack.push(ExprState::from_expr(lhs.deref()));
280                instructions.push(RibIR::Divide(analysed_type));
281            }
282            Expr::Multiply {
283                lhs,
284                rhs,
285                inferred_type,
286                ..
287            } => {
288                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
289
290                stack.push(ExprState::from_expr(rhs.deref()));
291                stack.push(ExprState::from_expr(lhs.deref()));
292                instructions.push(RibIR::Multiply(analysed_type));
293            }
294            Expr::And { lhs, rhs, .. } => {
295                // This optimization isn't optional, it's required for the correct functioning of the interpreter
296                let optimised_expr = Expr::cond(
297                    Expr::equal_to(lhs.deref().clone(), Expr::boolean(true)),
298                    Expr::equal_to(rhs.deref().clone(), Expr::boolean(true)),
299                    Expr::boolean(false),
300                );
301
302                stack.push(ExprState::from_expr(&optimised_expr));
303            }
304
305            Expr::Or { lhs, rhs, .. } => {
306                let optimised_expr = Expr::cond(
307                    Expr::equal_to(lhs.deref().clone(), Expr::boolean(true)),
308                    Expr::boolean(true),
309                    Expr::equal_to(rhs.deref().clone(), Expr::boolean(true)),
310                );
311
312                stack.push(ExprState::from_expr(&optimised_expr));
313            }
314
315            Expr::Record {
316                exprs,
317                inferred_type,
318                ..
319            } => {
320                // Push field instructions in reverse order
321                for (field_name, field_expr) in exprs.iter().rev() {
322                    stack.push(ExprState::from_expr(field_expr.as_ref()));
323                    instructions.push(RibIR::UpdateRecord(field_name.clone()));
324                }
325                // Push record creation instruction
326                let analysed_type = convert_to_analysed_type(expr, inferred_type);
327                instructions.push(RibIR::CreateAndPushRecord(analysed_type?));
328            }
329            Expr::Sequence {
330                exprs,
331                inferred_type,
332                ..
333            } => {
334                // Push all expressions in reverse order
335                for expr in exprs.iter().rev() {
336                    stack.push(ExprState::from_expr(expr));
337                }
338
339                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
340                instructions.push(RibIR::PushList(analysed_type, exprs.len()));
341            }
342            Expr::ExprBlock { exprs, .. } => {
343                // Push all expressions in reverse order
344                for expr in exprs.iter() {
345                    stack.push(ExprState::from_expr(expr));
346                }
347            }
348            Expr::Let {
349                variable_id, expr, ..
350            } => {
351                stack.push(ExprState::from_expr(expr.deref()));
352                instructions.push(RibIR::AssignVar(variable_id.clone()));
353            }
354            Expr::PatternMatch {
355                predicate,
356                match_arms,
357                inferred_type,
358                ..
359            } => {
360                let desugared_pattern_match =
361                    desugar_pattern_match(predicate.deref(), match_arms, inferred_type.clone())
362                        .ok_or(RibByteCodeGenerationError::PatternMatchDesugarError)?;
363
364                stack.push(ExprState::from_expr(&desugared_pattern_match));
365            }
366            Expr::Cond { cond, lhs, rhs, .. } => {
367                handle_if_condition(
368                    instruction_id,
369                    cond.deref(),
370                    lhs.deref(),
371                    rhs.deref(),
372                    stack,
373                );
374            }
375
376            Expr::SelectField { expr, field, .. } => {
377                stack.push(ExprState::from_expr(expr.deref()));
378                instructions.push(RibIR::SelectField(field.clone()));
379            }
380
381            Expr::SelectIndex { expr, index, .. } => match index.inferred_type().internal_type() {
382                TypeInternal::Range { .. } => {
383                    let list_comprehension =
384                        desugar_range_selection(expr, index).map_err(|err| {
385                            RibByteCodeGenerationError::RangeSelectionDesugarError(format!(
386                                "Failed to desugar range selection: {}",
387                                err
388                            ))
389                        })?;
390                    stack.push(ExprState::from_expr(&list_comprehension));
391                }
392                _ => {
393                    stack.push(ExprState::from_expr(index.deref()));
394                    stack.push(ExprState::from_expr(expr.deref()));
395                    instructions.push(RibIR::SelectIndexV1);
396                }
397            },
398
399            Expr::Option {
400                expr: Some(inner_expr),
401                inferred_type,
402                ..
403            } => {
404                stack.push(ExprState::from_expr(inner_expr.deref()));
405                instructions.push(RibIR::PushSome(convert_to_analysed_type(
406                    expr,
407                    inferred_type,
408                )?));
409            }
410
411            Expr::Option { inferred_type, .. } => {
412                let optional = convert_to_analysed_type(expr, inferred_type);
413                instructions.push(RibIR::PushNone(optional.ok()));
414            }
415
416            Expr::Result {
417                expr: Ok(inner_expr),
418                inferred_type,
419                ..
420            } => {
421                stack.push(ExprState::from_expr(inner_expr.deref()));
422                instructions.push(RibIR::PushOkResult(convert_to_analysed_type(
423                    expr,
424                    inferred_type,
425                )?));
426            }
427
428            Expr::Result {
429                expr: Err(inner_expr),
430                inferred_type,
431                ..
432            } => {
433                stack.push(ExprState::from_expr(inner_expr.deref()));
434                instructions.push(RibIR::PushErrResult(convert_to_analysed_type(
435                    expr,
436                    inferred_type,
437                )?));
438            }
439
440            Expr::Call {
441                call_type,
442                args,
443                inferred_type,
444                ..
445            } => {
446                // If the call type is an instance creation (worker creation),
447                // this will push worker name expression.
448                match call_type {
449                    CallType::Function {
450                        function_name,
451                        instance_identifier: module,
452                        component_info,
453                    } => {
454                        for expr in args.iter().rev() {
455                            stack.push(ExprState::from_expr(expr));
456                        }
457
458                        let function_result_type = if inferred_type.is_unit() {
459                            AnalysedTypeWithUnit::Unit
460                        } else {
461                            AnalysedTypeWithUnit::Type(convert_to_analysed_type(
462                                expr,
463                                inferred_type,
464                            )?)
465                        };
466
467                        let module = module
468                            .as_ref()
469                            .expect("Module should be present for function calls");
470
471                        let instance_variable = match module.as_ref() {
472                            InstanceIdentifier::WitResource { variable_id, .. } => {
473                                let variable_id = variable_id.clone().ok_or({
474                                    RibByteCodeGenerationError::UnresolvedResourceVariable
475                                })?;
476                                InstanceVariable::WitResource(variable_id)
477                            }
478                            InstanceIdentifier::WitWorker { variable_id, .. } => {
479                                let variable_id = variable_id
480                                    .clone()
481                                    .ok_or(RibByteCodeGenerationError::UnresolvedWorkerName)?;
482
483                                InstanceVariable::WitWorker(variable_id)
484                            }
485                        };
486
487                        let component_info = component_info.as_ref().ok_or(
488                            RibByteCodeGenerationError::UnresolvedWasmComponent {
489                                function: function_name.to_string(),
490                            },
491                        )?;
492
493                        instructions.push(RibIR::InvokeFunction(
494                            component_info.clone(),
495                            instance_variable,
496                            args.len(),
497                            function_result_type,
498                        ));
499
500                        let site = function_name.site.clone();
501
502                        // Resolve the function name and update stack
503                        match &function_name.function {
504                            DynamicParsedFunctionReference::Function { function } => instructions
505                                .push(RibIR::CreateFunctionName(
506                                    site,
507                                    FunctionReferenceType::Function {
508                                        function: function.clone(),
509                                    },
510                                )),
511
512                            DynamicParsedFunctionReference::RawResourceConstructor { resource } => {
513                                instructions.push(RibIR::CreateFunctionName(
514                                    site,
515                                    FunctionReferenceType::RawResourceConstructor {
516                                        resource: resource.clone(),
517                                    },
518                                ))
519                            }
520                            DynamicParsedFunctionReference::RawResourceDrop { resource } => {
521                                instructions.push(RibIR::CreateFunctionName(
522                                    site,
523                                    FunctionReferenceType::RawResourceDrop {
524                                        resource: resource.clone(),
525                                    },
526                                ))
527                            }
528                            DynamicParsedFunctionReference::RawResourceMethod {
529                                resource,
530                                method,
531                            } => instructions.push(RibIR::CreateFunctionName(
532                                site,
533                                FunctionReferenceType::RawResourceMethod {
534                                    resource: resource.clone(),
535                                    method: method.clone(),
536                                },
537                            )),
538                            DynamicParsedFunctionReference::RawResourceStaticMethod {
539                                resource,
540                                method,
541                            } => instructions.push(RibIR::CreateFunctionName(
542                                site,
543                                FunctionReferenceType::RawResourceStaticMethod {
544                                    resource: resource.clone(),
545                                    method: method.clone(),
546                                },
547                            )),
548                            DynamicParsedFunctionReference::IndexedResourceConstructor {
549                                resource,
550                                resource_params,
551                            } => {
552                                for param in resource_params {
553                                    stack.push(ExprState::from_expr(param));
554                                }
555                                instructions.push(RibIR::CreateFunctionName(
556                                    site,
557                                    FunctionReferenceType::IndexedResourceConstructor {
558                                        resource: resource.clone(),
559                                        arg_size: resource_params.len(),
560                                    },
561                                ))
562                            }
563                            DynamicParsedFunctionReference::IndexedResourceMethod {
564                                resource,
565                                resource_params,
566                                method,
567                            } => {
568                                for param in resource_params {
569                                    stack.push(ExprState::from_expr(param));
570                                }
571                                instructions.push(RibIR::CreateFunctionName(
572                                    site,
573                                    FunctionReferenceType::IndexedResourceMethod {
574                                        resource: resource.clone(),
575                                        arg_size: resource_params.len(),
576                                        method: method.clone(),
577                                    },
578                                ))
579                            }
580                            DynamicParsedFunctionReference::IndexedResourceStaticMethod {
581                                resource,
582                                resource_params,
583                                method,
584                            } => {
585                                for param in resource_params {
586                                    stack.push(ExprState::from_expr(param));
587                                }
588                                instructions.push(RibIR::CreateFunctionName(
589                                    site,
590                                    FunctionReferenceType::IndexedResourceStaticMethod {
591                                        resource: resource.clone(),
592                                        arg_size: resource_params.len(),
593                                        method: method.clone(),
594                                    },
595                                ))
596                            }
597                            DynamicParsedFunctionReference::IndexedResourceDrop {
598                                resource,
599                                resource_params,
600                            } => {
601                                for param in resource_params {
602                                    stack.push(ExprState::from_expr(param));
603                                }
604                                instructions.push(RibIR::CreateFunctionName(
605                                    site,
606                                    FunctionReferenceType::IndexedResourceDrop {
607                                        resource: resource.clone(),
608                                        arg_size: resource_params.len(),
609                                    },
610                                ))
611                            }
612                        }
613                    }
614
615                    // if there are no arguments to instance that would typically mean
616                    // it's an ephemeral worker or a resource with no arguments
617                    // This would imply there is nothing in the stack of instructions related
618                    // to these cases. So to make sure expressions such as the following work,
619                    // we need to push a place holder in the stack that does nothing
620                    CallType::InstanceCreation(instance_creation_type) => {
621                        match instance_creation_type {
622                            InstanceCreationType::WitWorker { .. } => {
623                                for expr in args.iter().rev() {
624                                    stack.push(ExprState::from_expr(expr));
625                                }
626                            }
627
628                            InstanceCreationType::WitResource {
629                                module,
630                                resource_name,
631                                component_info,
632                            } => {
633                                for expr in args.iter().rev() {
634                                    stack.push(ExprState::from_expr(expr));
635                                }
636
637                                let module = module
638                                    .as_ref()
639                                    .expect("Module should be present for resource calls");
640
641                                let instance_variable = match module {
642                                    InstanceIdentifier::WitResource { variable_id, .. } => {
643                                        let variable_id = variable_id.as_ref().ok_or({
644                                            RibByteCodeGenerationError::UnresolvedResourceVariable
645                                        })?;
646
647                                        InstanceVariable::WitResource(variable_id.clone())
648                                    }
649                                    InstanceIdentifier::WitWorker { variable_id, .. } => {
650                                        let variable_id = variable_id.as_ref().ok_or({
651                                            RibByteCodeGenerationError::UnresolvedWorkerName
652                                        })?;
653
654                                        InstanceVariable::WitWorker(variable_id.clone())
655                                    }
656                                };
657
658                                let site = resource_name.parsed_function_site();
659
660                                let component_info = component_info.as_ref().ok_or(
661                                    RibByteCodeGenerationError::UnresolvedWasmComponent {
662                                        function: resource_name.resource_name.clone(),
663                                    },
664                                )?;
665
666                                let function_result_type = if inferred_type.is_unit() {
667                                    AnalysedTypeWithUnit::Unit
668                                } else {
669                                    AnalysedTypeWithUnit::Type(convert_to_analysed_type(
670                                        expr,
671                                        inferred_type,
672                                    )?)
673                                };
674
675                                instructions.push(RibIR::InvokeFunction(
676                                    component_info.clone(),
677                                    instance_variable,
678                                    args.len(),
679                                    function_result_type,
680                                ));
681
682                                instructions.push(RibIR::CreateFunctionName(
683                                    site,
684                                    FunctionReferenceType::RawResourceConstructor {
685                                        resource: resource_name.resource_name.clone(),
686                                    },
687                                ));
688                            }
689                        }
690                    }
691
692                    CallType::VariantConstructor(variant_name) => {
693                        for expr in args.iter().rev() {
694                            stack.push(ExprState::from_expr(expr));
695                        }
696
697                        instructions.push(RibIR::PushVariant(
698                            variant_name.clone(),
699                            convert_to_analysed_type(expr, inferred_type)?,
700                        ));
701                    }
702                    CallType::EnumConstructor(enum_name) => {
703                        for expr in args.iter().rev() {
704                            stack.push(ExprState::from_expr(expr));
705                        }
706
707                        instructions.push(RibIR::PushEnum(
708                            enum_name.clone(),
709                            convert_to_analysed_type(expr, inferred_type)?,
710                        ));
711                    }
712                }
713            }
714
715            Expr::Flags {
716                flags,
717                inferred_type,
718                ..
719            } => match inferred_type.internal_type() {
720                TypeInternal::Flags(all_flags) => {
721                    let mut bitmap = Vec::new();
722                    let flag_values_set: HashSet<&String> = HashSet::from_iter(flags.iter());
723                    for flag in all_flags.iter() {
724                        bitmap.push(flag_values_set.contains(flag));
725                    }
726                    instructions.push(RibIR::PushFlag(ValueAndType {
727                        value: Value::Flags(bitmap),
728                        typ: AnalysedType::Flags(TypeFlags {
729                            names: all_flags.iter().map(|n| n.to_string()).collect(),
730                        }),
731                    }));
732                }
733                _ => {
734                    return Err(RibByteCodeGenerationError::UnexpectedTypeError {
735                        expected: TypeHint::Flag(Some(flags.clone())),
736                        actual: inferred_type.get_type_hint(),
737                    });
738                }
739            },
740            Expr::Boolean { value, .. } => {
741                instructions.push(RibIR::PushLit(value.into_value_and_type()));
742            }
743            Expr::GetTag { expr, .. } => {
744                stack.push(ExprState::from_expr(expr.deref()));
745                stack.push(ExprState::from_ir(RibIR::GetTag));
746            }
747
748            Expr::Concat { exprs, .. } => {
749                for expr in exprs.iter().rev() {
750                    stack.push(ExprState::from_expr(expr));
751                }
752
753                instructions.push(RibIR::Concat(exprs.len()));
754            }
755
756            Expr::Not { expr, .. } => {
757                stack.push(ExprState::from_expr(expr.deref()));
758                instructions.push(RibIR::Negate);
759            }
760
761            Expr::Tuple {
762                exprs,
763                inferred_type,
764                ..
765            } => {
766                for expr in exprs.iter().rev() {
767                    stack.push(ExprState::from_expr(expr));
768                }
769                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
770                instructions.push(RibIR::PushTuple(analysed_type, exprs.len()));
771            }
772
773            Expr::ListComprehension {
774                iterated_variable,
775                iterable_expr,
776                yield_expr,
777                inferred_type,
778                ..
779            } => {
780                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
781                handle_list_comprehension(
782                    instruction_id,
783                    stack,
784                    iterable_expr,
785                    yield_expr,
786                    iterated_variable,
787                    &analysed_type,
788                )
789            }
790
791            range_expr @ Expr::Range {
792                range,
793                inferred_type,
794                ..
795            } => match inferred_type.internal_type() {
796                TypeInternal::Range { .. } => {
797                    let analysed_type = convert_to_analysed_type(range_expr, inferred_type)?;
798
799                    handle_range(range, stack, analysed_type, instructions);
800                }
801
802                _ => {
803                    return Err(RibByteCodeGenerationError::UnexpectedTypeError {
804                        expected: TypeHint::Range,
805                        actual: inferred_type.get_type_hint(),
806                    });
807                }
808            },
809
810            // Invoke is always handled by the CallType::Function branch
811            Expr::InvokeMethodLazy { .. } => {}
812
813            Expr::ListReduce {
814                reduce_variable,
815                iterated_variable,
816                iterable_expr,
817                init_value_expr,
818                yield_expr,
819                ..
820            } => handle_list_reduce(
821                instruction_id,
822                stack,
823                reduce_variable,
824                iterated_variable,
825                iterable_expr,
826                init_value_expr,
827                yield_expr,
828            ),
829        }
830
831        Ok(())
832    }
833
834    pub(crate) fn convert_to_analysed_type(
835        expr: &Expr,
836        inferred_type: &InferredType,
837    ) -> Result<AnalysedType, RibByteCodeGenerationError> {
838        AnalysedType::try_from(inferred_type).map_err(|error| {
839            RibByteCodeGenerationError::AnalysedTypeConversionError(format!(
840                "Invalid Rib {}. Error converting {} to AnalysedType: {}",
841                expr,
842                inferred_type.get_type_hint(),
843                error
844            ))
845        })
846    }
847
848    // We create a temporary stack of expressions that we pop one by one,
849    // while injecting some pre-defined IRs such as Jump in certain cases
850    // A stack is required on one side to maintain the order of expressions
851    // As soon a `Expr` becomes `Instruction` the instruction stack will be in order.
852    pub(crate) enum ExprState {
853        Expr(Expr),
854        Instruction(RibIR),
855    }
856
857    impl ExprState {
858        pub(crate) fn from_expr(expr: &Expr) -> Self {
859            ExprState::Expr(expr.clone())
860        }
861
862        pub(crate) fn from_ir(ir: RibIR) -> Self {
863            ExprState::Instruction(ir)
864        }
865    }
866
867    fn handle_range(
868        range: &Range,
869        stack: &mut Vec<ExprState>,
870        analysed_type: AnalysedType,
871        instructions: &mut Vec<RibIR>,
872    ) {
873        let from = range.from();
874        let to = range.to();
875        let inclusive = range.inclusive();
876
877        if let Some(from) = from {
878            stack.push(ExprState::from_expr(from));
879            instructions.push(RibIR::UpdateRecord("from".to_string()));
880        }
881
882        if let Some(to) = to {
883            stack.push(ExprState::from_expr(to));
884            instructions.push(RibIR::UpdateRecord("to".to_string()));
885        }
886
887        stack.push(ExprState::from_ir(RibIR::PushLit(ValueAndType::new(
888            Value::Bool(inclusive),
889            bool(),
890        ))));
891
892        instructions.push(RibIR::UpdateRecord("inclusive".to_string()));
893
894        instructions.push(RibIR::CreateAndPushRecord(analysed_type));
895    }
896
897    fn handle_list_comprehension(
898        instruction_id: &mut InstructionId,
899        stack: &mut Vec<ExprState>,
900        iterable_expr: &Expr,
901        yield_expr: &Expr,
902        variable_id: &VariableId,
903        sink_type: &AnalysedType,
904    ) {
905        stack.push(ExprState::from_expr(iterable_expr));
906
907        stack.push(ExprState::from_ir(RibIR::ToIterator));
908
909        stack.push(ExprState::from_ir(RibIR::CreateSink(sink_type.clone())));
910
911        let loop_start_label = instruction_id.increment_mut();
912
913        stack.push(ExprState::from_ir(RibIR::Label(loop_start_label.clone())));
914
915        let exit_label = instruction_id.increment_mut();
916
917        stack.push(ExprState::from_ir(RibIR::IsEmpty));
918
919        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(exit_label.clone())));
920
921        stack.push(ExprState::from_ir(RibIR::AdvanceIterator));
922
923        stack.push(ExprState::from_ir(RibIR::AssignVar(variable_id.clone())));
924
925        stack.push(ExprState::from_expr(yield_expr));
926
927        stack.push(ExprState::from_ir(RibIR::PushToSink));
928
929        stack.push(ExprState::from_ir(RibIR::Jump(loop_start_label)));
930
931        stack.push(ExprState::from_ir(RibIR::Label(exit_label)));
932
933        stack.push(ExprState::from_ir(RibIR::SinkToList))
934    }
935
936    fn handle_list_reduce(
937        instruction_id: &mut InstructionId,
938        stack: &mut Vec<ExprState>,
939        reduce_variable: &VariableId,
940        iterated_variable: &VariableId,
941        iterable_expr: &Expr,
942        initial_value_expr: &Expr,
943        yield_expr: &Expr,
944    ) {
945        stack.push(ExprState::from_expr(iterable_expr));
946
947        stack.push(ExprState::from_expr(initial_value_expr));
948
949        stack.push(ExprState::from_ir(RibIR::AssignVar(
950            reduce_variable.clone(),
951        )));
952
953        stack.push(ExprState::from_ir(RibIR::ToIterator));
954
955        let loop_start_label = instruction_id.increment_mut();
956
957        stack.push(ExprState::from_ir(RibIR::Label(loop_start_label.clone())));
958
959        let exit_label = instruction_id.increment_mut();
960
961        stack.push(ExprState::from_ir(RibIR::IsEmpty));
962
963        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(exit_label.clone())));
964
965        stack.push(ExprState::from_ir(RibIR::AdvanceIterator));
966
967        stack.push(ExprState::from_ir(RibIR::AssignVar(
968            iterated_variable.clone(),
969        )));
970
971        stack.push(ExprState::from_expr(yield_expr));
972
973        stack.push(ExprState::from_ir(RibIR::AssignVar(
974            reduce_variable.clone(),
975        )));
976
977        stack.push(ExprState::from_ir(RibIR::Jump(loop_start_label)));
978
979        stack.push(ExprState::from_ir(RibIR::Label(exit_label)));
980
981        stack.push(ExprState::from_ir(RibIR::LoadVar(reduce_variable.clone())))
982    }
983
984    fn handle_if_condition(
985        instruction_id: &mut InstructionId,
986        if_expr: &Expr,
987        then_expr: &Expr,
988        else_expr: &Expr,
989        stack: &mut Vec<ExprState>,
990    ) {
991        instruction_id.increment_mut();
992        let else_beginning_id = instruction_id.clone();
993        instruction_id.increment_mut();
994        let else_ending_id = instruction_id.clone();
995
996        stack.push(ExprState::from_expr(if_expr));
997
998        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(
999            else_beginning_id.clone(),
1000        )));
1001
1002        stack.push(ExprState::from_expr(then_expr));
1003
1004        stack.push(ExprState::from_ir(RibIR::Jump(else_ending_id.clone())));
1005
1006        stack.push(ExprState::from_ir(RibIR::Label(else_beginning_id.clone())));
1007
1008        stack.push(ExprState::from_expr(else_expr));
1009
1010        stack.push(ExprState::from_ir(RibIR::Label(else_ending_id.clone())));
1011    }
1012}
1013
1014#[cfg(test)]
1015mod compiler_tests {
1016    use bigdecimal::BigDecimal;
1017    use test_r::test;
1018
1019    use super::*;
1020    use crate::{ArmPattern, InferredType, MatchArm, RibCompiler, VariableId};
1021    use golem_wasm_ast::analysis::analysed_type::{list, s32, str};
1022    use golem_wasm_ast::analysis::{AnalysedType, NameTypePair, TypeRecord, TypeStr};
1023    use golem_wasm_rpc::{IntoValueAndType, Value, ValueAndType};
1024
1025    #[test]
1026    fn test_instructions_for_literal() {
1027        let literal = Expr::literal("hello");
1028
1029        let compiler = RibCompiler::default();
1030
1031        let compiler_output = compiler.compile(literal).unwrap();
1032
1033        let instruction_set = vec![RibIR::PushLit("hello".into_value_and_type())];
1034
1035        let expected_instructions = RibByteCode {
1036            instructions: instruction_set,
1037        };
1038
1039        assert_eq!(compiler_output.byte_code, expected_instructions);
1040    }
1041
1042    #[test]
1043    fn test_instructions_for_identifier() {
1044        let inferred_input_type = InferredType::string();
1045
1046        let variable_id = VariableId::local("request", 0);
1047
1048        let expr = Expr::identifier_with_variable_id(variable_id.clone(), None)
1049            .with_inferred_type(inferred_input_type);
1050
1051        let compiler = RibCompiler::default();
1052
1053        let inferred_expr = compiler.infer_types(expr).unwrap();
1054
1055        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1056
1057        let instruction_set = vec![RibIR::LoadVar(variable_id)];
1058
1059        let expected_instructions = RibByteCode {
1060            instructions: instruction_set,
1061        };
1062
1063        assert_eq!(instructions, expected_instructions);
1064    }
1065
1066    #[test]
1067    fn test_instructions_assign_variable() {
1068        let literal = Expr::literal("hello");
1069
1070        let variable_id = VariableId::local("request", 0);
1071
1072        let expr = Expr::let_binding_with_variable_id(variable_id.clone(), literal, None);
1073
1074        let compiler = RibCompiler::default();
1075
1076        let inferred_expr = compiler.infer_types(expr).unwrap();
1077
1078        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1079
1080        let instruction_set = vec![
1081            RibIR::PushLit("hello".into_value_and_type()),
1082            RibIR::AssignVar(variable_id),
1083        ];
1084
1085        let expected_instructions = RibByteCode {
1086            instructions: instruction_set,
1087        };
1088
1089        assert_eq!(instructions, expected_instructions);
1090    }
1091
1092    #[test]
1093    fn test_instructions_equal_to() {
1094        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1095
1096        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1097
1098        let expr = Expr::equal_to(number_f32, number_u32);
1099
1100        let compiler = RibCompiler::default();
1101
1102        let inferred_expr = compiler.infer_types(expr).unwrap();
1103
1104        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1105
1106        let value_and_type1 = 1.0f32.into_value_and_type();
1107
1108        let value_and_type2 = 1u32.into_value_and_type();
1109
1110        let instruction_set = vec![
1111            RibIR::PushLit(value_and_type2),
1112            RibIR::PushLit(value_and_type1),
1113            RibIR::EqualTo,
1114        ];
1115
1116        let expected_instructions = RibByteCode {
1117            instructions: instruction_set,
1118        };
1119
1120        assert_eq!(instructions, expected_instructions);
1121    }
1122
1123    #[test]
1124    fn test_instructions_greater_than() {
1125        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1126
1127        let number_u32 = Expr::number_inferred(BigDecimal::from(2), None, InferredType::u32());
1128
1129        let expr = Expr::greater_than(number_f32, number_u32);
1130
1131        let compiler = RibCompiler::default();
1132
1133        let inferred_expr = compiler.infer_types(expr).unwrap();
1134
1135        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1136
1137        let value_and_type1 = 1.0f32.into_value_and_type();
1138
1139        let value_and_type2 = 2u32.into_value_and_type();
1140
1141        let instruction_set = vec![
1142            RibIR::PushLit(value_and_type2),
1143            RibIR::PushLit(value_and_type1),
1144            RibIR::GreaterThan,
1145        ];
1146
1147        let expected_instructions = RibByteCode {
1148            instructions: instruction_set,
1149        };
1150
1151        assert_eq!(instructions, expected_instructions);
1152    }
1153
1154    #[test]
1155    fn test_instructions_less_than() {
1156        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1157
1158        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1159
1160        let expr = Expr::less_than(number_f32, number_u32);
1161
1162        let compiler = RibCompiler::default();
1163
1164        let inferred_expr = compiler.infer_types(expr).unwrap();
1165
1166        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1167
1168        let value_and_type1 = 1.0f32.into_value_and_type();
1169
1170        let value_and_type2 = 1u32.into_value_and_type();
1171
1172        let instruction_set = vec![
1173            RibIR::PushLit(value_and_type2),
1174            RibIR::PushLit(value_and_type1),
1175            RibIR::LessThan,
1176        ];
1177
1178        let expected_instructions = RibByteCode {
1179            instructions: instruction_set,
1180        };
1181
1182        assert_eq!(instructions, expected_instructions);
1183    }
1184
1185    #[test]
1186    fn test_instructions_greater_than_or_equal_to() {
1187        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1188
1189        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1190
1191        let expr = Expr::greater_than_or_equal_to(number_f32, number_u32);
1192
1193        let compiler = RibCompiler::default();
1194
1195        let inferred_expr = compiler.infer_types(expr).unwrap();
1196
1197        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1198
1199        let value_and_type1 = 1.0f32.into_value_and_type();
1200
1201        let value_and_type2 = 1u32.into_value_and_type();
1202
1203        let instruction_set = vec![
1204            RibIR::PushLit(value_and_type2),
1205            RibIR::PushLit(value_and_type1),
1206            RibIR::GreaterThanOrEqualTo,
1207        ];
1208
1209        let expected_instructions = RibByteCode {
1210            instructions: instruction_set,
1211        };
1212
1213        assert_eq!(instructions, expected_instructions);
1214    }
1215
1216    #[test]
1217    fn test_instructions_less_than_or_equal_to() {
1218        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1219
1220        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1221
1222        let expr = Expr::less_than_or_equal_to(number_f32, number_u32);
1223
1224        let compiler = RibCompiler::default();
1225
1226        let inferred_expr = compiler.infer_types(expr).unwrap();
1227
1228        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1229
1230        let value_and_type1 = 1.0f32.into_value_and_type();
1231
1232        let value_and_type2 = 1u32.into_value_and_type();
1233
1234        let instruction_set = vec![
1235            RibIR::PushLit(value_and_type2),
1236            RibIR::PushLit(value_and_type1),
1237            RibIR::LessThanOrEqualTo,
1238        ];
1239
1240        let expected_instructions = RibByteCode {
1241            instructions: instruction_set,
1242        };
1243
1244        assert_eq!(instructions, expected_instructions);
1245    }
1246
1247    #[test]
1248    fn test_instructions_for_record() {
1249        let expr = Expr::record(vec![
1250            ("foo_key".to_string(), Expr::literal("foo_value")),
1251            ("bar_key".to_string(), Expr::literal("bar_value")),
1252        ])
1253        .with_inferred_type(InferredType::record(vec![
1254            (String::from("foo_key"), InferredType::string()),
1255            (String::from("bar_key"), InferredType::string()),
1256        ]));
1257
1258        let compiler = RibCompiler::default();
1259
1260        let inferred_expr = compiler.infer_types(expr).unwrap();
1261
1262        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1263
1264        let bar_value = "bar_value".into_value_and_type();
1265
1266        let foo_value = "foo_value".into_value_and_type();
1267
1268        let instruction_set = vec![
1269            RibIR::PushLit(bar_value),
1270            RibIR::PushLit(foo_value),
1271            RibIR::CreateAndPushRecord(AnalysedType::Record(TypeRecord {
1272                fields: vec![
1273                    NameTypePair {
1274                        name: "foo_key".to_string(),
1275                        typ: AnalysedType::Str(TypeStr),
1276                    },
1277                    NameTypePair {
1278                        name: "bar_key".to_string(),
1279                        typ: AnalysedType::Str(TypeStr),
1280                    },
1281                ],
1282            })),
1283            RibIR::UpdateRecord("foo_key".to_string()),
1284            RibIR::UpdateRecord("bar_key".to_string()),
1285        ];
1286
1287        let expected_instructions = RibByteCode {
1288            instructions: instruction_set,
1289        };
1290
1291        assert_eq!(instructions, expected_instructions);
1292    }
1293
1294    #[test]
1295    fn test_instructions_for_multiple() {
1296        let expr = Expr::expr_block(vec![Expr::literal("foo"), Expr::literal("bar")]);
1297
1298        let compiler = RibCompiler::default();
1299
1300        let inferred_expr = compiler.infer_types(expr).unwrap();
1301
1302        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1303
1304        let instruction_set = vec![
1305            RibIR::PushLit("foo".into_value_and_type()),
1306            RibIR::PushLit("bar".into_value_and_type()),
1307        ];
1308
1309        let expected_instructions = RibByteCode {
1310            instructions: instruction_set,
1311        };
1312
1313        assert_eq!(instructions, expected_instructions);
1314    }
1315
1316    #[test]
1317    fn test_instructions_if_conditional() {
1318        let if_expr = Expr::literal("pred").with_inferred_type(InferredType::bool());
1319
1320        let then_expr = Expr::literal("then");
1321
1322        let else_expr = Expr::literal("else");
1323
1324        let expr =
1325            Expr::cond(if_expr, then_expr, else_expr).with_inferred_type(InferredType::string());
1326
1327        let compiler = RibCompiler::default();
1328
1329        let inferred_expr = compiler.infer_types(expr).unwrap();
1330
1331        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1332
1333        let instruction_set = vec![
1334            RibIR::PushLit("pred".into_value_and_type()),
1335            RibIR::JumpIfFalse(InstructionId { index: 1 }), // jumps to the next label having Id 1 (which is else block)
1336            RibIR::PushLit("then".into_value_and_type()),
1337            RibIR::Jump(InstructionId { index: 2 }), // Once if is executed then jump to the end of the else block with id 2
1338            RibIR::Label(InstructionId { index: 1 }),
1339            RibIR::PushLit("else".into_value_and_type()),
1340            RibIR::Label(InstructionId { index: 2 }),
1341        ];
1342
1343        let expected_instructions = RibByteCode {
1344            instructions: instruction_set,
1345        };
1346
1347        assert_eq!(instructions, expected_instructions);
1348    }
1349
1350    #[test]
1351    fn test_instructions_for_nested_if_else() {
1352        let if_expr = Expr::literal("if-pred1").with_inferred_type(InferredType::bool());
1353
1354        let then_expr = Expr::literal("then1").with_inferred_type(InferredType::string());
1355
1356        let else_expr = Expr::cond(
1357            Expr::literal("else-pred2").with_inferred_type(InferredType::bool()),
1358            Expr::literal("else-then2"),
1359            Expr::literal("else-else2"),
1360        )
1361        .with_inferred_type(InferredType::string());
1362
1363        let expr =
1364            Expr::cond(if_expr, then_expr, else_expr).with_inferred_type(InferredType::string());
1365
1366        let compiler = RibCompiler::default();
1367
1368        let inferred_expr = compiler.infer_types(expr).unwrap();
1369
1370        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1371
1372        let instruction_set = vec![
1373            // if case
1374            RibIR::PushLit("if-pred1".into_value_and_type()),
1375            RibIR::JumpIfFalse(InstructionId { index: 1 }), // jumps to the next label having Id 1 (which is else block)
1376            RibIR::PushLit("then1".into_value_and_type()),
1377            RibIR::Jump(InstructionId { index: 2 }), // Once if is executed then jump to the end of the else block with id 3
1378            RibIR::Label(InstructionId { index: 1 }),
1379            RibIR::PushLit("else-pred2".into_value_and_type()),
1380            RibIR::JumpIfFalse(InstructionId { index: 3 }), // jumps to the next label having Id 2 (which is else block)
1381            RibIR::PushLit("else-then2".into_value_and_type()),
1382            RibIR::Jump(InstructionId { index: 4 }), // Once if is executed then jump to the end of the else block with id 3
1383            RibIR::Label(InstructionId { index: 3 }),
1384            RibIR::PushLit("else-else2".into_value_and_type()),
1385            RibIR::Label(InstructionId { index: 4 }),
1386            RibIR::Label(InstructionId { index: 2 }),
1387        ];
1388
1389        let expected_instructions = RibByteCode {
1390            instructions: instruction_set,
1391        };
1392
1393        assert_eq!(instructions, expected_instructions);
1394    }
1395
1396    #[test]
1397    fn test_instructions_for_select_field() {
1398        let record = Expr::record(vec![
1399            ("foo_key".to_string(), Expr::literal("foo_value")),
1400            ("bar_key".to_string(), Expr::literal("bar_value")),
1401        ])
1402        .with_inferred_type(InferredType::record(vec![
1403            (String::from("foo_key"), InferredType::string()),
1404            (String::from("bar_key"), InferredType::string()),
1405        ]));
1406
1407        let expr =
1408            Expr::select_field(record, "bar_key", None).with_inferred_type(InferredType::string());
1409
1410        let compiler = RibCompiler::default();
1411
1412        let inferred_expr = compiler.infer_types(expr).unwrap();
1413
1414        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1415
1416        let bar_value = "bar_value".into_value_and_type();
1417
1418        let foo_value = "foo_value".into_value_and_type();
1419
1420        let instruction_set = vec![
1421            RibIR::PushLit(bar_value),
1422            RibIR::PushLit(foo_value),
1423            RibIR::CreateAndPushRecord(AnalysedType::Record(TypeRecord {
1424                fields: vec![
1425                    NameTypePair {
1426                        name: "bar_key".to_string(),
1427                        typ: AnalysedType::Str(TypeStr),
1428                    },
1429                    NameTypePair {
1430                        name: "foo_key".to_string(),
1431                        typ: AnalysedType::Str(TypeStr),
1432                    },
1433                ],
1434            })),
1435            RibIR::UpdateRecord("foo_key".to_string()), // next pop is foo_value
1436            RibIR::UpdateRecord("bar_key".to_string()), // last pop is bar_value
1437            RibIR::SelectField("bar_key".to_string()),
1438        ];
1439
1440        let expected_instructions = RibByteCode {
1441            instructions: instruction_set,
1442        };
1443
1444        assert_eq!(instructions, expected_instructions);
1445    }
1446
1447    #[test]
1448    fn test_instructions_for_select_index() {
1449        let sequence = Expr::sequence(vec![Expr::literal("foo"), Expr::literal("bar")], None)
1450            .with_inferred_type(InferredType::list(InferredType::string()));
1451
1452        let expr = Expr::select_index(sequence, Expr::number(BigDecimal::from(1)))
1453            .with_inferred_type(InferredType::string());
1454
1455        let compiler = RibCompiler::default();
1456
1457        let inferred_expr = compiler.infer_types(expr).unwrap();
1458
1459        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1460
1461        let instruction_set = vec![
1462            RibIR::PushLit(ValueAndType::new(Value::S32(1), s32())),
1463            RibIR::PushLit("bar".into_value_and_type()),
1464            RibIR::PushLit("foo".into_value_and_type()),
1465            RibIR::PushList(list(str()), 2),
1466            RibIR::SelectIndexV1,
1467        ];
1468
1469        let expected_instructions = RibByteCode {
1470            instructions: instruction_set,
1471        };
1472
1473        assert_eq!(instructions, expected_instructions);
1474    }
1475
1476    #[test]
1477    fn test_instructions_for_expr_arm_pattern_match() {
1478        let expr = Expr::pattern_match(
1479            Expr::literal("pred"),
1480            vec![
1481                MatchArm::new(
1482                    ArmPattern::Literal(Box::new(Expr::literal("arm1_pattern_expr"))),
1483                    Expr::literal("arm1_resolution_expr"),
1484                ),
1485                MatchArm::new(
1486                    ArmPattern::Literal(Box::new(Expr::literal("arm2_pattern_expr"))),
1487                    Expr::literal("arm2_resolution_expr"),
1488                ),
1489                MatchArm::new(
1490                    ArmPattern::Literal(Box::new(Expr::literal("arm3_pattern_expr"))),
1491                    Expr::literal("arm3_resolution_expr"),
1492                ),
1493            ],
1494        )
1495        .with_inferred_type(InferredType::string());
1496
1497        let rib_compiler = RibCompiler::default();
1498
1499        let instructions = rib_compiler.compile(expr).unwrap().byte_code;
1500
1501        // instructions will correspond to an if-else statement
1502        let instruction_set = vec![
1503            RibIR::PushLit("arm1_pattern_expr".into_value_and_type()),
1504            RibIR::PushLit("pred".into_value_and_type()),
1505            RibIR::EqualTo,
1506            RibIR::JumpIfFalse(InstructionId { index: 1 }),
1507            RibIR::PushLit("arm1_resolution_expr".into_value_and_type()),
1508            RibIR::Jump(InstructionId { index: 2 }),
1509            RibIR::Label(InstructionId { index: 1 }),
1510            RibIR::PushLit("arm2_pattern_expr".into_value_and_type()),
1511            RibIR::PushLit("pred".into_value_and_type()),
1512            RibIR::EqualTo,
1513            RibIR::JumpIfFalse(InstructionId { index: 3 }),
1514            RibIR::PushLit("arm2_resolution_expr".into_value_and_type()),
1515            RibIR::Jump(InstructionId { index: 4 }),
1516            RibIR::Label(InstructionId { index: 3 }),
1517            RibIR::PushLit("arm3_pattern_expr".into_value_and_type()),
1518            RibIR::PushLit("pred".into_value_and_type()),
1519            RibIR::EqualTo,
1520            RibIR::JumpIfFalse(InstructionId { index: 5 }),
1521            RibIR::PushLit("arm3_resolution_expr".into_value_and_type()),
1522            RibIR::Jump(InstructionId { index: 6 }),
1523            RibIR::Label(InstructionId { index: 5 }),
1524            RibIR::Throw("No match found".to_string()),
1525            RibIR::Label(InstructionId { index: 6 }),
1526            RibIR::Label(InstructionId { index: 4 }),
1527            RibIR::Label(InstructionId { index: 2 }),
1528        ];
1529
1530        let expected_instructions = RibByteCode {
1531            instructions: instruction_set,
1532        };
1533
1534        assert_eq!(instructions, expected_instructions);
1535    }
1536
1537    #[cfg(test)]
1538    mod invalid_function_invoke_tests {
1539        use test_r::test;
1540
1541        use crate::compiler::byte_code::compiler_tests::internal;
1542        use crate::{Expr, RibCompiler, RibCompilerConfig};
1543        use golem_wasm_ast::analysis::{AnalysedType, TypeStr};
1544
1545        #[test]
1546        fn test_unknown_function() {
1547            let expr = r#"
1548               foo(request);
1549               "success"
1550            "#;
1551
1552            let expr = Expr::from_text(expr).unwrap();
1553            let compiler = RibCompiler::default();
1554
1555            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1556
1557            assert_eq!(compiler_error, "error in the following rib found at line 2, column 16\n`foo(request)`\ncause: invalid function call `foo`\nunknown function\n");
1558        }
1559
1560        #[test]
1561        fn test_unknown_resource_method() {
1562            let metadata = internal::metadata_with_resource_methods();
1563            let expr = r#"
1564               let user_id = "user";
1565               golem:it/api.{cart(user_id).add-item}("apple");
1566               golem:it/api.{cart(user_id).foo}("apple");
1567                "success"
1568            "#;
1569
1570            let expr = Expr::from_text(expr).unwrap();
1571
1572            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1573
1574            let compiler = RibCompiler::new(compiler_config);
1575
1576            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1577            assert_eq!(
1578                compiler_error,
1579                "error in the following rib found at line 4, column 16\n`foo(\"apple\")`\ncause: invalid function call `foo`\nunknown function\n"
1580            );
1581        }
1582
1583        #[test]
1584        fn test_invalid_arg_size_function() {
1585            let metadata = internal::get_component_metadata(
1586                "foo",
1587                vec![AnalysedType::Str(TypeStr)],
1588                AnalysedType::Str(TypeStr),
1589            );
1590
1591            let expr = r#"
1592               let user_id = "user";
1593               let result = foo(user_id, user_id);
1594               result
1595            "#;
1596
1597            let expr = Expr::from_text(expr).unwrap();
1598
1599            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1600
1601            let compiler = RibCompiler::new(compiler_config);
1602
1603            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1604            assert_eq!(
1605                compiler_error,
1606                "error in the following rib found at line 3, column 29\n`foo(user_id, user_id)`\ncause: invalid argument size for function `foo`. expected 1 arguments, found 2\n"
1607            );
1608        }
1609
1610        #[test]
1611        fn test_invalid_arg_size_resource_method() {
1612            let metadata = internal::metadata_with_resource_methods();
1613            let expr = r#"
1614               let user_id = "user";
1615               golem:it/api.{cart(user_id).add-item}("apple", "samsung");
1616                "success"
1617            "#;
1618
1619            let expr = Expr::from_text(expr).unwrap();
1620
1621            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1622
1623            let compiler = RibCompiler::new(compiler_config);
1624
1625            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1626            assert_eq!(
1627                compiler_error,
1628                "error in the following rib found at line 3, column 16\n`add-item(\"apple\", \"samsung\")`\ncause: invalid argument size for function `add-item`. expected 1 arguments, found 2\n"
1629            );
1630        }
1631
1632        #[test]
1633        fn test_invalid_arg_types_function() {
1634            let metadata = internal::get_component_metadata(
1635                "foo",
1636                vec![AnalysedType::Str(TypeStr)],
1637                AnalysedType::Str(TypeStr),
1638            );
1639
1640            let expr = r#"
1641               let result = foo(1u64);
1642               result
1643            "#;
1644
1645            let expr = Expr::from_text(expr).unwrap();
1646
1647            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1648
1649            let compiler = RibCompiler::new(compiler_config);
1650
1651            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1652            assert_eq!(
1653                compiler_error,
1654                "error in the following rib found at line 2, column 33\n`1: u64`\ncause: type mismatch. expected string, found u64\ninvalid argument to the function `foo`\n"
1655            );
1656        }
1657
1658        #[test]
1659        fn test_invalid_arg_types_resource_method() {
1660            let metadata = internal::metadata_with_resource_methods();
1661            let expr = r#"
1662               let user_id = "user";
1663               golem:it/api.{cart(user_id).add-item}("apple");
1664                "success"
1665            "#;
1666
1667            let expr = Expr::from_text(expr).unwrap();
1668
1669            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1670
1671            let compiler = RibCompiler::new(compiler_config);
1672
1673            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1674            assert_eq!(
1675                compiler_error,
1676                "error in the following rib found at line 3, column 54\n`\"apple\"`\ncause: type mismatch. expected record { name: string }, found string\ninvalid argument to the function `add-item`\n"
1677            );
1678        }
1679
1680        #[test]
1681        fn test_invalid_arg_types_variants() {
1682            let metadata = internal::metadata_with_variants();
1683
1684            let expr = r#"
1685               let regiser_user_action = register-user("foo");
1686               let result = golem:it/api.{foo}(regiser_user_action);
1687               result
1688            "#;
1689
1690            let expr = Expr::from_text(expr).unwrap();
1691
1692            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1693
1694            let compiler = RibCompiler::new(compiler_config);
1695
1696            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1697            assert_eq!(
1698                compiler_error,
1699                "error in the following rib found at line 2, column 56\n`\"foo\"`\ncause: type mismatch. expected u64, found string\ninvalid argument to the function `register-user`\n"
1700            );
1701        }
1702    }
1703
1704    #[cfg(test)]
1705    mod global_input_tests {
1706        use test_r::test;
1707
1708        use crate::compiler::byte_code::compiler_tests::internal;
1709        use crate::{Expr, RibCompiler, RibCompilerConfig};
1710        use golem_wasm_ast::analysis::{
1711            AnalysedType, NameOptionTypePair, NameTypePair, TypeEnum, TypeList, TypeOption,
1712            TypeRecord, TypeResult, TypeStr, TypeTuple, TypeU32, TypeU64, TypeVariant,
1713        };
1714
1715        #[test]
1716        async fn test_str_global_input() {
1717            let request_value_type = AnalysedType::Str(TypeStr);
1718
1719            let output_analysed_type = AnalysedType::Str(TypeStr);
1720
1721            let analysed_exports = internal::get_component_metadata(
1722                "my-worker-function",
1723                vec![request_value_type.clone()],
1724                output_analysed_type,
1725            );
1726
1727            let expr = r#"
1728               let x = request;
1729               let worker = instance();
1730               worker.my-worker-function(x);
1731               match x {
1732                "foo"  => "success",
1733                 _ => "fallback"
1734               }
1735            "#;
1736
1737            let expr = Expr::from_text(expr).unwrap();
1738            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1739            let compiled = compiler.compile(expr).unwrap();
1740            let expected_type_info =
1741                internal::rib_input_type_info(vec![("request", request_value_type)]);
1742
1743            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1744        }
1745
1746        #[test]
1747        async fn test_number_global_input() {
1748            let request_value_type = AnalysedType::U32(TypeU32);
1749
1750            let output_analysed_type = AnalysedType::Str(TypeStr);
1751
1752            let analysed_exports = internal::get_component_metadata(
1753                "my-worker-function",
1754                vec![request_value_type.clone()],
1755                output_analysed_type,
1756            );
1757
1758            let expr = r#"
1759               let x = request;
1760               let worker = instance();
1761               worker.my-worker-function(x);
1762               match x {
1763                1  => "success",
1764                0 => "failure"
1765               }
1766            "#;
1767
1768            let expr = Expr::from_text(expr).unwrap();
1769            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1770            let compiled = compiler.compile(expr).unwrap();
1771            let expected_type_info =
1772                internal::rib_input_type_info(vec![("request", request_value_type)]);
1773
1774            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1775        }
1776
1777        #[test]
1778        async fn test_variant_type_info() {
1779            let request_value_type = AnalysedType::Variant(TypeVariant {
1780                cases: vec![
1781                    NameOptionTypePair {
1782                        name: "register-user".to_string(),
1783                        typ: Some(AnalysedType::U64(TypeU64)),
1784                    },
1785                    NameOptionTypePair {
1786                        name: "process-user".to_string(),
1787                        typ: Some(AnalysedType::Str(TypeStr)),
1788                    },
1789                    NameOptionTypePair {
1790                        name: "validate".to_string(),
1791                        typ: None,
1792                    },
1793                ],
1794            });
1795
1796            let output_analysed_type = AnalysedType::Str(TypeStr);
1797
1798            let analysed_exports = internal::get_component_metadata(
1799                "my-worker-function",
1800                vec![request_value_type.clone()],
1801                output_analysed_type,
1802            );
1803
1804            // x = request, implies we are expecting a global variable
1805            // called request as the  input to Rib.
1806            // my-worker-function is a function that takes a Variant as input,
1807            // implies the type of request is a Variant.
1808            // This means the rib interpreter env has to have a request variable in it,
1809            // with a value that should be of the type Variant
1810            let expr = r#"
1811               let worker = instance();
1812               worker.my-worker-function(request);
1813               match request {
1814                 process-user(user) => user,
1815                 _ => "default"
1816               }
1817            "#;
1818
1819            let expr = Expr::from_text(expr).unwrap();
1820            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1821            let compiled = compiler.compile(expr).unwrap();
1822            let expected_type_info =
1823                internal::rib_input_type_info(vec![("request", request_value_type)]);
1824
1825            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1826        }
1827
1828        #[test]
1829        async fn test_result_type_info() {
1830            let request_value_type = AnalysedType::Result(TypeResult {
1831                ok: Some(Box::new(AnalysedType::U64(TypeU64))),
1832                err: Some(Box::new(AnalysedType::Str(TypeStr))),
1833            });
1834
1835            let output_analysed_type = AnalysedType::Str(TypeStr);
1836
1837            let analysed_exports = internal::get_component_metadata(
1838                "my-worker-function",
1839                vec![request_value_type.clone()],
1840                output_analysed_type,
1841            );
1842
1843            // x = request, implies we are expecting a global variable
1844            // called request as the  input to Rib.
1845            // my-worker-function is a function that takes a Result as input,
1846            // implies the type of request is a Result.
1847            // This means the rib interpreter env has to have a request variable in it,
1848            // with a value that should be of the type Result
1849            let expr = r#"
1850               let worker = instance();
1851               worker.my-worker-function(request);
1852               match request {
1853                 ok(x) => "${x}",
1854                 err(msg) => msg
1855               }
1856            "#;
1857
1858            let expr = Expr::from_text(expr).unwrap();
1859            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1860            let compiled = compiler.compile(expr).unwrap();
1861            let expected_type_info =
1862                internal::rib_input_type_info(vec![("request", request_value_type)]);
1863
1864            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1865        }
1866
1867        #[test]
1868        async fn test_option_type_info() {
1869            let request_value_type = AnalysedType::Option(TypeOption {
1870                inner: Box::new(AnalysedType::Str(TypeStr)),
1871            });
1872
1873            let output_analysed_type = AnalysedType::Str(TypeStr);
1874
1875            let analysed_exports = internal::get_component_metadata(
1876                "my-worker-function",
1877                vec![request_value_type.clone()],
1878                output_analysed_type,
1879            );
1880
1881            // x = request, implies we are expecting a global variable
1882            // called request as the input to Rib.
1883            // my-worker-function is a function that takes a Option as input,
1884            // implies the type of request is a Result.
1885            // This means the rib interpreter env has to have a request variable in it,
1886            // with a value that should be of the type Option
1887            let expr = r#"
1888               let worker = instance();
1889               worker.my-worker-function(request);
1890               match request {
1891                 some(x) => x,
1892                 none => "error"
1893               }
1894            "#;
1895
1896            let expr = Expr::from_text(expr).unwrap();
1897            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1898            let compiled = compiler.compile(expr).unwrap();
1899            let expected_type_info =
1900                internal::rib_input_type_info(vec![("request", request_value_type)]);
1901
1902            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1903        }
1904
1905        #[test]
1906        async fn test_enum_type_info() {
1907            let request_value_type = AnalysedType::Enum(TypeEnum {
1908                cases: vec!["prod".to_string(), "dev".to_string(), "test".to_string()],
1909            });
1910
1911            let output_analysed_type = AnalysedType::Str(TypeStr);
1912
1913            let analysed_exports = internal::get_component_metadata(
1914                "my-worker-function",
1915                vec![request_value_type.clone()],
1916                output_analysed_type,
1917            );
1918
1919            // x = request, implies we are expecting a global variable
1920            // called request as the input to Rib.
1921            // my-worker-function is a function that takes a Option as input,
1922            // implies the type of request is a Result.
1923            // This means the rib interpreter env has to have a request variable in it,
1924            // with a value that should be of the type Option
1925            let expr = r#"
1926               let worker = instance();
1927               worker.my-worker-function(request);
1928               match request {
1929                 prod  => "p",
1930                 dev => "d",
1931                 test => "t"
1932               }
1933            "#;
1934
1935            let expr = Expr::from_text(expr).unwrap();
1936            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1937            let compiled = compiler.compile(expr).unwrap();
1938            let expected_type_info =
1939                internal::rib_input_type_info(vec![("request", request_value_type)]);
1940
1941            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1942        }
1943
1944        #[test]
1945        async fn test_record_global_input() {
1946            let request_value_type = AnalysedType::Record(TypeRecord {
1947                fields: vec![NameTypePair {
1948                    name: "path".to_string(),
1949                    typ: AnalysedType::Record(TypeRecord {
1950                        fields: vec![NameTypePair {
1951                            name: "user".to_string(),
1952                            typ: AnalysedType::Str(TypeStr),
1953                        }],
1954                    }),
1955                }],
1956            });
1957
1958            let output_analysed_type = AnalysedType::Str(TypeStr);
1959
1960            let analysed_exports = internal::get_component_metadata(
1961                "my-worker-function",
1962                vec![request_value_type.clone()],
1963                output_analysed_type,
1964            );
1965
1966            // x = request, implies we are expecting a global variable
1967            // called request as the  input to Rib.
1968            // my-worker-function is a function that takes a Record of path -> user -> str as input
1969            // implies the type of request is a Record.
1970            // This means the rib interpreter env has to have a request variable in it,
1971            // with a value that should be of the type Record
1972            let expr = r#"
1973               let x = request;
1974               let worker = instance();
1975               worker.my-worker-function(x);
1976
1977               let name = x.path.user;
1978
1979               match x {
1980                 { path : { user : some_name } } => some_name,
1981                 _ => name
1982               }
1983            "#;
1984
1985            let expr = Expr::from_text(expr).unwrap();
1986            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1987            let compiled = compiler.compile(expr).unwrap();
1988            let expected_type_info =
1989                internal::rib_input_type_info(vec![("request", request_value_type)]);
1990
1991            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1992        }
1993
1994        #[test]
1995        async fn test_tuple_global_input() {
1996            let request_value_type = AnalysedType::Tuple(TypeTuple {
1997                items: vec![
1998                    AnalysedType::Str(TypeStr),
1999                    AnalysedType::U32(TypeU32),
2000                    AnalysedType::Record(TypeRecord {
2001                        fields: vec![NameTypePair {
2002                            name: "user".to_string(),
2003                            typ: AnalysedType::Str(TypeStr),
2004                        }],
2005                    }),
2006                ],
2007            });
2008
2009            let output_analysed_type = AnalysedType::Str(TypeStr);
2010
2011            let analysed_exports = internal::get_component_metadata(
2012                "my-worker-function",
2013                vec![request_value_type.clone()],
2014                output_analysed_type,
2015            );
2016
2017            // x = request, implies we are expecting a global variable
2018            // called request as the  input to Rib.
2019            // my-worker-function is a function that takes a Tuple,
2020            // implies the type of request is a Tuple.
2021            let expr = r#"
2022               let x = request;
2023               let worker = instance();
2024               worker.my-worker-function(x);
2025               match x {
2026                (_, _, record) =>  record.user,
2027                 _ => "fallback"
2028               }
2029            "#;
2030
2031            let expr = Expr::from_text(expr).unwrap();
2032            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
2033            let compiled = compiler.compile(expr).unwrap();
2034            let expected_type_info =
2035                internal::rib_input_type_info(vec![("request", request_value_type)]);
2036
2037            assert_eq!(compiled.rib_input_type_info, expected_type_info);
2038        }
2039
2040        #[test]
2041        async fn test_list_global_input() {
2042            let request_value_type = AnalysedType::List(TypeList {
2043                inner: Box::new(AnalysedType::Str(TypeStr)),
2044            });
2045
2046            let output_analysed_type = AnalysedType::Str(TypeStr);
2047
2048            let analysed_exports = internal::get_component_metadata(
2049                "my-worker-function",
2050                vec![request_value_type.clone()],
2051                output_analysed_type,
2052            );
2053
2054            // x = request, implies we are expecting a global variable
2055            // called request as the  input to Rib.
2056            // my-worker-function is a function that takes a List,
2057            // implies the type of request should be a List
2058            let expr = r#"
2059               let x = request;
2060               let worker = instance();
2061               worker.my-worker-function(x);
2062               match x {
2063               [a, b, c]  => a,
2064                 _ => "fallback"
2065               }
2066            "#;
2067
2068            let expr = Expr::from_text(expr).unwrap();
2069            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
2070            let compiled = compiler.compile(expr).unwrap();
2071            let expected_type_info =
2072                internal::rib_input_type_info(vec![("request", request_value_type)]);
2073
2074            assert_eq!(compiled.rib_input_type_info, expected_type_info);
2075        }
2076    }
2077
2078    mod internal {
2079        use crate::{ComponentDependency, ComponentDependencyKey, RibInputTypeInfo};
2080        use golem_wasm_ast::analysis::*;
2081        use std::collections::HashMap;
2082        use uuid::Uuid;
2083
2084        pub(crate) fn metadata_with_variants() -> Vec<ComponentDependency> {
2085            let instance = AnalysedExport::Instance(AnalysedInstance {
2086                name: "golem:it/api".to_string(),
2087                functions: vec![AnalysedFunction {
2088                    name: "foo".to_string(),
2089                    parameters: vec![AnalysedFunctionParameter {
2090                        name: "param1".to_string(),
2091                        typ: AnalysedType::Variant(TypeVariant {
2092                            cases: vec![
2093                                NameOptionTypePair {
2094                                    name: "register-user".to_string(),
2095                                    typ: Some(AnalysedType::U64(TypeU64)),
2096                                },
2097                                NameOptionTypePair {
2098                                    name: "process-user".to_string(),
2099                                    typ: Some(AnalysedType::Str(TypeStr)),
2100                                },
2101                                NameOptionTypePair {
2102                                    name: "validate".to_string(),
2103                                    typ: None,
2104                                },
2105                            ],
2106                        }),
2107                    }],
2108                    result: Some(AnalysedFunctionResult {
2109                        typ: AnalysedType::Handle(TypeHandle {
2110                            resource_id: AnalysedResourceId(0),
2111                            mode: AnalysedResourceMode::Owned,
2112                        }),
2113                    }),
2114                }],
2115            });
2116
2117            let component_info = ComponentDependencyKey {
2118                component_name: "foo".to_string(),
2119                component_id: Uuid::new_v4(),
2120                root_package_name: None,
2121                root_package_version: None,
2122            };
2123
2124            vec![ComponentDependency {
2125                component_dependency_key: component_info,
2126                component_exports: vec![instance],
2127            }]
2128        }
2129
2130        pub(crate) fn metadata_with_resource_methods() -> Vec<ComponentDependency> {
2131            let instance = AnalysedExport::Instance(AnalysedInstance {
2132                name: "golem:it/api".to_string(),
2133                functions: vec![
2134                    AnalysedFunction {
2135                        name: "[constructor]cart".to_string(),
2136                        parameters: vec![AnalysedFunctionParameter {
2137                            name: "param1".to_string(),
2138                            typ: AnalysedType::Str(TypeStr),
2139                        }],
2140                        result: Some(AnalysedFunctionResult {
2141                            typ: AnalysedType::Handle(TypeHandle {
2142                                resource_id: AnalysedResourceId(0),
2143                                mode: AnalysedResourceMode::Owned,
2144                            }),
2145                        }),
2146                    },
2147                    AnalysedFunction {
2148                        name: "[method]cart.add-item".to_string(),
2149                        parameters: vec![
2150                            AnalysedFunctionParameter {
2151                                name: "self".to_string(),
2152                                typ: AnalysedType::Handle(TypeHandle {
2153                                    resource_id: AnalysedResourceId(0),
2154                                    mode: AnalysedResourceMode::Borrowed,
2155                                }),
2156                            },
2157                            AnalysedFunctionParameter {
2158                                name: "item".to_string(),
2159                                typ: AnalysedType::Record(TypeRecord {
2160                                    fields: vec![NameTypePair {
2161                                        name: "name".to_string(),
2162                                        typ: AnalysedType::Str(TypeStr),
2163                                    }],
2164                                }),
2165                            },
2166                        ],
2167                        result: None,
2168                    },
2169                ],
2170            });
2171
2172            let component_info = ComponentDependencyKey {
2173                component_name: "foo".to_string(),
2174                component_id: Uuid::new_v4(),
2175                root_package_name: None,
2176                root_package_version: None,
2177            };
2178
2179            vec![ComponentDependency {
2180                component_dependency_key: component_info,
2181                component_exports: vec![instance],
2182            }]
2183        }
2184        pub(crate) fn get_component_metadata(
2185            function_name: &str,
2186            input_types: Vec<AnalysedType>,
2187            output: AnalysedType,
2188        ) -> Vec<ComponentDependency> {
2189            let analysed_function_parameters = input_types
2190                .into_iter()
2191                .enumerate()
2192                .map(|(index, typ)| AnalysedFunctionParameter {
2193                    name: format!("param{}", index),
2194                    typ,
2195                })
2196                .collect();
2197
2198            let component_info = ComponentDependencyKey {
2199                component_name: "foo".to_string(),
2200                component_id: Uuid::new_v4(),
2201                root_package_name: None,
2202                root_package_version: None,
2203            };
2204
2205            vec![ComponentDependency {
2206                component_dependency_key: component_info,
2207                component_exports: vec![AnalysedExport::Function(AnalysedFunction {
2208                    name: function_name.to_string(),
2209                    parameters: analysed_function_parameters,
2210                    result: Some(AnalysedFunctionResult { typ: output }),
2211                })],
2212            }]
2213        }
2214
2215        pub(crate) fn rib_input_type_info(types: Vec<(&str, AnalysedType)>) -> RibInputTypeInfo {
2216            let mut type_info = HashMap::new();
2217            for (name, typ) in types {
2218                type_info.insert(name.to_string(), typ);
2219            }
2220            RibInputTypeInfo { types: type_info }
2221        }
2222    }
2223}