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: {err}"
387                            ))
388                        })?;
389                    stack.push(ExprState::from_expr(&list_comprehension));
390                }
391                _ => {
392                    stack.push(ExprState::from_expr(index.deref()));
393                    stack.push(ExprState::from_expr(expr.deref()));
394                    instructions.push(RibIR::SelectIndexV1);
395                }
396            },
397
398            Expr::Option {
399                expr: Some(inner_expr),
400                inferred_type,
401                ..
402            } => {
403                stack.push(ExprState::from_expr(inner_expr.deref()));
404                instructions.push(RibIR::PushSome(convert_to_analysed_type(
405                    expr,
406                    inferred_type,
407                )?));
408            }
409
410            Expr::Option { inferred_type, .. } => {
411                let optional = convert_to_analysed_type(expr, inferred_type);
412                instructions.push(RibIR::PushNone(optional.ok()));
413            }
414
415            Expr::Result {
416                expr: Ok(inner_expr),
417                inferred_type,
418                ..
419            } => {
420                stack.push(ExprState::from_expr(inner_expr.deref()));
421                instructions.push(RibIR::PushOkResult(convert_to_analysed_type(
422                    expr,
423                    inferred_type,
424                )?));
425            }
426
427            Expr::Result {
428                expr: Err(inner_expr),
429                inferred_type,
430                ..
431            } => {
432                stack.push(ExprState::from_expr(inner_expr.deref()));
433                instructions.push(RibIR::PushErrResult(convert_to_analysed_type(
434                    expr,
435                    inferred_type,
436                )?));
437            }
438
439            Expr::Call {
440                call_type,
441                args,
442                inferred_type,
443                ..
444            } => {
445                // If the call type is an instance creation (worker creation),
446                // this will push worker name expression.
447                match call_type {
448                    CallType::Function {
449                        function_name,
450                        instance_identifier: module,
451                        component_info,
452                    } => {
453                        for expr in args.iter().rev() {
454                            stack.push(ExprState::from_expr(expr));
455                        }
456
457                        let function_result_type = if inferred_type.is_unit() {
458                            AnalysedTypeWithUnit::Unit
459                        } else {
460                            AnalysedTypeWithUnit::Type(convert_to_analysed_type(
461                                expr,
462                                inferred_type,
463                            )?)
464                        };
465
466                        let module = module
467                            .as_ref()
468                            .expect("Module should be present for function calls");
469
470                        let instance_variable = match module.as_ref() {
471                            InstanceIdentifier::WitResource { variable_id, .. } => {
472                                let variable_id = variable_id.clone().ok_or({
473                                    RibByteCodeGenerationError::UnresolvedResourceVariable
474                                })?;
475                                InstanceVariable::WitResource(variable_id)
476                            }
477                            InstanceIdentifier::WitWorker { variable_id, .. } => {
478                                let variable_id = variable_id
479                                    .clone()
480                                    .ok_or(RibByteCodeGenerationError::UnresolvedWorkerName)?;
481
482                                InstanceVariable::WitWorker(variable_id)
483                            }
484                        };
485
486                        let component_info = component_info.as_ref().ok_or(
487                            RibByteCodeGenerationError::UnresolvedWasmComponent {
488                                function: function_name.to_string(),
489                            },
490                        )?;
491
492                        instructions.push(RibIR::InvokeFunction(
493                            component_info.clone(),
494                            instance_variable,
495                            args.len(),
496                            function_result_type,
497                        ));
498
499                        let site = function_name.site.clone();
500
501                        // Resolve the function name and update stack
502                        match &function_name.function {
503                            DynamicParsedFunctionReference::Function { function } => instructions
504                                .push(RibIR::CreateFunctionName(
505                                    site,
506                                    FunctionReferenceType::Function {
507                                        function: function.clone(),
508                                    },
509                                )),
510
511                            DynamicParsedFunctionReference::RawResourceConstructor { resource } => {
512                                instructions.push(RibIR::CreateFunctionName(
513                                    site,
514                                    FunctionReferenceType::RawResourceConstructor {
515                                        resource: resource.clone(),
516                                    },
517                                ))
518                            }
519                            DynamicParsedFunctionReference::RawResourceDrop { resource } => {
520                                instructions.push(RibIR::CreateFunctionName(
521                                    site,
522                                    FunctionReferenceType::RawResourceDrop {
523                                        resource: resource.clone(),
524                                    },
525                                ))
526                            }
527                            DynamicParsedFunctionReference::RawResourceMethod {
528                                resource,
529                                method,
530                            } => instructions.push(RibIR::CreateFunctionName(
531                                site,
532                                FunctionReferenceType::RawResourceMethod {
533                                    resource: resource.clone(),
534                                    method: method.clone(),
535                                },
536                            )),
537                            DynamicParsedFunctionReference::RawResourceStaticMethod {
538                                resource,
539                                method,
540                            } => instructions.push(RibIR::CreateFunctionName(
541                                site,
542                                FunctionReferenceType::RawResourceStaticMethod {
543                                    resource: resource.clone(),
544                                    method: method.clone(),
545                                },
546                            )),
547                            DynamicParsedFunctionReference::IndexedResourceConstructor {
548                                resource,
549                                resource_params,
550                            } => {
551                                for param in resource_params {
552                                    stack.push(ExprState::from_expr(param));
553                                }
554                                instructions.push(RibIR::CreateFunctionName(
555                                    site,
556                                    FunctionReferenceType::IndexedResourceConstructor {
557                                        resource: resource.clone(),
558                                        arg_size: resource_params.len(),
559                                    },
560                                ))
561                            }
562                            DynamicParsedFunctionReference::IndexedResourceMethod {
563                                resource,
564                                resource_params,
565                                method,
566                            } => {
567                                for param in resource_params {
568                                    stack.push(ExprState::from_expr(param));
569                                }
570                                instructions.push(RibIR::CreateFunctionName(
571                                    site,
572                                    FunctionReferenceType::IndexedResourceMethod {
573                                        resource: resource.clone(),
574                                        arg_size: resource_params.len(),
575                                        method: method.clone(),
576                                    },
577                                ))
578                            }
579                            DynamicParsedFunctionReference::IndexedResourceStaticMethod {
580                                resource,
581                                resource_params,
582                                method,
583                            } => {
584                                for param in resource_params {
585                                    stack.push(ExprState::from_expr(param));
586                                }
587                                instructions.push(RibIR::CreateFunctionName(
588                                    site,
589                                    FunctionReferenceType::IndexedResourceStaticMethod {
590                                        resource: resource.clone(),
591                                        arg_size: resource_params.len(),
592                                        method: method.clone(),
593                                    },
594                                ))
595                            }
596                            DynamicParsedFunctionReference::IndexedResourceDrop {
597                                resource,
598                                resource_params,
599                            } => {
600                                for param in resource_params {
601                                    stack.push(ExprState::from_expr(param));
602                                }
603                                instructions.push(RibIR::CreateFunctionName(
604                                    site,
605                                    FunctionReferenceType::IndexedResourceDrop {
606                                        resource: resource.clone(),
607                                        arg_size: resource_params.len(),
608                                    },
609                                ))
610                            }
611                        }
612                    }
613
614                    // if there are no arguments to instance that would typically mean
615                    // it's an ephemeral worker or a resource with no arguments
616                    // This would imply there is nothing in the stack of instructions related
617                    // to these cases. So to make sure expressions such as the following work,
618                    // we need to push a place holder in the stack that does nothing
619                    CallType::InstanceCreation(instance_creation_type) => {
620                        match instance_creation_type {
621                            InstanceCreationType::WitWorker { .. } => {
622                                for expr in args.iter().rev() {
623                                    stack.push(ExprState::from_expr(expr));
624                                }
625                            }
626
627                            InstanceCreationType::WitResource {
628                                module,
629                                resource_name,
630                                component_info,
631                            } => {
632                                for expr in args.iter().rev() {
633                                    stack.push(ExprState::from_expr(expr));
634                                }
635
636                                let module = module
637                                    .as_ref()
638                                    .expect("Module should be present for resource calls");
639
640                                let instance_variable = match module {
641                                    InstanceIdentifier::WitResource { variable_id, .. } => {
642                                        let variable_id = variable_id.as_ref().ok_or({
643                                            RibByteCodeGenerationError::UnresolvedResourceVariable
644                                        })?;
645
646                                        InstanceVariable::WitResource(variable_id.clone())
647                                    }
648                                    InstanceIdentifier::WitWorker { variable_id, .. } => {
649                                        let variable_id = variable_id.as_ref().ok_or({
650                                            RibByteCodeGenerationError::UnresolvedWorkerName
651                                        })?;
652
653                                        InstanceVariable::WitWorker(variable_id.clone())
654                                    }
655                                };
656
657                                let site = resource_name.parsed_function_site();
658
659                                let component_info = component_info.as_ref().ok_or(
660                                    RibByteCodeGenerationError::UnresolvedWasmComponent {
661                                        function: resource_name.resource_name.clone(),
662                                    },
663                                )?;
664
665                                let function_result_type = if inferred_type.is_unit() {
666                                    AnalysedTypeWithUnit::Unit
667                                } else {
668                                    AnalysedTypeWithUnit::Type(convert_to_analysed_type(
669                                        expr,
670                                        inferred_type,
671                                    )?)
672                                };
673
674                                instructions.push(RibIR::InvokeFunction(
675                                    component_info.clone(),
676                                    instance_variable,
677                                    args.len(),
678                                    function_result_type,
679                                ));
680
681                                instructions.push(RibIR::CreateFunctionName(
682                                    site,
683                                    FunctionReferenceType::RawResourceConstructor {
684                                        resource: resource_name.resource_name.clone(),
685                                    },
686                                ));
687                            }
688                        }
689                    }
690
691                    CallType::VariantConstructor(variant_name) => {
692                        for expr in args.iter().rev() {
693                            stack.push(ExprState::from_expr(expr));
694                        }
695
696                        instructions.push(RibIR::PushVariant(
697                            variant_name.clone(),
698                            convert_to_analysed_type(expr, inferred_type)?,
699                        ));
700                    }
701                    CallType::EnumConstructor(enum_name) => {
702                        for expr in args.iter().rev() {
703                            stack.push(ExprState::from_expr(expr));
704                        }
705
706                        instructions.push(RibIR::PushEnum(
707                            enum_name.clone(),
708                            convert_to_analysed_type(expr, inferred_type)?,
709                        ));
710                    }
711                }
712            }
713
714            Expr::Flags {
715                flags,
716                inferred_type,
717                ..
718            } => match inferred_type.internal_type() {
719                TypeInternal::Flags(all_flags) => {
720                    let mut bitmap = Vec::new();
721                    let flag_values_set: HashSet<&String> = HashSet::from_iter(flags.iter());
722                    for flag in all_flags.iter() {
723                        bitmap.push(flag_values_set.contains(flag));
724                    }
725                    instructions.push(RibIR::PushFlag(ValueAndType {
726                        value: Value::Flags(bitmap),
727                        typ: AnalysedType::Flags(TypeFlags {
728                            names: all_flags.iter().map(|n| n.to_string()).collect(),
729                            owner: None,
730                            name: None,
731                        }),
732                    }));
733                }
734                _ => {
735                    return Err(RibByteCodeGenerationError::UnexpectedTypeError {
736                        expected: TypeHint::Flag(Some(flags.clone())),
737                        actual: inferred_type.get_type_hint(),
738                    });
739                }
740            },
741            Expr::Boolean { value, .. } => {
742                instructions.push(RibIR::PushLit(value.into_value_and_type()));
743            }
744            Expr::GetTag { expr, .. } => {
745                stack.push(ExprState::from_expr(expr.deref()));
746                stack.push(ExprState::from_ir(RibIR::GetTag));
747            }
748
749            Expr::Concat { exprs, .. } => {
750                for expr in exprs.iter().rev() {
751                    stack.push(ExprState::from_expr(expr));
752                }
753
754                instructions.push(RibIR::Concat(exprs.len()));
755            }
756
757            Expr::Not { expr, .. } => {
758                stack.push(ExprState::from_expr(expr.deref()));
759                instructions.push(RibIR::Negate);
760            }
761
762            Expr::Tuple {
763                exprs,
764                inferred_type,
765                ..
766            } => {
767                for expr in exprs.iter().rev() {
768                    stack.push(ExprState::from_expr(expr));
769                }
770                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
771                instructions.push(RibIR::PushTuple(analysed_type, exprs.len()));
772            }
773
774            Expr::ListComprehension {
775                iterated_variable,
776                iterable_expr,
777                yield_expr,
778                inferred_type,
779                ..
780            } => {
781                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
782                handle_list_comprehension(
783                    instruction_id,
784                    stack,
785                    iterable_expr,
786                    yield_expr,
787                    iterated_variable,
788                    &analysed_type,
789                )
790            }
791
792            range_expr @ Expr::Range {
793                range,
794                inferred_type,
795                ..
796            } => match inferred_type.internal_type() {
797                TypeInternal::Range { .. } => {
798                    let analysed_type = convert_to_analysed_type(range_expr, inferred_type)?;
799
800                    handle_range(range, stack, analysed_type, instructions);
801                }
802
803                _ => {
804                    return Err(RibByteCodeGenerationError::UnexpectedTypeError {
805                        expected: TypeHint::Range,
806                        actual: inferred_type.get_type_hint(),
807                    });
808                }
809            },
810
811            // Invoke is always handled by the CallType::Function branch
812            Expr::InvokeMethodLazy { .. } => {}
813
814            Expr::ListReduce {
815                reduce_variable,
816                iterated_variable,
817                iterable_expr,
818                init_value_expr,
819                yield_expr,
820                ..
821            } => handle_list_reduce(
822                instruction_id,
823                stack,
824                reduce_variable,
825                iterated_variable,
826                iterable_expr,
827                init_value_expr,
828                yield_expr,
829            ),
830        }
831
832        Ok(())
833    }
834
835    pub(crate) fn convert_to_analysed_type(
836        expr: &Expr,
837        inferred_type: &InferredType,
838    ) -> Result<AnalysedType, RibByteCodeGenerationError> {
839        AnalysedType::try_from(inferred_type).map_err(|error| {
840            RibByteCodeGenerationError::AnalysedTypeConversionError(format!(
841                "Invalid Rib {}. Error converting {} to AnalysedType: {}",
842                expr,
843                inferred_type.get_type_hint(),
844                error
845            ))
846        })
847    }
848
849    // We create a temporary stack of expressions that we pop one by one,
850    // while injecting some pre-defined IRs such as Jump in certain cases
851    // A stack is required on one side to maintain the order of expressions
852    // As soon a `Expr` becomes `Instruction` the instruction stack will be in order.
853    pub(crate) enum ExprState {
854        Expr(Expr),
855        Instruction(RibIR),
856    }
857
858    impl ExprState {
859        pub(crate) fn from_expr(expr: &Expr) -> Self {
860            ExprState::Expr(expr.clone())
861        }
862
863        pub(crate) fn from_ir(ir: RibIR) -> Self {
864            ExprState::Instruction(ir)
865        }
866    }
867
868    fn handle_range(
869        range: &Range,
870        stack: &mut Vec<ExprState>,
871        analysed_type: AnalysedType,
872        instructions: &mut Vec<RibIR>,
873    ) {
874        let from = range.from();
875        let to = range.to();
876        let inclusive = range.inclusive();
877
878        if let Some(from) = from {
879            stack.push(ExprState::from_expr(from));
880            instructions.push(RibIR::UpdateRecord("from".to_string()));
881        }
882
883        if let Some(to) = to {
884            stack.push(ExprState::from_expr(to));
885            instructions.push(RibIR::UpdateRecord("to".to_string()));
886        }
887
888        stack.push(ExprState::from_ir(RibIR::PushLit(ValueAndType::new(
889            Value::Bool(inclusive),
890            bool(),
891        ))));
892
893        instructions.push(RibIR::UpdateRecord("inclusive".to_string()));
894
895        instructions.push(RibIR::CreateAndPushRecord(analysed_type));
896    }
897
898    fn handle_list_comprehension(
899        instruction_id: &mut InstructionId,
900        stack: &mut Vec<ExprState>,
901        iterable_expr: &Expr,
902        yield_expr: &Expr,
903        variable_id: &VariableId,
904        sink_type: &AnalysedType,
905    ) {
906        stack.push(ExprState::from_expr(iterable_expr));
907
908        stack.push(ExprState::from_ir(RibIR::ToIterator));
909
910        stack.push(ExprState::from_ir(RibIR::CreateSink(sink_type.clone())));
911
912        let loop_start_label = instruction_id.increment_mut();
913
914        stack.push(ExprState::from_ir(RibIR::Label(loop_start_label.clone())));
915
916        let exit_label = instruction_id.increment_mut();
917
918        stack.push(ExprState::from_ir(RibIR::IsEmpty));
919
920        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(exit_label.clone())));
921
922        stack.push(ExprState::from_ir(RibIR::AdvanceIterator));
923
924        stack.push(ExprState::from_ir(RibIR::AssignVar(variable_id.clone())));
925
926        stack.push(ExprState::from_expr(yield_expr));
927
928        stack.push(ExprState::from_ir(RibIR::PushToSink));
929
930        stack.push(ExprState::from_ir(RibIR::Jump(loop_start_label)));
931
932        stack.push(ExprState::from_ir(RibIR::Label(exit_label)));
933
934        stack.push(ExprState::from_ir(RibIR::SinkToList))
935    }
936
937    fn handle_list_reduce(
938        instruction_id: &mut InstructionId,
939        stack: &mut Vec<ExprState>,
940        reduce_variable: &VariableId,
941        iterated_variable: &VariableId,
942        iterable_expr: &Expr,
943        initial_value_expr: &Expr,
944        yield_expr: &Expr,
945    ) {
946        stack.push(ExprState::from_expr(iterable_expr));
947
948        stack.push(ExprState::from_expr(initial_value_expr));
949
950        stack.push(ExprState::from_ir(RibIR::AssignVar(
951            reduce_variable.clone(),
952        )));
953
954        stack.push(ExprState::from_ir(RibIR::ToIterator));
955
956        let loop_start_label = instruction_id.increment_mut();
957
958        stack.push(ExprState::from_ir(RibIR::Label(loop_start_label.clone())));
959
960        let exit_label = instruction_id.increment_mut();
961
962        stack.push(ExprState::from_ir(RibIR::IsEmpty));
963
964        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(exit_label.clone())));
965
966        stack.push(ExprState::from_ir(RibIR::AdvanceIterator));
967
968        stack.push(ExprState::from_ir(RibIR::AssignVar(
969            iterated_variable.clone(),
970        )));
971
972        stack.push(ExprState::from_expr(yield_expr));
973
974        stack.push(ExprState::from_ir(RibIR::AssignVar(
975            reduce_variable.clone(),
976        )));
977
978        stack.push(ExprState::from_ir(RibIR::Jump(loop_start_label)));
979
980        stack.push(ExprState::from_ir(RibIR::Label(exit_label)));
981
982        stack.push(ExprState::from_ir(RibIR::LoadVar(reduce_variable.clone())))
983    }
984
985    fn handle_if_condition(
986        instruction_id: &mut InstructionId,
987        if_expr: &Expr,
988        then_expr: &Expr,
989        else_expr: &Expr,
990        stack: &mut Vec<ExprState>,
991    ) {
992        instruction_id.increment_mut();
993        let else_beginning_id = instruction_id.clone();
994        instruction_id.increment_mut();
995        let else_ending_id = instruction_id.clone();
996
997        stack.push(ExprState::from_expr(if_expr));
998
999        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(
1000            else_beginning_id.clone(),
1001        )));
1002
1003        stack.push(ExprState::from_expr(then_expr));
1004
1005        stack.push(ExprState::from_ir(RibIR::Jump(else_ending_id.clone())));
1006
1007        stack.push(ExprState::from_ir(RibIR::Label(else_beginning_id.clone())));
1008
1009        stack.push(ExprState::from_expr(else_expr));
1010
1011        stack.push(ExprState::from_ir(RibIR::Label(else_ending_id.clone())));
1012    }
1013}
1014
1015#[cfg(test)]
1016mod compiler_tests {
1017    use bigdecimal::BigDecimal;
1018    use test_r::test;
1019
1020    use super::*;
1021    use crate::{ArmPattern, InferredType, MatchArm, RibCompiler, VariableId};
1022    use golem_wasm_ast::analysis::analysed_type;
1023    use golem_wasm_ast::analysis::analysed_type::{field, list, record, s32, str};
1024    use golem_wasm_rpc::{IntoValueAndType, Value, ValueAndType};
1025
1026    #[test]
1027    fn test_instructions_for_literal() {
1028        let literal = Expr::literal("hello");
1029
1030        let compiler = RibCompiler::default();
1031
1032        let compiler_output = compiler.compile(literal).unwrap();
1033
1034        let instruction_set = vec![RibIR::PushLit("hello".into_value_and_type())];
1035
1036        let expected_instructions = RibByteCode {
1037            instructions: instruction_set,
1038        };
1039
1040        assert_eq!(compiler_output.byte_code, expected_instructions);
1041    }
1042
1043    #[test]
1044    fn test_instructions_for_identifier() {
1045        let inferred_input_type = InferredType::string();
1046
1047        let variable_id = VariableId::local("request", 0);
1048
1049        let expr = Expr::identifier_with_variable_id(variable_id.clone(), None)
1050            .with_inferred_type(inferred_input_type);
1051
1052        let compiler = RibCompiler::default();
1053
1054        let inferred_expr = compiler.infer_types(expr).unwrap();
1055
1056        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1057
1058        let instruction_set = vec![RibIR::LoadVar(variable_id)];
1059
1060        let expected_instructions = RibByteCode {
1061            instructions: instruction_set,
1062        };
1063
1064        assert_eq!(instructions, expected_instructions);
1065    }
1066
1067    #[test]
1068    fn test_instructions_assign_variable() {
1069        let literal = Expr::literal("hello");
1070
1071        let variable_id = VariableId::local("request", 0);
1072
1073        let expr = Expr::let_binding_with_variable_id(variable_id.clone(), literal, None);
1074
1075        let compiler = RibCompiler::default();
1076
1077        let inferred_expr = compiler.infer_types(expr).unwrap();
1078
1079        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1080
1081        let instruction_set = vec![
1082            RibIR::PushLit("hello".into_value_and_type()),
1083            RibIR::AssignVar(variable_id),
1084        ];
1085
1086        let expected_instructions = RibByteCode {
1087            instructions: instruction_set,
1088        };
1089
1090        assert_eq!(instructions, expected_instructions);
1091    }
1092
1093    #[test]
1094    fn test_instructions_equal_to() {
1095        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1096
1097        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1098
1099        let expr = Expr::equal_to(number_f32, number_u32);
1100
1101        let compiler = RibCompiler::default();
1102
1103        let inferred_expr = compiler.infer_types(expr).unwrap();
1104
1105        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1106
1107        let value_and_type1 = 1.0f32.into_value_and_type();
1108
1109        let value_and_type2 = 1u32.into_value_and_type();
1110
1111        let instruction_set = vec![
1112            RibIR::PushLit(value_and_type2),
1113            RibIR::PushLit(value_and_type1),
1114            RibIR::EqualTo,
1115        ];
1116
1117        let expected_instructions = RibByteCode {
1118            instructions: instruction_set,
1119        };
1120
1121        assert_eq!(instructions, expected_instructions);
1122    }
1123
1124    #[test]
1125    fn test_instructions_greater_than() {
1126        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1127
1128        let number_u32 = Expr::number_inferred(BigDecimal::from(2), None, InferredType::u32());
1129
1130        let expr = Expr::greater_than(number_f32, number_u32);
1131
1132        let compiler = RibCompiler::default();
1133
1134        let inferred_expr = compiler.infer_types(expr).unwrap();
1135
1136        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1137
1138        let value_and_type1 = 1.0f32.into_value_and_type();
1139
1140        let value_and_type2 = 2u32.into_value_and_type();
1141
1142        let instruction_set = vec![
1143            RibIR::PushLit(value_and_type2),
1144            RibIR::PushLit(value_and_type1),
1145            RibIR::GreaterThan,
1146        ];
1147
1148        let expected_instructions = RibByteCode {
1149            instructions: instruction_set,
1150        };
1151
1152        assert_eq!(instructions, expected_instructions);
1153    }
1154
1155    #[test]
1156    fn test_instructions_less_than() {
1157        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1158
1159        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1160
1161        let expr = Expr::less_than(number_f32, number_u32);
1162
1163        let compiler = RibCompiler::default();
1164
1165        let inferred_expr = compiler.infer_types(expr).unwrap();
1166
1167        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1168
1169        let value_and_type1 = 1.0f32.into_value_and_type();
1170
1171        let value_and_type2 = 1u32.into_value_and_type();
1172
1173        let instruction_set = vec![
1174            RibIR::PushLit(value_and_type2),
1175            RibIR::PushLit(value_and_type1),
1176            RibIR::LessThan,
1177        ];
1178
1179        let expected_instructions = RibByteCode {
1180            instructions: instruction_set,
1181        };
1182
1183        assert_eq!(instructions, expected_instructions);
1184    }
1185
1186    #[test]
1187    fn test_instructions_greater_than_or_equal_to() {
1188        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1189
1190        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1191
1192        let expr = Expr::greater_than_or_equal_to(number_f32, number_u32);
1193
1194        let compiler = RibCompiler::default();
1195
1196        let inferred_expr = compiler.infer_types(expr).unwrap();
1197
1198        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1199
1200        let value_and_type1 = 1.0f32.into_value_and_type();
1201
1202        let value_and_type2 = 1u32.into_value_and_type();
1203
1204        let instruction_set = vec![
1205            RibIR::PushLit(value_and_type2),
1206            RibIR::PushLit(value_and_type1),
1207            RibIR::GreaterThanOrEqualTo,
1208        ];
1209
1210        let expected_instructions = RibByteCode {
1211            instructions: instruction_set,
1212        };
1213
1214        assert_eq!(instructions, expected_instructions);
1215    }
1216
1217    #[test]
1218    fn test_instructions_less_than_or_equal_to() {
1219        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1220
1221        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1222
1223        let expr = Expr::less_than_or_equal_to(number_f32, number_u32);
1224
1225        let compiler = RibCompiler::default();
1226
1227        let inferred_expr = compiler.infer_types(expr).unwrap();
1228
1229        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1230
1231        let value_and_type1 = 1.0f32.into_value_and_type();
1232
1233        let value_and_type2 = 1u32.into_value_and_type();
1234
1235        let instruction_set = vec![
1236            RibIR::PushLit(value_and_type2),
1237            RibIR::PushLit(value_and_type1),
1238            RibIR::LessThanOrEqualTo,
1239        ];
1240
1241        let expected_instructions = RibByteCode {
1242            instructions: instruction_set,
1243        };
1244
1245        assert_eq!(instructions, expected_instructions);
1246    }
1247
1248    #[test]
1249    fn test_instructions_for_record() {
1250        let expr = Expr::record(vec![
1251            ("foo_key".to_string(), Expr::literal("foo_value")),
1252            ("bar_key".to_string(), Expr::literal("bar_value")),
1253        ])
1254        .with_inferred_type(InferredType::record(vec![
1255            (String::from("foo_key"), InferredType::string()),
1256            (String::from("bar_key"), InferredType::string()),
1257        ]));
1258
1259        let compiler = RibCompiler::default();
1260
1261        let inferred_expr = compiler.infer_types(expr).unwrap();
1262
1263        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1264
1265        let bar_value = "bar_value".into_value_and_type();
1266
1267        let foo_value = "foo_value".into_value_and_type();
1268
1269        let instruction_set = vec![
1270            RibIR::PushLit(bar_value),
1271            RibIR::PushLit(foo_value),
1272            RibIR::CreateAndPushRecord(record(vec![
1273                field("foo_key", str()),
1274                field("bar_key", str()),
1275            ])),
1276            RibIR::UpdateRecord("foo_key".to_string()),
1277            RibIR::UpdateRecord("bar_key".to_string()),
1278        ];
1279
1280        let expected_instructions = RibByteCode {
1281            instructions: instruction_set,
1282        };
1283
1284        assert_eq!(instructions, expected_instructions);
1285    }
1286
1287    #[test]
1288    fn test_instructions_for_multiple() {
1289        let expr = Expr::expr_block(vec![Expr::literal("foo"), Expr::literal("bar")]);
1290
1291        let compiler = RibCompiler::default();
1292
1293        let inferred_expr = compiler.infer_types(expr).unwrap();
1294
1295        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1296
1297        let instruction_set = vec![
1298            RibIR::PushLit("foo".into_value_and_type()),
1299            RibIR::PushLit("bar".into_value_and_type()),
1300        ];
1301
1302        let expected_instructions = RibByteCode {
1303            instructions: instruction_set,
1304        };
1305
1306        assert_eq!(instructions, expected_instructions);
1307    }
1308
1309    #[test]
1310    fn test_instructions_if_conditional() {
1311        let if_expr = Expr::literal("pred").with_inferred_type(InferredType::bool());
1312
1313        let then_expr = Expr::literal("then");
1314
1315        let else_expr = Expr::literal("else");
1316
1317        let expr =
1318            Expr::cond(if_expr, then_expr, else_expr).with_inferred_type(InferredType::string());
1319
1320        let compiler = RibCompiler::default();
1321
1322        let inferred_expr = compiler.infer_types(expr).unwrap();
1323
1324        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1325
1326        let instruction_set = vec![
1327            RibIR::PushLit("pred".into_value_and_type()),
1328            RibIR::JumpIfFalse(InstructionId { index: 1 }), // jumps to the next label having Id 1 (which is else block)
1329            RibIR::PushLit("then".into_value_and_type()),
1330            RibIR::Jump(InstructionId { index: 2 }), // Once if is executed then jump to the end of the else block with id 2
1331            RibIR::Label(InstructionId { index: 1 }),
1332            RibIR::PushLit("else".into_value_and_type()),
1333            RibIR::Label(InstructionId { index: 2 }),
1334        ];
1335
1336        let expected_instructions = RibByteCode {
1337            instructions: instruction_set,
1338        };
1339
1340        assert_eq!(instructions, expected_instructions);
1341    }
1342
1343    #[test]
1344    fn test_instructions_for_nested_if_else() {
1345        let if_expr = Expr::literal("if-pred1").with_inferred_type(InferredType::bool());
1346
1347        let then_expr = Expr::literal("then1").with_inferred_type(InferredType::string());
1348
1349        let else_expr = Expr::cond(
1350            Expr::literal("else-pred2").with_inferred_type(InferredType::bool()),
1351            Expr::literal("else-then2"),
1352            Expr::literal("else-else2"),
1353        )
1354        .with_inferred_type(InferredType::string());
1355
1356        let expr =
1357            Expr::cond(if_expr, then_expr, else_expr).with_inferred_type(InferredType::string());
1358
1359        let compiler = RibCompiler::default();
1360
1361        let inferred_expr = compiler.infer_types(expr).unwrap();
1362
1363        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1364
1365        let instruction_set = vec![
1366            // if case
1367            RibIR::PushLit("if-pred1".into_value_and_type()),
1368            RibIR::JumpIfFalse(InstructionId { index: 1 }), // jumps to the next label having Id 1 (which is else block)
1369            RibIR::PushLit("then1".into_value_and_type()),
1370            RibIR::Jump(InstructionId { index: 2 }), // Once if is executed then jump to the end of the else block with id 3
1371            RibIR::Label(InstructionId { index: 1 }),
1372            RibIR::PushLit("else-pred2".into_value_and_type()),
1373            RibIR::JumpIfFalse(InstructionId { index: 3 }), // jumps to the next label having Id 2 (which is else block)
1374            RibIR::PushLit("else-then2".into_value_and_type()),
1375            RibIR::Jump(InstructionId { index: 4 }), // Once if is executed then jump to the end of the else block with id 3
1376            RibIR::Label(InstructionId { index: 3 }),
1377            RibIR::PushLit("else-else2".into_value_and_type()),
1378            RibIR::Label(InstructionId { index: 4 }),
1379            RibIR::Label(InstructionId { index: 2 }),
1380        ];
1381
1382        let expected_instructions = RibByteCode {
1383            instructions: instruction_set,
1384        };
1385
1386        assert_eq!(instructions, expected_instructions);
1387    }
1388
1389    #[test]
1390    fn test_instructions_for_select_field() {
1391        let record = Expr::record(vec![
1392            ("foo_key".to_string(), Expr::literal("foo_value")),
1393            ("bar_key".to_string(), Expr::literal("bar_value")),
1394        ])
1395        .with_inferred_type(InferredType::record(vec![
1396            (String::from("foo_key"), InferredType::string()),
1397            (String::from("bar_key"), InferredType::string()),
1398        ]));
1399
1400        let expr =
1401            Expr::select_field(record, "bar_key", None).with_inferred_type(InferredType::string());
1402
1403        let compiler = RibCompiler::default();
1404
1405        let inferred_expr = compiler.infer_types(expr).unwrap();
1406
1407        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1408
1409        let bar_value = "bar_value".into_value_and_type();
1410
1411        let foo_value = "foo_value".into_value_and_type();
1412
1413        let instruction_set = vec![
1414            RibIR::PushLit(bar_value),
1415            RibIR::PushLit(foo_value),
1416            RibIR::CreateAndPushRecord(analysed_type::record(vec![
1417                field("bar_key", str()),
1418                field("foo_key", str()),
1419            ])),
1420            RibIR::UpdateRecord("foo_key".to_string()), // next pop is foo_value
1421            RibIR::UpdateRecord("bar_key".to_string()), // last pop is bar_value
1422            RibIR::SelectField("bar_key".to_string()),
1423        ];
1424
1425        let expected_instructions = RibByteCode {
1426            instructions: instruction_set,
1427        };
1428
1429        assert_eq!(instructions, expected_instructions);
1430    }
1431
1432    #[test]
1433    fn test_instructions_for_select_index() {
1434        let sequence = Expr::sequence(vec![Expr::literal("foo"), Expr::literal("bar")], None)
1435            .with_inferred_type(InferredType::list(InferredType::string()));
1436
1437        let expr = Expr::select_index(sequence, Expr::number(BigDecimal::from(1)))
1438            .with_inferred_type(InferredType::string());
1439
1440        let compiler = RibCompiler::default();
1441
1442        let inferred_expr = compiler.infer_types(expr).unwrap();
1443
1444        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1445
1446        let instruction_set = vec![
1447            RibIR::PushLit(ValueAndType::new(Value::S32(1), s32())),
1448            RibIR::PushLit("bar".into_value_and_type()),
1449            RibIR::PushLit("foo".into_value_and_type()),
1450            RibIR::PushList(list(str()), 2),
1451            RibIR::SelectIndexV1,
1452        ];
1453
1454        let expected_instructions = RibByteCode {
1455            instructions: instruction_set,
1456        };
1457
1458        assert_eq!(instructions, expected_instructions);
1459    }
1460
1461    #[test]
1462    fn test_instructions_for_expr_arm_pattern_match() {
1463        let expr = Expr::pattern_match(
1464            Expr::literal("pred"),
1465            vec![
1466                MatchArm::new(
1467                    ArmPattern::Literal(Box::new(Expr::literal("arm1_pattern_expr"))),
1468                    Expr::literal("arm1_resolution_expr"),
1469                ),
1470                MatchArm::new(
1471                    ArmPattern::Literal(Box::new(Expr::literal("arm2_pattern_expr"))),
1472                    Expr::literal("arm2_resolution_expr"),
1473                ),
1474                MatchArm::new(
1475                    ArmPattern::Literal(Box::new(Expr::literal("arm3_pattern_expr"))),
1476                    Expr::literal("arm3_resolution_expr"),
1477                ),
1478            ],
1479        )
1480        .with_inferred_type(InferredType::string());
1481
1482        let rib_compiler = RibCompiler::default();
1483
1484        let instructions = rib_compiler.compile(expr).unwrap().byte_code;
1485
1486        // instructions will correspond to an if-else statement
1487        let instruction_set = vec![
1488            RibIR::PushLit("arm1_pattern_expr".into_value_and_type()),
1489            RibIR::PushLit("pred".into_value_and_type()),
1490            RibIR::EqualTo,
1491            RibIR::JumpIfFalse(InstructionId { index: 1 }),
1492            RibIR::PushLit("arm1_resolution_expr".into_value_and_type()),
1493            RibIR::Jump(InstructionId { index: 2 }),
1494            RibIR::Label(InstructionId { index: 1 }),
1495            RibIR::PushLit("arm2_pattern_expr".into_value_and_type()),
1496            RibIR::PushLit("pred".into_value_and_type()),
1497            RibIR::EqualTo,
1498            RibIR::JumpIfFalse(InstructionId { index: 3 }),
1499            RibIR::PushLit("arm2_resolution_expr".into_value_and_type()),
1500            RibIR::Jump(InstructionId { index: 4 }),
1501            RibIR::Label(InstructionId { index: 3 }),
1502            RibIR::PushLit("arm3_pattern_expr".into_value_and_type()),
1503            RibIR::PushLit("pred".into_value_and_type()),
1504            RibIR::EqualTo,
1505            RibIR::JumpIfFalse(InstructionId { index: 5 }),
1506            RibIR::PushLit("arm3_resolution_expr".into_value_and_type()),
1507            RibIR::Jump(InstructionId { index: 6 }),
1508            RibIR::Label(InstructionId { index: 5 }),
1509            RibIR::Throw("No match found".to_string()),
1510            RibIR::Label(InstructionId { index: 6 }),
1511            RibIR::Label(InstructionId { index: 4 }),
1512            RibIR::Label(InstructionId { index: 2 }),
1513        ];
1514
1515        let expected_instructions = RibByteCode {
1516            instructions: instruction_set,
1517        };
1518
1519        assert_eq!(instructions, expected_instructions);
1520    }
1521
1522    #[cfg(test)]
1523    mod invalid_function_invoke_tests {
1524        use test_r::test;
1525
1526        use crate::compiler::byte_code::compiler_tests::internal;
1527        use crate::{Expr, RibCompiler, RibCompilerConfig};
1528        use golem_wasm_ast::analysis::analysed_type::str;
1529
1530        #[test]
1531        fn test_unknown_function() {
1532            let expr = r#"
1533               foo(request);
1534               "success"
1535            "#;
1536
1537            let expr = Expr::from_text(expr).unwrap();
1538            let compiler = RibCompiler::default();
1539
1540            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1541
1542            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");
1543        }
1544
1545        #[test]
1546        fn test_unknown_resource_method() {
1547            let metadata = internal::metadata_with_resource_methods();
1548            let expr = r#"
1549               let user_id = "user";
1550               golem:it/api.{cart(user_id).add-item}("apple");
1551               golem:it/api.{cart(user_id).foo}("apple");
1552                "success"
1553            "#;
1554
1555            let expr = Expr::from_text(expr).unwrap();
1556
1557            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1558
1559            let compiler = RibCompiler::new(compiler_config);
1560
1561            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1562            assert_eq!(
1563                compiler_error,
1564                "error in the following rib found at line 4, column 16\n`foo(\"apple\")`\ncause: invalid function call `foo`\nunknown function\n"
1565            );
1566        }
1567
1568        #[test]
1569        fn test_invalid_arg_size_function() {
1570            let metadata = internal::get_component_metadata("foo", vec![str()], str());
1571
1572            let expr = r#"
1573               let user_id = "user";
1574               let result = foo(user_id, user_id);
1575               result
1576            "#;
1577
1578            let expr = Expr::from_text(expr).unwrap();
1579
1580            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1581
1582            let compiler = RibCompiler::new(compiler_config);
1583
1584            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1585            assert_eq!(
1586                compiler_error,
1587                "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"
1588            );
1589        }
1590
1591        #[test]
1592        fn test_invalid_arg_size_resource_method() {
1593            let metadata = internal::metadata_with_resource_methods();
1594            let expr = r#"
1595               let user_id = "user";
1596               golem:it/api.{cart(user_id).add-item}("apple", "samsung");
1597                "success"
1598            "#;
1599
1600            let expr = Expr::from_text(expr).unwrap();
1601
1602            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1603
1604            let compiler = RibCompiler::new(compiler_config);
1605
1606            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1607            assert_eq!(
1608                compiler_error,
1609                "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"
1610            );
1611        }
1612
1613        #[test]
1614        fn test_invalid_arg_types_function() {
1615            let metadata = internal::get_component_metadata("foo", vec![str()], str());
1616
1617            let expr = r#"
1618               let result = foo(1u64);
1619               result
1620            "#;
1621
1622            let expr = Expr::from_text(expr).unwrap();
1623
1624            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1625
1626            let compiler = RibCompiler::new(compiler_config);
1627
1628            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1629            assert_eq!(
1630                compiler_error,
1631                "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"
1632            );
1633        }
1634
1635        #[test]
1636        fn test_invalid_arg_types_resource_method() {
1637            let metadata = internal::metadata_with_resource_methods();
1638            let expr = r#"
1639               let user_id = "user";
1640               golem:it/api.{cart(user_id).add-item}("apple");
1641                "success"
1642            "#;
1643
1644            let expr = Expr::from_text(expr).unwrap();
1645
1646            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1647
1648            let compiler = RibCompiler::new(compiler_config);
1649
1650            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1651            assert_eq!(
1652                compiler_error,
1653                "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"
1654            );
1655        }
1656
1657        #[test]
1658        fn test_invalid_arg_types_variants() {
1659            let metadata = internal::metadata_with_variants();
1660
1661            let expr = r#"
1662               let regiser_user_action = register-user("foo");
1663               let result = golem:it/api.{foo}(regiser_user_action);
1664               result
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 2, column 56\n`\"foo\"`\ncause: type mismatch. expected u64, found string\ninvalid argument to the function `register-user`\n"
1677            );
1678        }
1679    }
1680
1681    #[cfg(test)]
1682    mod global_input_tests {
1683        use test_r::test;
1684
1685        use crate::compiler::byte_code::compiler_tests::internal;
1686        use crate::{Expr, RibCompiler, RibCompilerConfig};
1687        use golem_wasm_ast::analysis::analysed_type::{
1688            case, field, list, option, r#enum, record, result, str, tuple, u32, u64, unit_case,
1689            variant,
1690        };
1691
1692        #[test]
1693        async fn test_str_global_input() {
1694            let request_value_type = str();
1695
1696            let output_analysed_type = str();
1697
1698            let analysed_exports = internal::get_component_metadata(
1699                "my-worker-function",
1700                vec![request_value_type.clone()],
1701                output_analysed_type,
1702            );
1703
1704            let expr = r#"
1705               let x = request;
1706               let worker = instance();
1707               worker.my-worker-function(x);
1708               match x {
1709                "foo"  => "success",
1710                 _ => "fallback"
1711               }
1712            "#;
1713
1714            let expr = Expr::from_text(expr).unwrap();
1715            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1716            let compiled = compiler.compile(expr).unwrap();
1717            let expected_type_info =
1718                internal::rib_input_type_info(vec![("request", request_value_type)]);
1719
1720            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1721        }
1722
1723        #[test]
1724        async fn test_number_global_input() {
1725            let request_value_type = u32();
1726
1727            let output_analysed_type = str();
1728
1729            let analysed_exports = internal::get_component_metadata(
1730                "my-worker-function",
1731                vec![request_value_type.clone()],
1732                output_analysed_type,
1733            );
1734
1735            let expr = r#"
1736               let x = request;
1737               let worker = instance();
1738               worker.my-worker-function(x);
1739               match x {
1740                1  => "success",
1741                0 => "failure"
1742               }
1743            "#;
1744
1745            let expr = Expr::from_text(expr).unwrap();
1746            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1747            let compiled = compiler.compile(expr).unwrap();
1748            let expected_type_info =
1749                internal::rib_input_type_info(vec![("request", request_value_type)]);
1750
1751            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1752        }
1753
1754        #[test]
1755        async fn test_variant_type_info() {
1756            let request_value_type = variant(vec![
1757                case("register-user", u64()),
1758                case("process-user", str()),
1759                unit_case("validate"),
1760            ]);
1761
1762            let output_analysed_type = str();
1763
1764            let analysed_exports = internal::get_component_metadata(
1765                "my-worker-function",
1766                vec![request_value_type.clone()],
1767                output_analysed_type,
1768            );
1769
1770            // x = request, implies we are expecting a global variable
1771            // called request as the  input to Rib.
1772            // my-worker-function is a function that takes a Variant as input,
1773            // implies the type of request is a Variant.
1774            // This means the rib interpreter env has to have a request variable in it,
1775            // with a value that should be of the type Variant
1776            let expr = r#"
1777               let worker = instance();
1778               worker.my-worker-function(request);
1779               match request {
1780                 process-user(user) => user,
1781                 _ => "default"
1782               }
1783            "#;
1784
1785            let expr = Expr::from_text(expr).unwrap();
1786            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1787            let compiled = compiler.compile(expr).unwrap();
1788            let expected_type_info =
1789                internal::rib_input_type_info(vec![("request", request_value_type)]);
1790
1791            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1792        }
1793
1794        #[test]
1795        async fn test_result_type_info() {
1796            let request_value_type = result(u64(), str());
1797
1798            let output_analysed_type = str();
1799
1800            let analysed_exports = internal::get_component_metadata(
1801                "my-worker-function",
1802                vec![request_value_type.clone()],
1803                output_analysed_type,
1804            );
1805
1806            // x = request, implies we are expecting a global variable
1807            // called request as the  input to Rib.
1808            // my-worker-function is a function that takes a Result as input,
1809            // implies the type of request is a Result.
1810            // This means the rib interpreter env has to have a request variable in it,
1811            // with a value that should be of the type Result
1812            let expr = r#"
1813               let worker = instance();
1814               worker.my-worker-function(request);
1815               match request {
1816                 ok(x) => "${x}",
1817                 err(msg) => msg
1818               }
1819            "#;
1820
1821            let expr = Expr::from_text(expr).unwrap();
1822            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1823            let compiled = compiler.compile(expr).unwrap();
1824            let expected_type_info =
1825                internal::rib_input_type_info(vec![("request", request_value_type)]);
1826
1827            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1828        }
1829
1830        #[test]
1831        async fn test_option_type_info() {
1832            let request_value_type = option(str());
1833
1834            let output_analysed_type = str();
1835
1836            let analysed_exports = internal::get_component_metadata(
1837                "my-worker-function",
1838                vec![request_value_type.clone()],
1839                output_analysed_type,
1840            );
1841
1842            // x = request, implies we are expecting a global variable
1843            // called request as the input to Rib.
1844            // my-worker-function is a function that takes a Option as input,
1845            // implies the type of request is a Result.
1846            // This means the rib interpreter env has to have a request variable in it,
1847            // with a value that should be of the type Option
1848            let expr = r#"
1849               let worker = instance();
1850               worker.my-worker-function(request);
1851               match request {
1852                 some(x) => x,
1853                 none => "error"
1854               }
1855            "#;
1856
1857            let expr = Expr::from_text(expr).unwrap();
1858            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1859            let compiled = compiler.compile(expr).unwrap();
1860            let expected_type_info =
1861                internal::rib_input_type_info(vec![("request", request_value_type)]);
1862
1863            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1864        }
1865
1866        #[test]
1867        async fn test_enum_type_info() {
1868            let request_value_type = r#enum(&["prod", "dev", "test"]);
1869            let output_analysed_type = str();
1870
1871            let analysed_exports = internal::get_component_metadata(
1872                "my-worker-function",
1873                vec![request_value_type.clone()],
1874                output_analysed_type,
1875            );
1876
1877            // x = request, implies we are expecting a global variable
1878            // called request as the input to Rib.
1879            // my-worker-function is a function that takes a Option as input,
1880            // implies the type of request is a Result.
1881            // This means the rib interpreter env has to have a request variable in it,
1882            // with a value that should be of the type Option
1883            let expr = r#"
1884               let worker = instance();
1885               worker.my-worker-function(request);
1886               match request {
1887                 prod  => "p",
1888                 dev => "d",
1889                 test => "t"
1890               }
1891            "#;
1892
1893            let expr = Expr::from_text(expr).unwrap();
1894            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1895            let compiled = compiler.compile(expr).unwrap();
1896            let expected_type_info =
1897                internal::rib_input_type_info(vec![("request", request_value_type)]);
1898
1899            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1900        }
1901
1902        #[test]
1903        async fn test_record_global_input() {
1904            let request_value_type =
1905                record(vec![field("path", record(vec![field("user", str())]))]);
1906
1907            let output_analysed_type = str();
1908
1909            let analysed_exports = internal::get_component_metadata(
1910                "my-worker-function",
1911                vec![request_value_type.clone()],
1912                output_analysed_type,
1913            );
1914
1915            // x = request, implies we are expecting a global variable
1916            // called request as the  input to Rib.
1917            // my-worker-function is a function that takes a Record of path -> user -> str as input
1918            // implies the type of request is a Record.
1919            // This means the rib interpreter env has to have a request variable in it,
1920            // with a value that should be of the type Record
1921            let expr = r#"
1922               let x = request;
1923               let worker = instance();
1924               worker.my-worker-function(x);
1925
1926               let name = x.path.user;
1927
1928               match x {
1929                 { path : { user : some_name } } => some_name,
1930                 _ => name
1931               }
1932            "#;
1933
1934            let expr = Expr::from_text(expr).unwrap();
1935            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1936            let compiled = compiler.compile(expr).unwrap();
1937            let expected_type_info =
1938                internal::rib_input_type_info(vec![("request", request_value_type)]);
1939
1940            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1941        }
1942
1943        #[test]
1944        async fn test_tuple_global_input() {
1945            let request_value_type = tuple(vec![str(), u32(), record(vec![field("user", str())])]);
1946
1947            let output_analysed_type = str();
1948
1949            let analysed_exports = internal::get_component_metadata(
1950                "my-worker-function",
1951                vec![request_value_type.clone()],
1952                output_analysed_type,
1953            );
1954
1955            // x = request, implies we are expecting a global variable
1956            // called request as the  input to Rib.
1957            // my-worker-function is a function that takes a Tuple,
1958            // implies the type of request is a Tuple.
1959            let expr = r#"
1960               let x = request;
1961               let worker = instance();
1962               worker.my-worker-function(x);
1963               match x {
1964                (_, _, record) =>  record.user,
1965                 _ => "fallback"
1966               }
1967            "#;
1968
1969            let expr = Expr::from_text(expr).unwrap();
1970            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1971            let compiled = compiler.compile(expr).unwrap();
1972            let expected_type_info =
1973                internal::rib_input_type_info(vec![("request", request_value_type)]);
1974
1975            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1976        }
1977
1978        #[test]
1979        async fn test_list_global_input() {
1980            let request_value_type = list(str());
1981
1982            let output_analysed_type = str();
1983
1984            let analysed_exports = internal::get_component_metadata(
1985                "my-worker-function",
1986                vec![request_value_type.clone()],
1987                output_analysed_type,
1988            );
1989
1990            // x = request, implies we are expecting a global variable
1991            // called request as the  input to Rib.
1992            // my-worker-function is a function that takes a List,
1993            // implies the type of request should be a List
1994            let expr = r#"
1995               let x = request;
1996               let worker = instance();
1997               worker.my-worker-function(x);
1998               match x {
1999               [a, b, c]  => a,
2000                 _ => "fallback"
2001               }
2002            "#;
2003
2004            let expr = Expr::from_text(expr).unwrap();
2005            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
2006            let compiled = compiler.compile(expr).unwrap();
2007            let expected_type_info =
2008                internal::rib_input_type_info(vec![("request", request_value_type)]);
2009
2010            assert_eq!(compiled.rib_input_type_info, expected_type_info);
2011        }
2012    }
2013
2014    mod internal {
2015        use crate::{ComponentDependency, ComponentDependencyKey, RibInputTypeInfo};
2016        use golem_wasm_ast::analysis::analysed_type::{
2017            case, field, record, str, u64, unit_case, variant,
2018        };
2019        use golem_wasm_ast::analysis::*;
2020        use std::collections::HashMap;
2021        use uuid::Uuid;
2022
2023        pub(crate) fn metadata_with_variants() -> Vec<ComponentDependency> {
2024            let instance = AnalysedExport::Instance(AnalysedInstance {
2025                name: "golem:it/api".to_string(),
2026                functions: vec![AnalysedFunction {
2027                    name: "foo".to_string(),
2028                    parameters: vec![AnalysedFunctionParameter {
2029                        name: "param1".to_string(),
2030                        typ: variant(vec![
2031                            case("register-user", u64()),
2032                            case("process-user", str()),
2033                            unit_case("validate"),
2034                        ]),
2035                    }],
2036                    result: Some(AnalysedFunctionResult {
2037                        typ: AnalysedType::Handle(TypeHandle {
2038                            resource_id: AnalysedResourceId(0),
2039                            mode: AnalysedResourceMode::Owned,
2040                            name: None,
2041                            owner: None,
2042                        }),
2043                    }),
2044                }],
2045            });
2046
2047            let component_info = ComponentDependencyKey {
2048                component_name: "foo".to_string(),
2049                component_id: Uuid::new_v4(),
2050                root_package_name: None,
2051                root_package_version: None,
2052            };
2053
2054            vec![ComponentDependency {
2055                component_dependency_key: component_info,
2056                component_exports: vec![instance],
2057            }]
2058        }
2059
2060        pub(crate) fn metadata_with_resource_methods() -> Vec<ComponentDependency> {
2061            let instance = AnalysedExport::Instance(AnalysedInstance {
2062                name: "golem:it/api".to_string(),
2063                functions: vec![
2064                    AnalysedFunction {
2065                        name: "[constructor]cart".to_string(),
2066                        parameters: vec![AnalysedFunctionParameter {
2067                            name: "param1".to_string(),
2068                            typ: str(),
2069                        }],
2070                        result: Some(AnalysedFunctionResult {
2071                            typ: AnalysedType::Handle(TypeHandle {
2072                                resource_id: AnalysedResourceId(0),
2073                                mode: AnalysedResourceMode::Owned,
2074                                name: None,
2075                                owner: None,
2076                            }),
2077                        }),
2078                    },
2079                    AnalysedFunction {
2080                        name: "[method]cart.add-item".to_string(),
2081                        parameters: vec![
2082                            AnalysedFunctionParameter {
2083                                name: "self".to_string(),
2084                                typ: AnalysedType::Handle(TypeHandle {
2085                                    resource_id: AnalysedResourceId(0),
2086                                    mode: AnalysedResourceMode::Borrowed,
2087                                    name: None,
2088                                    owner: None,
2089                                }),
2090                            },
2091                            AnalysedFunctionParameter {
2092                                name: "item".to_string(),
2093                                typ: record(vec![field("name", str())]),
2094                            },
2095                        ],
2096                        result: None,
2097                    },
2098                ],
2099            });
2100
2101            let component_info = ComponentDependencyKey {
2102                component_name: "foo".to_string(),
2103                component_id: Uuid::new_v4(),
2104                root_package_name: None,
2105                root_package_version: None,
2106            };
2107
2108            vec![ComponentDependency {
2109                component_dependency_key: component_info,
2110                component_exports: vec![instance],
2111            }]
2112        }
2113        pub(crate) fn get_component_metadata(
2114            function_name: &str,
2115            input_types: Vec<AnalysedType>,
2116            output: AnalysedType,
2117        ) -> Vec<ComponentDependency> {
2118            let analysed_function_parameters = input_types
2119                .into_iter()
2120                .enumerate()
2121                .map(|(index, typ)| AnalysedFunctionParameter {
2122                    name: format!("param{index}"),
2123                    typ,
2124                })
2125                .collect();
2126
2127            let component_info = ComponentDependencyKey {
2128                component_name: "foo".to_string(),
2129                component_id: Uuid::new_v4(),
2130                root_package_name: None,
2131                root_package_version: None,
2132            };
2133
2134            vec![ComponentDependency {
2135                component_dependency_key: component_info,
2136                component_exports: vec![AnalysedExport::Function(AnalysedFunction {
2137                    name: function_name.to_string(),
2138                    parameters: analysed_function_parameters,
2139                    result: Some(AnalysedFunctionResult { typ: output }),
2140                })],
2141            }]
2142        }
2143
2144        pub(crate) fn rib_input_type_info(types: Vec<(&str, AnalysedType)>) -> RibInputTypeInfo {
2145            let mut type_info = HashMap::new();
2146            for (name, typ) in types {
2147                type_info.insert(name.to_string(), typ);
2148            }
2149            RibInputTypeInfo { types: type_info }
2150        }
2151    }
2152}