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                        }),
730                    }));
731                }
732                _ => {
733                    return Err(RibByteCodeGenerationError::UnexpectedTypeError {
734                        expected: TypeHint::Flag(Some(flags.clone())),
735                        actual: inferred_type.get_type_hint(),
736                    });
737                }
738            },
739            Expr::Boolean { value, .. } => {
740                instructions.push(RibIR::PushLit(value.into_value_and_type()));
741            }
742            Expr::GetTag { expr, .. } => {
743                stack.push(ExprState::from_expr(expr.deref()));
744                stack.push(ExprState::from_ir(RibIR::GetTag));
745            }
746
747            Expr::Concat { exprs, .. } => {
748                for expr in exprs.iter().rev() {
749                    stack.push(ExprState::from_expr(expr));
750                }
751
752                instructions.push(RibIR::Concat(exprs.len()));
753            }
754
755            Expr::Not { expr, .. } => {
756                stack.push(ExprState::from_expr(expr.deref()));
757                instructions.push(RibIR::Negate);
758            }
759
760            Expr::Tuple {
761                exprs,
762                inferred_type,
763                ..
764            } => {
765                for expr in exprs.iter().rev() {
766                    stack.push(ExprState::from_expr(expr));
767                }
768                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
769                instructions.push(RibIR::PushTuple(analysed_type, exprs.len()));
770            }
771
772            Expr::ListComprehension {
773                iterated_variable,
774                iterable_expr,
775                yield_expr,
776                inferred_type,
777                ..
778            } => {
779                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
780                handle_list_comprehension(
781                    instruction_id,
782                    stack,
783                    iterable_expr,
784                    yield_expr,
785                    iterated_variable,
786                    &analysed_type,
787                )
788            }
789
790            range_expr @ Expr::Range {
791                range,
792                inferred_type,
793                ..
794            } => match inferred_type.internal_type() {
795                TypeInternal::Range { .. } => {
796                    let analysed_type = convert_to_analysed_type(range_expr, inferred_type)?;
797
798                    handle_range(range, stack, analysed_type, instructions);
799                }
800
801                _ => {
802                    return Err(RibByteCodeGenerationError::UnexpectedTypeError {
803                        expected: TypeHint::Range,
804                        actual: inferred_type.get_type_hint(),
805                    });
806                }
807            },
808
809            // Invoke is always handled by the CallType::Function branch
810            Expr::InvokeMethodLazy { .. } => {}
811
812            Expr::ListReduce {
813                reduce_variable,
814                iterated_variable,
815                iterable_expr,
816                init_value_expr,
817                yield_expr,
818                ..
819            } => handle_list_reduce(
820                instruction_id,
821                stack,
822                reduce_variable,
823                iterated_variable,
824                iterable_expr,
825                init_value_expr,
826                yield_expr,
827            ),
828        }
829
830        Ok(())
831    }
832
833    pub(crate) fn convert_to_analysed_type(
834        expr: &Expr,
835        inferred_type: &InferredType,
836    ) -> Result<AnalysedType, RibByteCodeGenerationError> {
837        AnalysedType::try_from(inferred_type).map_err(|error| {
838            RibByteCodeGenerationError::AnalysedTypeConversionError(format!(
839                "Invalid Rib {}. Error converting {} to AnalysedType: {}",
840                expr,
841                inferred_type.get_type_hint(),
842                error
843            ))
844        })
845    }
846
847    // We create a temporary stack of expressions that we pop one by one,
848    // while injecting some pre-defined IRs such as Jump in certain cases
849    // A stack is required on one side to maintain the order of expressions
850    // As soon a `Expr` becomes `Instruction` the instruction stack will be in order.
851    pub(crate) enum ExprState {
852        Expr(Expr),
853        Instruction(RibIR),
854    }
855
856    impl ExprState {
857        pub(crate) fn from_expr(expr: &Expr) -> Self {
858            ExprState::Expr(expr.clone())
859        }
860
861        pub(crate) fn from_ir(ir: RibIR) -> Self {
862            ExprState::Instruction(ir)
863        }
864    }
865
866    fn handle_range(
867        range: &Range,
868        stack: &mut Vec<ExprState>,
869        analysed_type: AnalysedType,
870        instructions: &mut Vec<RibIR>,
871    ) {
872        let from = range.from();
873        let to = range.to();
874        let inclusive = range.inclusive();
875
876        if let Some(from) = from {
877            stack.push(ExprState::from_expr(from));
878            instructions.push(RibIR::UpdateRecord("from".to_string()));
879        }
880
881        if let Some(to) = to {
882            stack.push(ExprState::from_expr(to));
883            instructions.push(RibIR::UpdateRecord("to".to_string()));
884        }
885
886        stack.push(ExprState::from_ir(RibIR::PushLit(ValueAndType::new(
887            Value::Bool(inclusive),
888            bool(),
889        ))));
890
891        instructions.push(RibIR::UpdateRecord("inclusive".to_string()));
892
893        instructions.push(RibIR::CreateAndPushRecord(analysed_type));
894    }
895
896    fn handle_list_comprehension(
897        instruction_id: &mut InstructionId,
898        stack: &mut Vec<ExprState>,
899        iterable_expr: &Expr,
900        yield_expr: &Expr,
901        variable_id: &VariableId,
902        sink_type: &AnalysedType,
903    ) {
904        stack.push(ExprState::from_expr(iterable_expr));
905
906        stack.push(ExprState::from_ir(RibIR::ToIterator));
907
908        stack.push(ExprState::from_ir(RibIR::CreateSink(sink_type.clone())));
909
910        let loop_start_label = instruction_id.increment_mut();
911
912        stack.push(ExprState::from_ir(RibIR::Label(loop_start_label.clone())));
913
914        let exit_label = instruction_id.increment_mut();
915
916        stack.push(ExprState::from_ir(RibIR::IsEmpty));
917
918        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(exit_label.clone())));
919
920        stack.push(ExprState::from_ir(RibIR::AdvanceIterator));
921
922        stack.push(ExprState::from_ir(RibIR::AssignVar(variable_id.clone())));
923
924        stack.push(ExprState::from_expr(yield_expr));
925
926        stack.push(ExprState::from_ir(RibIR::PushToSink));
927
928        stack.push(ExprState::from_ir(RibIR::Jump(loop_start_label)));
929
930        stack.push(ExprState::from_ir(RibIR::Label(exit_label)));
931
932        stack.push(ExprState::from_ir(RibIR::SinkToList))
933    }
934
935    fn handle_list_reduce(
936        instruction_id: &mut InstructionId,
937        stack: &mut Vec<ExprState>,
938        reduce_variable: &VariableId,
939        iterated_variable: &VariableId,
940        iterable_expr: &Expr,
941        initial_value_expr: &Expr,
942        yield_expr: &Expr,
943    ) {
944        stack.push(ExprState::from_expr(iterable_expr));
945
946        stack.push(ExprState::from_expr(initial_value_expr));
947
948        stack.push(ExprState::from_ir(RibIR::AssignVar(
949            reduce_variable.clone(),
950        )));
951
952        stack.push(ExprState::from_ir(RibIR::ToIterator));
953
954        let loop_start_label = instruction_id.increment_mut();
955
956        stack.push(ExprState::from_ir(RibIR::Label(loop_start_label.clone())));
957
958        let exit_label = instruction_id.increment_mut();
959
960        stack.push(ExprState::from_ir(RibIR::IsEmpty));
961
962        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(exit_label.clone())));
963
964        stack.push(ExprState::from_ir(RibIR::AdvanceIterator));
965
966        stack.push(ExprState::from_ir(RibIR::AssignVar(
967            iterated_variable.clone(),
968        )));
969
970        stack.push(ExprState::from_expr(yield_expr));
971
972        stack.push(ExprState::from_ir(RibIR::AssignVar(
973            reduce_variable.clone(),
974        )));
975
976        stack.push(ExprState::from_ir(RibIR::Jump(loop_start_label)));
977
978        stack.push(ExprState::from_ir(RibIR::Label(exit_label)));
979
980        stack.push(ExprState::from_ir(RibIR::LoadVar(reduce_variable.clone())))
981    }
982
983    fn handle_if_condition(
984        instruction_id: &mut InstructionId,
985        if_expr: &Expr,
986        then_expr: &Expr,
987        else_expr: &Expr,
988        stack: &mut Vec<ExprState>,
989    ) {
990        instruction_id.increment_mut();
991        let else_beginning_id = instruction_id.clone();
992        instruction_id.increment_mut();
993        let else_ending_id = instruction_id.clone();
994
995        stack.push(ExprState::from_expr(if_expr));
996
997        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(
998            else_beginning_id.clone(),
999        )));
1000
1001        stack.push(ExprState::from_expr(then_expr));
1002
1003        stack.push(ExprState::from_ir(RibIR::Jump(else_ending_id.clone())));
1004
1005        stack.push(ExprState::from_ir(RibIR::Label(else_beginning_id.clone())));
1006
1007        stack.push(ExprState::from_expr(else_expr));
1008
1009        stack.push(ExprState::from_ir(RibIR::Label(else_ending_id.clone())));
1010    }
1011}
1012
1013#[cfg(test)]
1014mod compiler_tests {
1015    use bigdecimal::BigDecimal;
1016    use test_r::test;
1017
1018    use super::*;
1019    use crate::{ArmPattern, InferredType, MatchArm, RibCompiler, VariableId};
1020    use golem_wasm_ast::analysis::analysed_type::{list, s32, str};
1021    use golem_wasm_ast::analysis::{AnalysedType, NameTypePair, TypeRecord, TypeStr};
1022    use golem_wasm_rpc::{IntoValueAndType, Value, ValueAndType};
1023
1024    #[test]
1025    fn test_instructions_for_literal() {
1026        let literal = Expr::literal("hello");
1027
1028        let compiler = RibCompiler::default();
1029
1030        let compiler_output = compiler.compile(literal).unwrap();
1031
1032        let instruction_set = vec![RibIR::PushLit("hello".into_value_and_type())];
1033
1034        let expected_instructions = RibByteCode {
1035            instructions: instruction_set,
1036        };
1037
1038        assert_eq!(compiler_output.byte_code, expected_instructions);
1039    }
1040
1041    #[test]
1042    fn test_instructions_for_identifier() {
1043        let inferred_input_type = InferredType::string();
1044
1045        let variable_id = VariableId::local("request", 0);
1046
1047        let expr = Expr::identifier_with_variable_id(variable_id.clone(), None)
1048            .with_inferred_type(inferred_input_type);
1049
1050        let compiler = RibCompiler::default();
1051
1052        let inferred_expr = compiler.infer_types(expr).unwrap();
1053
1054        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1055
1056        let instruction_set = vec![RibIR::LoadVar(variable_id)];
1057
1058        let expected_instructions = RibByteCode {
1059            instructions: instruction_set,
1060        };
1061
1062        assert_eq!(instructions, expected_instructions);
1063    }
1064
1065    #[test]
1066    fn test_instructions_assign_variable() {
1067        let literal = Expr::literal("hello");
1068
1069        let variable_id = VariableId::local("request", 0);
1070
1071        let expr = Expr::let_binding_with_variable_id(variable_id.clone(), literal, None);
1072
1073        let compiler = RibCompiler::default();
1074
1075        let inferred_expr = compiler.infer_types(expr).unwrap();
1076
1077        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1078
1079        let instruction_set = vec![
1080            RibIR::PushLit("hello".into_value_and_type()),
1081            RibIR::AssignVar(variable_id),
1082        ];
1083
1084        let expected_instructions = RibByteCode {
1085            instructions: instruction_set,
1086        };
1087
1088        assert_eq!(instructions, expected_instructions);
1089    }
1090
1091    #[test]
1092    fn test_instructions_equal_to() {
1093        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1094
1095        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1096
1097        let expr = Expr::equal_to(number_f32, number_u32);
1098
1099        let compiler = RibCompiler::default();
1100
1101        let inferred_expr = compiler.infer_types(expr).unwrap();
1102
1103        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1104
1105        let value_and_type1 = 1.0f32.into_value_and_type();
1106
1107        let value_and_type2 = 1u32.into_value_and_type();
1108
1109        let instruction_set = vec![
1110            RibIR::PushLit(value_and_type2),
1111            RibIR::PushLit(value_and_type1),
1112            RibIR::EqualTo,
1113        ];
1114
1115        let expected_instructions = RibByteCode {
1116            instructions: instruction_set,
1117        };
1118
1119        assert_eq!(instructions, expected_instructions);
1120    }
1121
1122    #[test]
1123    fn test_instructions_greater_than() {
1124        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1125
1126        let number_u32 = Expr::number_inferred(BigDecimal::from(2), None, InferredType::u32());
1127
1128        let expr = Expr::greater_than(number_f32, number_u32);
1129
1130        let compiler = RibCompiler::default();
1131
1132        let inferred_expr = compiler.infer_types(expr).unwrap();
1133
1134        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1135
1136        let value_and_type1 = 1.0f32.into_value_and_type();
1137
1138        let value_and_type2 = 2u32.into_value_and_type();
1139
1140        let instruction_set = vec![
1141            RibIR::PushLit(value_and_type2),
1142            RibIR::PushLit(value_and_type1),
1143            RibIR::GreaterThan,
1144        ];
1145
1146        let expected_instructions = RibByteCode {
1147            instructions: instruction_set,
1148        };
1149
1150        assert_eq!(instructions, expected_instructions);
1151    }
1152
1153    #[test]
1154    fn test_instructions_less_than() {
1155        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1156
1157        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1158
1159        let expr = Expr::less_than(number_f32, number_u32);
1160
1161        let compiler = RibCompiler::default();
1162
1163        let inferred_expr = compiler.infer_types(expr).unwrap();
1164
1165        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1166
1167        let value_and_type1 = 1.0f32.into_value_and_type();
1168
1169        let value_and_type2 = 1u32.into_value_and_type();
1170
1171        let instruction_set = vec![
1172            RibIR::PushLit(value_and_type2),
1173            RibIR::PushLit(value_and_type1),
1174            RibIR::LessThan,
1175        ];
1176
1177        let expected_instructions = RibByteCode {
1178            instructions: instruction_set,
1179        };
1180
1181        assert_eq!(instructions, expected_instructions);
1182    }
1183
1184    #[test]
1185    fn test_instructions_greater_than_or_equal_to() {
1186        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1187
1188        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1189
1190        let expr = Expr::greater_than_or_equal_to(number_f32, number_u32);
1191
1192        let compiler = RibCompiler::default();
1193
1194        let inferred_expr = compiler.infer_types(expr).unwrap();
1195
1196        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1197
1198        let value_and_type1 = 1.0f32.into_value_and_type();
1199
1200        let value_and_type2 = 1u32.into_value_and_type();
1201
1202        let instruction_set = vec![
1203            RibIR::PushLit(value_and_type2),
1204            RibIR::PushLit(value_and_type1),
1205            RibIR::GreaterThanOrEqualTo,
1206        ];
1207
1208        let expected_instructions = RibByteCode {
1209            instructions: instruction_set,
1210        };
1211
1212        assert_eq!(instructions, expected_instructions);
1213    }
1214
1215    #[test]
1216    fn test_instructions_less_than_or_equal_to() {
1217        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1218
1219        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1220
1221        let expr = Expr::less_than_or_equal_to(number_f32, number_u32);
1222
1223        let compiler = RibCompiler::default();
1224
1225        let inferred_expr = compiler.infer_types(expr).unwrap();
1226
1227        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1228
1229        let value_and_type1 = 1.0f32.into_value_and_type();
1230
1231        let value_and_type2 = 1u32.into_value_and_type();
1232
1233        let instruction_set = vec![
1234            RibIR::PushLit(value_and_type2),
1235            RibIR::PushLit(value_and_type1),
1236            RibIR::LessThanOrEqualTo,
1237        ];
1238
1239        let expected_instructions = RibByteCode {
1240            instructions: instruction_set,
1241        };
1242
1243        assert_eq!(instructions, expected_instructions);
1244    }
1245
1246    #[test]
1247    fn test_instructions_for_record() {
1248        let expr = Expr::record(vec![
1249            ("foo_key".to_string(), Expr::literal("foo_value")),
1250            ("bar_key".to_string(), Expr::literal("bar_value")),
1251        ])
1252        .with_inferred_type(InferredType::record(vec![
1253            (String::from("foo_key"), InferredType::string()),
1254            (String::from("bar_key"), InferredType::string()),
1255        ]));
1256
1257        let compiler = RibCompiler::default();
1258
1259        let inferred_expr = compiler.infer_types(expr).unwrap();
1260
1261        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1262
1263        let bar_value = "bar_value".into_value_and_type();
1264
1265        let foo_value = "foo_value".into_value_and_type();
1266
1267        let instruction_set = vec![
1268            RibIR::PushLit(bar_value),
1269            RibIR::PushLit(foo_value),
1270            RibIR::CreateAndPushRecord(AnalysedType::Record(TypeRecord {
1271                fields: vec![
1272                    NameTypePair {
1273                        name: "foo_key".to_string(),
1274                        typ: AnalysedType::Str(TypeStr),
1275                    },
1276                    NameTypePair {
1277                        name: "bar_key".to_string(),
1278                        typ: AnalysedType::Str(TypeStr),
1279                    },
1280                ],
1281            })),
1282            RibIR::UpdateRecord("foo_key".to_string()),
1283            RibIR::UpdateRecord("bar_key".to_string()),
1284        ];
1285
1286        let expected_instructions = RibByteCode {
1287            instructions: instruction_set,
1288        };
1289
1290        assert_eq!(instructions, expected_instructions);
1291    }
1292
1293    #[test]
1294    fn test_instructions_for_multiple() {
1295        let expr = Expr::expr_block(vec![Expr::literal("foo"), Expr::literal("bar")]);
1296
1297        let compiler = RibCompiler::default();
1298
1299        let inferred_expr = compiler.infer_types(expr).unwrap();
1300
1301        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1302
1303        let instruction_set = vec![
1304            RibIR::PushLit("foo".into_value_and_type()),
1305            RibIR::PushLit("bar".into_value_and_type()),
1306        ];
1307
1308        let expected_instructions = RibByteCode {
1309            instructions: instruction_set,
1310        };
1311
1312        assert_eq!(instructions, expected_instructions);
1313    }
1314
1315    #[test]
1316    fn test_instructions_if_conditional() {
1317        let if_expr = Expr::literal("pred").with_inferred_type(InferredType::bool());
1318
1319        let then_expr = Expr::literal("then");
1320
1321        let else_expr = Expr::literal("else");
1322
1323        let expr =
1324            Expr::cond(if_expr, then_expr, else_expr).with_inferred_type(InferredType::string());
1325
1326        let compiler = RibCompiler::default();
1327
1328        let inferred_expr = compiler.infer_types(expr).unwrap();
1329
1330        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1331
1332        let instruction_set = vec![
1333            RibIR::PushLit("pred".into_value_and_type()),
1334            RibIR::JumpIfFalse(InstructionId { index: 1 }), // jumps to the next label having Id 1 (which is else block)
1335            RibIR::PushLit("then".into_value_and_type()),
1336            RibIR::Jump(InstructionId { index: 2 }), // Once if is executed then jump to the end of the else block with id 2
1337            RibIR::Label(InstructionId { index: 1 }),
1338            RibIR::PushLit("else".into_value_and_type()),
1339            RibIR::Label(InstructionId { index: 2 }),
1340        ];
1341
1342        let expected_instructions = RibByteCode {
1343            instructions: instruction_set,
1344        };
1345
1346        assert_eq!(instructions, expected_instructions);
1347    }
1348
1349    #[test]
1350    fn test_instructions_for_nested_if_else() {
1351        let if_expr = Expr::literal("if-pred1").with_inferred_type(InferredType::bool());
1352
1353        let then_expr = Expr::literal("then1").with_inferred_type(InferredType::string());
1354
1355        let else_expr = Expr::cond(
1356            Expr::literal("else-pred2").with_inferred_type(InferredType::bool()),
1357            Expr::literal("else-then2"),
1358            Expr::literal("else-else2"),
1359        )
1360        .with_inferred_type(InferredType::string());
1361
1362        let expr =
1363            Expr::cond(if_expr, then_expr, else_expr).with_inferred_type(InferredType::string());
1364
1365        let compiler = RibCompiler::default();
1366
1367        let inferred_expr = compiler.infer_types(expr).unwrap();
1368
1369        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1370
1371        let instruction_set = vec![
1372            // if case
1373            RibIR::PushLit("if-pred1".into_value_and_type()),
1374            RibIR::JumpIfFalse(InstructionId { index: 1 }), // jumps to the next label having Id 1 (which is else block)
1375            RibIR::PushLit("then1".into_value_and_type()),
1376            RibIR::Jump(InstructionId { index: 2 }), // Once if is executed then jump to the end of the else block with id 3
1377            RibIR::Label(InstructionId { index: 1 }),
1378            RibIR::PushLit("else-pred2".into_value_and_type()),
1379            RibIR::JumpIfFalse(InstructionId { index: 3 }), // jumps to the next label having Id 2 (which is else block)
1380            RibIR::PushLit("else-then2".into_value_and_type()),
1381            RibIR::Jump(InstructionId { index: 4 }), // Once if is executed then jump to the end of the else block with id 3
1382            RibIR::Label(InstructionId { index: 3 }),
1383            RibIR::PushLit("else-else2".into_value_and_type()),
1384            RibIR::Label(InstructionId { index: 4 }),
1385            RibIR::Label(InstructionId { index: 2 }),
1386        ];
1387
1388        let expected_instructions = RibByteCode {
1389            instructions: instruction_set,
1390        };
1391
1392        assert_eq!(instructions, expected_instructions);
1393    }
1394
1395    #[test]
1396    fn test_instructions_for_select_field() {
1397        let record = Expr::record(vec![
1398            ("foo_key".to_string(), Expr::literal("foo_value")),
1399            ("bar_key".to_string(), Expr::literal("bar_value")),
1400        ])
1401        .with_inferred_type(InferredType::record(vec![
1402            (String::from("foo_key"), InferredType::string()),
1403            (String::from("bar_key"), InferredType::string()),
1404        ]));
1405
1406        let expr =
1407            Expr::select_field(record, "bar_key", None).with_inferred_type(InferredType::string());
1408
1409        let compiler = RibCompiler::default();
1410
1411        let inferred_expr = compiler.infer_types(expr).unwrap();
1412
1413        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1414
1415        let bar_value = "bar_value".into_value_and_type();
1416
1417        let foo_value = "foo_value".into_value_and_type();
1418
1419        let instruction_set = vec![
1420            RibIR::PushLit(bar_value),
1421            RibIR::PushLit(foo_value),
1422            RibIR::CreateAndPushRecord(AnalysedType::Record(TypeRecord {
1423                fields: vec![
1424                    NameTypePair {
1425                        name: "bar_key".to_string(),
1426                        typ: AnalysedType::Str(TypeStr),
1427                    },
1428                    NameTypePair {
1429                        name: "foo_key".to_string(),
1430                        typ: AnalysedType::Str(TypeStr),
1431                    },
1432                ],
1433            })),
1434            RibIR::UpdateRecord("foo_key".to_string()), // next pop is foo_value
1435            RibIR::UpdateRecord("bar_key".to_string()), // last pop is bar_value
1436            RibIR::SelectField("bar_key".to_string()),
1437        ];
1438
1439        let expected_instructions = RibByteCode {
1440            instructions: instruction_set,
1441        };
1442
1443        assert_eq!(instructions, expected_instructions);
1444    }
1445
1446    #[test]
1447    fn test_instructions_for_select_index() {
1448        let sequence = Expr::sequence(vec![Expr::literal("foo"), Expr::literal("bar")], None)
1449            .with_inferred_type(InferredType::list(InferredType::string()));
1450
1451        let expr = Expr::select_index(sequence, Expr::number(BigDecimal::from(1)))
1452            .with_inferred_type(InferredType::string());
1453
1454        let compiler = RibCompiler::default();
1455
1456        let inferred_expr = compiler.infer_types(expr).unwrap();
1457
1458        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1459
1460        let instruction_set = vec![
1461            RibIR::PushLit(ValueAndType::new(Value::S32(1), s32())),
1462            RibIR::PushLit("bar".into_value_and_type()),
1463            RibIR::PushLit("foo".into_value_and_type()),
1464            RibIR::PushList(list(str()), 2),
1465            RibIR::SelectIndexV1,
1466        ];
1467
1468        let expected_instructions = RibByteCode {
1469            instructions: instruction_set,
1470        };
1471
1472        assert_eq!(instructions, expected_instructions);
1473    }
1474
1475    #[test]
1476    fn test_instructions_for_expr_arm_pattern_match() {
1477        let expr = Expr::pattern_match(
1478            Expr::literal("pred"),
1479            vec![
1480                MatchArm::new(
1481                    ArmPattern::Literal(Box::new(Expr::literal("arm1_pattern_expr"))),
1482                    Expr::literal("arm1_resolution_expr"),
1483                ),
1484                MatchArm::new(
1485                    ArmPattern::Literal(Box::new(Expr::literal("arm2_pattern_expr"))),
1486                    Expr::literal("arm2_resolution_expr"),
1487                ),
1488                MatchArm::new(
1489                    ArmPattern::Literal(Box::new(Expr::literal("arm3_pattern_expr"))),
1490                    Expr::literal("arm3_resolution_expr"),
1491                ),
1492            ],
1493        )
1494        .with_inferred_type(InferredType::string());
1495
1496        let rib_compiler = RibCompiler::default();
1497
1498        let instructions = rib_compiler.compile(expr).unwrap().byte_code;
1499
1500        // instructions will correspond to an if-else statement
1501        let instruction_set = vec![
1502            RibIR::PushLit("arm1_pattern_expr".into_value_and_type()),
1503            RibIR::PushLit("pred".into_value_and_type()),
1504            RibIR::EqualTo,
1505            RibIR::JumpIfFalse(InstructionId { index: 1 }),
1506            RibIR::PushLit("arm1_resolution_expr".into_value_and_type()),
1507            RibIR::Jump(InstructionId { index: 2 }),
1508            RibIR::Label(InstructionId { index: 1 }),
1509            RibIR::PushLit("arm2_pattern_expr".into_value_and_type()),
1510            RibIR::PushLit("pred".into_value_and_type()),
1511            RibIR::EqualTo,
1512            RibIR::JumpIfFalse(InstructionId { index: 3 }),
1513            RibIR::PushLit("arm2_resolution_expr".into_value_and_type()),
1514            RibIR::Jump(InstructionId { index: 4 }),
1515            RibIR::Label(InstructionId { index: 3 }),
1516            RibIR::PushLit("arm3_pattern_expr".into_value_and_type()),
1517            RibIR::PushLit("pred".into_value_and_type()),
1518            RibIR::EqualTo,
1519            RibIR::JumpIfFalse(InstructionId { index: 5 }),
1520            RibIR::PushLit("arm3_resolution_expr".into_value_and_type()),
1521            RibIR::Jump(InstructionId { index: 6 }),
1522            RibIR::Label(InstructionId { index: 5 }),
1523            RibIR::Throw("No match found".to_string()),
1524            RibIR::Label(InstructionId { index: 6 }),
1525            RibIR::Label(InstructionId { index: 4 }),
1526            RibIR::Label(InstructionId { index: 2 }),
1527        ];
1528
1529        let expected_instructions = RibByteCode {
1530            instructions: instruction_set,
1531        };
1532
1533        assert_eq!(instructions, expected_instructions);
1534    }
1535
1536    #[cfg(test)]
1537    mod invalid_function_invoke_tests {
1538        use test_r::test;
1539
1540        use crate::compiler::byte_code::compiler_tests::internal;
1541        use crate::{Expr, RibCompiler, RibCompilerConfig};
1542        use golem_wasm_ast::analysis::{AnalysedType, TypeStr};
1543
1544        #[test]
1545        fn test_unknown_function() {
1546            let expr = r#"
1547               foo(request);
1548               "success"
1549            "#;
1550
1551            let expr = Expr::from_text(expr).unwrap();
1552            let compiler = RibCompiler::default();
1553
1554            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1555
1556            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");
1557        }
1558
1559        #[test]
1560        fn test_unknown_resource_method() {
1561            let metadata = internal::metadata_with_resource_methods();
1562            let expr = r#"
1563               let user_id = "user";
1564               golem:it/api.{cart(user_id).add-item}("apple");
1565               golem:it/api.{cart(user_id).foo}("apple");
1566                "success"
1567            "#;
1568
1569            let expr = Expr::from_text(expr).unwrap();
1570
1571            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1572
1573            let compiler = RibCompiler::new(compiler_config);
1574
1575            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1576            assert_eq!(
1577                compiler_error,
1578                "error in the following rib found at line 4, column 16\n`foo(\"apple\")`\ncause: invalid function call `foo`\nunknown function\n"
1579            );
1580        }
1581
1582        #[test]
1583        fn test_invalid_arg_size_function() {
1584            let metadata = internal::get_component_metadata(
1585                "foo",
1586                vec![AnalysedType::Str(TypeStr)],
1587                AnalysedType::Str(TypeStr),
1588            );
1589
1590            let expr = r#"
1591               let user_id = "user";
1592               let result = foo(user_id, user_id);
1593               result
1594            "#;
1595
1596            let expr = Expr::from_text(expr).unwrap();
1597
1598            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1599
1600            let compiler = RibCompiler::new(compiler_config);
1601
1602            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1603            assert_eq!(
1604                compiler_error,
1605                "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"
1606            );
1607        }
1608
1609        #[test]
1610        fn test_invalid_arg_size_resource_method() {
1611            let metadata = internal::metadata_with_resource_methods();
1612            let expr = r#"
1613               let user_id = "user";
1614               golem:it/api.{cart(user_id).add-item}("apple", "samsung");
1615                "success"
1616            "#;
1617
1618            let expr = Expr::from_text(expr).unwrap();
1619
1620            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1621
1622            let compiler = RibCompiler::new(compiler_config);
1623
1624            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1625            assert_eq!(
1626                compiler_error,
1627                "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"
1628            );
1629        }
1630
1631        #[test]
1632        fn test_invalid_arg_types_function() {
1633            let metadata = internal::get_component_metadata(
1634                "foo",
1635                vec![AnalysedType::Str(TypeStr)],
1636                AnalysedType::Str(TypeStr),
1637            );
1638
1639            let expr = r#"
1640               let result = foo(1u64);
1641               result
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 2, column 33\n`1: u64`\ncause: type mismatch. expected string, found u64\ninvalid argument to the function `foo`\n"
1654            );
1655        }
1656
1657        #[test]
1658        fn test_invalid_arg_types_resource_method() {
1659            let metadata = internal::metadata_with_resource_methods();
1660            let expr = r#"
1661               let user_id = "user";
1662               golem:it/api.{cart(user_id).add-item}("apple");
1663                "success"
1664            "#;
1665
1666            let expr = Expr::from_text(expr).unwrap();
1667
1668            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1669
1670            let compiler = RibCompiler::new(compiler_config);
1671
1672            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1673            assert_eq!(
1674                compiler_error,
1675                "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"
1676            );
1677        }
1678
1679        #[test]
1680        fn test_invalid_arg_types_variants() {
1681            let metadata = internal::metadata_with_variants();
1682
1683            let expr = r#"
1684               let regiser_user_action = register-user("foo");
1685               let result = golem:it/api.{foo}(regiser_user_action);
1686               result
1687            "#;
1688
1689            let expr = Expr::from_text(expr).unwrap();
1690
1691            let compiler_config = RibCompilerConfig::new(metadata, vec![]);
1692
1693            let compiler = RibCompiler::new(compiler_config);
1694
1695            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1696            assert_eq!(
1697                compiler_error,
1698                "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"
1699            );
1700        }
1701    }
1702
1703    #[cfg(test)]
1704    mod global_input_tests {
1705        use test_r::test;
1706
1707        use crate::compiler::byte_code::compiler_tests::internal;
1708        use crate::{Expr, RibCompiler, RibCompilerConfig};
1709        use golem_wasm_ast::analysis::{
1710            AnalysedType, NameOptionTypePair, NameTypePair, TypeEnum, TypeList, TypeOption,
1711            TypeRecord, TypeResult, TypeStr, TypeTuple, TypeU32, TypeU64, TypeVariant,
1712        };
1713
1714        #[test]
1715        async fn test_str_global_input() {
1716            let request_value_type = AnalysedType::Str(TypeStr);
1717
1718            let output_analysed_type = AnalysedType::Str(TypeStr);
1719
1720            let analysed_exports = internal::get_component_metadata(
1721                "my-worker-function",
1722                vec![request_value_type.clone()],
1723                output_analysed_type,
1724            );
1725
1726            let expr = r#"
1727               let x = request;
1728               let worker = instance();
1729               worker.my-worker-function(x);
1730               match x {
1731                "foo"  => "success",
1732                 _ => "fallback"
1733               }
1734            "#;
1735
1736            let expr = Expr::from_text(expr).unwrap();
1737            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1738            let compiled = compiler.compile(expr).unwrap();
1739            let expected_type_info =
1740                internal::rib_input_type_info(vec![("request", request_value_type)]);
1741
1742            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1743        }
1744
1745        #[test]
1746        async fn test_number_global_input() {
1747            let request_value_type = AnalysedType::U32(TypeU32);
1748
1749            let output_analysed_type = AnalysedType::Str(TypeStr);
1750
1751            let analysed_exports = internal::get_component_metadata(
1752                "my-worker-function",
1753                vec![request_value_type.clone()],
1754                output_analysed_type,
1755            );
1756
1757            let expr = r#"
1758               let x = request;
1759               let worker = instance();
1760               worker.my-worker-function(x);
1761               match x {
1762                1  => "success",
1763                0 => "failure"
1764               }
1765            "#;
1766
1767            let expr = Expr::from_text(expr).unwrap();
1768            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1769            let compiled = compiler.compile(expr).unwrap();
1770            let expected_type_info =
1771                internal::rib_input_type_info(vec![("request", request_value_type)]);
1772
1773            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1774        }
1775
1776        #[test]
1777        async fn test_variant_type_info() {
1778            let request_value_type = AnalysedType::Variant(TypeVariant {
1779                cases: vec![
1780                    NameOptionTypePair {
1781                        name: "register-user".to_string(),
1782                        typ: Some(AnalysedType::U64(TypeU64)),
1783                    },
1784                    NameOptionTypePair {
1785                        name: "process-user".to_string(),
1786                        typ: Some(AnalysedType::Str(TypeStr)),
1787                    },
1788                    NameOptionTypePair {
1789                        name: "validate".to_string(),
1790                        typ: None,
1791                    },
1792                ],
1793            });
1794
1795            let output_analysed_type = AnalysedType::Str(TypeStr);
1796
1797            let analysed_exports = internal::get_component_metadata(
1798                "my-worker-function",
1799                vec![request_value_type.clone()],
1800                output_analysed_type,
1801            );
1802
1803            // x = request, implies we are expecting a global variable
1804            // called request as the  input to Rib.
1805            // my-worker-function is a function that takes a Variant as input,
1806            // implies the type of request is a Variant.
1807            // This means the rib interpreter env has to have a request variable in it,
1808            // with a value that should be of the type Variant
1809            let expr = r#"
1810               let worker = instance();
1811               worker.my-worker-function(request);
1812               match request {
1813                 process-user(user) => user,
1814                 _ => "default"
1815               }
1816            "#;
1817
1818            let expr = Expr::from_text(expr).unwrap();
1819            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1820            let compiled = compiler.compile(expr).unwrap();
1821            let expected_type_info =
1822                internal::rib_input_type_info(vec![("request", request_value_type)]);
1823
1824            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1825        }
1826
1827        #[test]
1828        async fn test_result_type_info() {
1829            let request_value_type = AnalysedType::Result(TypeResult {
1830                ok: Some(Box::new(AnalysedType::U64(TypeU64))),
1831                err: Some(Box::new(AnalysedType::Str(TypeStr))),
1832            });
1833
1834            let output_analysed_type = AnalysedType::Str(TypeStr);
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 Result 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 Result
1848            let expr = r#"
1849               let worker = instance();
1850               worker.my-worker-function(request);
1851               match request {
1852                 ok(x) => "${x}",
1853                 err(msg) => msg
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_option_type_info() {
1868            let request_value_type = AnalysedType::Option(TypeOption {
1869                inner: Box::new(AnalysedType::Str(TypeStr)),
1870            });
1871
1872            let output_analysed_type = AnalysedType::Str(TypeStr);
1873
1874            let analysed_exports = internal::get_component_metadata(
1875                "my-worker-function",
1876                vec![request_value_type.clone()],
1877                output_analysed_type,
1878            );
1879
1880            // x = request, implies we are expecting a global variable
1881            // called request as the input to Rib.
1882            // my-worker-function is a function that takes a Option as input,
1883            // implies the type of request is a Result.
1884            // This means the rib interpreter env has to have a request variable in it,
1885            // with a value that should be of the type Option
1886            let expr = r#"
1887               let worker = instance();
1888               worker.my-worker-function(request);
1889               match request {
1890                 some(x) => x,
1891                 none => "error"
1892               }
1893            "#;
1894
1895            let expr = Expr::from_text(expr).unwrap();
1896            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1897            let compiled = compiler.compile(expr).unwrap();
1898            let expected_type_info =
1899                internal::rib_input_type_info(vec![("request", request_value_type)]);
1900
1901            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1902        }
1903
1904        #[test]
1905        async fn test_enum_type_info() {
1906            let request_value_type = AnalysedType::Enum(TypeEnum {
1907                cases: vec!["prod".to_string(), "dev".to_string(), "test".to_string()],
1908            });
1909
1910            let output_analysed_type = AnalysedType::Str(TypeStr);
1911
1912            let analysed_exports = internal::get_component_metadata(
1913                "my-worker-function",
1914                vec![request_value_type.clone()],
1915                output_analysed_type,
1916            );
1917
1918            // x = request, implies we are expecting a global variable
1919            // called request as the input to Rib.
1920            // my-worker-function is a function that takes a Option as input,
1921            // implies the type of request is a Result.
1922            // This means the rib interpreter env has to have a request variable in it,
1923            // with a value that should be of the type Option
1924            let expr = r#"
1925               let worker = instance();
1926               worker.my-worker-function(request);
1927               match request {
1928                 prod  => "p",
1929                 dev => "d",
1930                 test => "t"
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_record_global_input() {
1945            let request_value_type = AnalysedType::Record(TypeRecord {
1946                fields: vec![NameTypePair {
1947                    name: "path".to_string(),
1948                    typ: AnalysedType::Record(TypeRecord {
1949                        fields: vec![NameTypePair {
1950                            name: "user".to_string(),
1951                            typ: AnalysedType::Str(TypeStr),
1952                        }],
1953                    }),
1954                }],
1955            });
1956
1957            let output_analysed_type = AnalysedType::Str(TypeStr);
1958
1959            let analysed_exports = internal::get_component_metadata(
1960                "my-worker-function",
1961                vec![request_value_type.clone()],
1962                output_analysed_type,
1963            );
1964
1965            // x = request, implies we are expecting a global variable
1966            // called request as the  input to Rib.
1967            // my-worker-function is a function that takes a Record of path -> user -> str as input
1968            // implies the type of request is a Record.
1969            // This means the rib interpreter env has to have a request variable in it,
1970            // with a value that should be of the type Record
1971            let expr = r#"
1972               let x = request;
1973               let worker = instance();
1974               worker.my-worker-function(x);
1975
1976               let name = x.path.user;
1977
1978               match x {
1979                 { path : { user : some_name } } => some_name,
1980                 _ => name
1981               }
1982            "#;
1983
1984            let expr = Expr::from_text(expr).unwrap();
1985            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
1986            let compiled = compiler.compile(expr).unwrap();
1987            let expected_type_info =
1988                internal::rib_input_type_info(vec![("request", request_value_type)]);
1989
1990            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1991        }
1992
1993        #[test]
1994        async fn test_tuple_global_input() {
1995            let request_value_type = AnalysedType::Tuple(TypeTuple {
1996                items: vec![
1997                    AnalysedType::Str(TypeStr),
1998                    AnalysedType::U32(TypeU32),
1999                    AnalysedType::Record(TypeRecord {
2000                        fields: vec![NameTypePair {
2001                            name: "user".to_string(),
2002                            typ: AnalysedType::Str(TypeStr),
2003                        }],
2004                    }),
2005                ],
2006            });
2007
2008            let output_analysed_type = AnalysedType::Str(TypeStr);
2009
2010            let analysed_exports = internal::get_component_metadata(
2011                "my-worker-function",
2012                vec![request_value_type.clone()],
2013                output_analysed_type,
2014            );
2015
2016            // x = request, implies we are expecting a global variable
2017            // called request as the  input to Rib.
2018            // my-worker-function is a function that takes a Tuple,
2019            // implies the type of request is a Tuple.
2020            let expr = r#"
2021               let x = request;
2022               let worker = instance();
2023               worker.my-worker-function(x);
2024               match x {
2025                (_, _, record) =>  record.user,
2026                 _ => "fallback"
2027               }
2028            "#;
2029
2030            let expr = Expr::from_text(expr).unwrap();
2031            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
2032            let compiled = compiler.compile(expr).unwrap();
2033            let expected_type_info =
2034                internal::rib_input_type_info(vec![("request", request_value_type)]);
2035
2036            assert_eq!(compiled.rib_input_type_info, expected_type_info);
2037        }
2038
2039        #[test]
2040        async fn test_list_global_input() {
2041            let request_value_type = AnalysedType::List(TypeList {
2042                inner: Box::new(AnalysedType::Str(TypeStr)),
2043            });
2044
2045            let output_analysed_type = AnalysedType::Str(TypeStr);
2046
2047            let analysed_exports = internal::get_component_metadata(
2048                "my-worker-function",
2049                vec![request_value_type.clone()],
2050                output_analysed_type,
2051            );
2052
2053            // x = request, implies we are expecting a global variable
2054            // called request as the  input to Rib.
2055            // my-worker-function is a function that takes a List,
2056            // implies the type of request should be a List
2057            let expr = r#"
2058               let x = request;
2059               let worker = instance();
2060               worker.my-worker-function(x);
2061               match x {
2062               [a, b, c]  => a,
2063                 _ => "fallback"
2064               }
2065            "#;
2066
2067            let expr = Expr::from_text(expr).unwrap();
2068            let compiler = RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![]));
2069            let compiled = compiler.compile(expr).unwrap();
2070            let expected_type_info =
2071                internal::rib_input_type_info(vec![("request", request_value_type)]);
2072
2073            assert_eq!(compiled.rib_input_type_info, expected_type_info);
2074        }
2075    }
2076
2077    mod internal {
2078        use crate::{ComponentDependency, ComponentDependencyKey, RibInputTypeInfo};
2079        use golem_wasm_ast::analysis::*;
2080        use std::collections::HashMap;
2081        use uuid::Uuid;
2082
2083        pub(crate) fn metadata_with_variants() -> Vec<ComponentDependency> {
2084            let instance = AnalysedExport::Instance(AnalysedInstance {
2085                name: "golem:it/api".to_string(),
2086                functions: vec![AnalysedFunction {
2087                    name: "foo".to_string(),
2088                    parameters: vec![AnalysedFunctionParameter {
2089                        name: "param1".to_string(),
2090                        typ: AnalysedType::Variant(TypeVariant {
2091                            cases: vec![
2092                                NameOptionTypePair {
2093                                    name: "register-user".to_string(),
2094                                    typ: Some(AnalysedType::U64(TypeU64)),
2095                                },
2096                                NameOptionTypePair {
2097                                    name: "process-user".to_string(),
2098                                    typ: Some(AnalysedType::Str(TypeStr)),
2099                                },
2100                                NameOptionTypePair {
2101                                    name: "validate".to_string(),
2102                                    typ: None,
2103                                },
2104                            ],
2105                        }),
2106                    }],
2107                    result: Some(AnalysedFunctionResult {
2108                        typ: AnalysedType::Handle(TypeHandle {
2109                            resource_id: AnalysedResourceId(0),
2110                            mode: AnalysedResourceMode::Owned,
2111                        }),
2112                    }),
2113                }],
2114            });
2115
2116            let component_info = ComponentDependencyKey {
2117                component_name: "foo".to_string(),
2118                component_id: Uuid::new_v4(),
2119                root_package_name: None,
2120                root_package_version: None,
2121            };
2122
2123            vec![ComponentDependency {
2124                component_dependency_key: component_info,
2125                component_exports: vec![instance],
2126            }]
2127        }
2128
2129        pub(crate) fn metadata_with_resource_methods() -> Vec<ComponentDependency> {
2130            let instance = AnalysedExport::Instance(AnalysedInstance {
2131                name: "golem:it/api".to_string(),
2132                functions: vec![
2133                    AnalysedFunction {
2134                        name: "[constructor]cart".to_string(),
2135                        parameters: vec![AnalysedFunctionParameter {
2136                            name: "param1".to_string(),
2137                            typ: AnalysedType::Str(TypeStr),
2138                        }],
2139                        result: Some(AnalysedFunctionResult {
2140                            typ: AnalysedType::Handle(TypeHandle {
2141                                resource_id: AnalysedResourceId(0),
2142                                mode: AnalysedResourceMode::Owned,
2143                            }),
2144                        }),
2145                    },
2146                    AnalysedFunction {
2147                        name: "[method]cart.add-item".to_string(),
2148                        parameters: vec![
2149                            AnalysedFunctionParameter {
2150                                name: "self".to_string(),
2151                                typ: AnalysedType::Handle(TypeHandle {
2152                                    resource_id: AnalysedResourceId(0),
2153                                    mode: AnalysedResourceMode::Borrowed,
2154                                }),
2155                            },
2156                            AnalysedFunctionParameter {
2157                                name: "item".to_string(),
2158                                typ: AnalysedType::Record(TypeRecord {
2159                                    fields: vec![NameTypePair {
2160                                        name: "name".to_string(),
2161                                        typ: AnalysedType::Str(TypeStr),
2162                                    }],
2163                                }),
2164                            },
2165                        ],
2166                        result: None,
2167                    },
2168                ],
2169            });
2170
2171            let component_info = ComponentDependencyKey {
2172                component_name: "foo".to_string(),
2173                component_id: Uuid::new_v4(),
2174                root_package_name: None,
2175                root_package_version: None,
2176            };
2177
2178            vec![ComponentDependency {
2179                component_dependency_key: component_info,
2180                component_exports: vec![instance],
2181            }]
2182        }
2183        pub(crate) fn get_component_metadata(
2184            function_name: &str,
2185            input_types: Vec<AnalysedType>,
2186            output: AnalysedType,
2187        ) -> Vec<ComponentDependency> {
2188            let analysed_function_parameters = input_types
2189                .into_iter()
2190                .enumerate()
2191                .map(|(index, typ)| AnalysedFunctionParameter {
2192                    name: format!("param{index}"),
2193                    typ,
2194                })
2195                .collect();
2196
2197            let component_info = ComponentDependencyKey {
2198                component_name: "foo".to_string(),
2199                component_id: Uuid::new_v4(),
2200                root_package_name: None,
2201                root_package_version: None,
2202            };
2203
2204            vec![ComponentDependency {
2205                component_dependency_key: component_info,
2206                component_exports: vec![AnalysedExport::Function(AnalysedFunction {
2207                    name: function_name.to_string(),
2208                    parameters: analysed_function_parameters,
2209                    result: Some(AnalysedFunctionResult { typ: output }),
2210                })],
2211            }]
2212        }
2213
2214        pub(crate) fn rib_input_type_info(types: Vec<(&str, AnalysedType)>) -> RibInputTypeInfo {
2215            let mut type_info = HashMap::new();
2216            for (name, typ) in types {
2217                type_info.insert(name.to_string(), typ);
2218            }
2219            RibInputTypeInfo { types: type_info }
2220        }
2221    }
2222}