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