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