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