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