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