rib/compiler/
byte_code.rs

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