1mod model;
16pub use model::*;
17
18#[cfg(feature = "protobuf")]
20pub mod protobuf;
21
22#[cfg(feature = "wave")]
26pub mod wave;
27#[cfg(feature = "wit-parser")]
28pub mod wit_parser;
29
30use crate::component::*;
31use crate::core::Mem;
32use crate::AstCustomization;
33use mappable_rc::Mrc;
34use std::cell::RefCell;
35use std::collections::HashMap;
36use std::fmt::Debug;
37use std::rc::Rc;
38
39pub type AnalysisResult<A> = Result<A, AnalysisFailure>;
40
41#[derive(Debug, Clone)]
42struct ComponentStackItem<Ast: AstCustomization + 'static> {
43 component: Mrc<Component<Ast>>,
44 component_idx: Option<ComponentIdx>,
45}
46
47type ResourceIdMap = HashMap<(Vec<ComponentIdx>, ComponentTypeIdx), AnalysedResourceId>;
48
49#[derive(Debug, Clone)]
50pub struct AnalysisContext<Ast: AstCustomization + 'static> {
51 component_stack: Vec<ComponentStackItem<Ast>>,
52 warnings: Rc<RefCell<Vec<AnalysisWarning>>>,
53 resource_ids: Rc<RefCell<ResourceIdMap>>,
54}
55
56impl<Ast: AstCustomization + 'static> AnalysisContext<Ast> {
57 pub fn new(component: Component<Ast>) -> AnalysisContext<Ast> {
58 AnalysisContext::from_rc(Mrc::new(component))
59 }
60
61 pub fn from_rc(component: Mrc<Component<Ast>>) -> AnalysisContext<Ast> {
63 AnalysisContext {
64 component_stack: vec![ComponentStackItem {
65 component,
66 component_idx: None,
67 }],
68 warnings: Rc::new(RefCell::new(Vec::new())),
69 resource_ids: Rc::new(RefCell::new(HashMap::new())),
70 }
71 }
72
73 pub fn get_top_level_exports(&self) -> AnalysisResult<Vec<AnalysedExport>> {
76 let component = self.get_component();
77 let mut result = Vec::new();
78 for export in component.exports() {
79 match export.kind {
80 ComponentExternalKind::Func => {
81 let export = self.analyse_func_export(export.name.as_string(), export.idx)?;
82 result.push(AnalysedExport::Function(export));
83 }
84 ComponentExternalKind::Instance => {
85 let instance =
86 self.analyse_instance_export(export.name.as_string(), export.idx)?;
87 result.push(AnalysedExport::Instance(instance));
88 }
89 _ => self.warning(AnalysisWarning::UnsupportedExport(
90 UnsupportedExportWarning {
91 kind: export.kind.clone(),
92 name: export.name.as_string(),
93 },
94 )),
95 }
96 }
97
98 Ok(result)
99 }
100
101 pub fn get_all_memories(&self) -> AnalysisResult<Vec<Mem>> {
103 let mut result = Vec::new();
104
105 let mut component_stack = vec![self.get_component()];
106 while let Some(component) = component_stack.pop() {
107 for module in component.modules() {
108 for mem in module.mems() {
109 result.push((*mem).clone());
110 }
111 }
112 for inner_component in component.components() {
113 component_stack.push(inner_component.clone());
114 }
115 }
116 Ok(result)
117 }
118
119 pub fn warnings(&self) -> Vec<AnalysisWarning> {
120 self.warnings.borrow().clone()
121 }
122
123 fn warning(&self, warning: AnalysisWarning) {
124 self.warnings.borrow_mut().push(warning);
125 }
126
127 fn get_resource_id(&self, type_idx: &ComponentTypeIdx) -> AnalysedResourceId {
128 let new_unique_id = self.resource_ids.borrow().len() as u64;
129 let mut resource_ids = self.resource_ids.borrow_mut();
130 let path = self
131 .component_stack
132 .iter()
133 .filter_map(|item| item.component_idx)
134 .collect();
135 let key = (path, *type_idx);
136 resource_ids
137 .entry(key)
138 .or_insert_with(|| {
139 AnalysedResourceId(new_unique_id) })
141 .clone()
142 }
143
144 fn analyse_func_export(
145 &self,
146 name: String,
147 idx: ComponentFuncIdx,
148 ) -> AnalysisResult<AnalysedFunction> {
149 let (function_section, next_ctx) = self
150 .get_final_referenced(format!("component function {idx}"), |component| {
151 component.get_component_func(idx)
152 })?;
153 let (func_type_section, next_ctx) = match &*function_section {
154 ComponentSection::Canon(Canon::Lift { function_type, .. }) => next_ctx
155 .get_final_referenced(
156 format!("component function type {function_type}"),
157 |component| component.get_component_type(*function_type),
158 ),
159 ComponentSection::Import(ComponentImport {
160 desc: ComponentTypeRef::Func(func_type_idx),
161 ..
162 }) => next_ctx.get_final_referenced(
163 format!("component function type {func_type_idx}"),
164 |component| component.get_component_type(*func_type_idx),
165 ),
166 ComponentSection::Import(ComponentImport { desc, .. }) => Err(AnalysisFailure::failed(
167 format!("Expected function import, but got {:?} instead", desc),
168 )),
169 _ => Err(AnalysisFailure::failed(format!(
170 "Expected canonical lift function or function import, but got {} instead",
171 function_section.type_name()
172 ))),
173 }?;
174 match &*func_type_section {
175 ComponentSection::Type(ComponentType::Func(func_type)) => {
176 next_ctx.analyse_component_func_type(name, func_type)
177 }
178 _ => Err(AnalysisFailure::failed(format!(
179 "Expected function type, but got {} instead",
180 func_type_section.type_name()
181 ))),
182 }
183 }
184
185 fn analyse_component_func_type(
186 &self,
187 name: String,
188 func_type: &ComponentFuncType,
189 ) -> AnalysisResult<AnalysedFunction> {
190 let mut params: Vec<AnalysedFunctionParameter> = Vec::new();
191 for (param_name, param_type) in &func_type.params {
192 params.push(AnalysedFunctionParameter {
193 name: param_name.clone(),
194 typ: self.analyse_component_val_type(param_type)?,
195 })
196 }
197
198 let mut results: Vec<AnalysedFunctionResult> = Vec::new();
199 if let Some(tpe) = &func_type.result {
200 results.push(AnalysedFunctionResult {
201 name: None,
202 typ: self.analyse_component_val_type(tpe)?,
203 });
204 }
205
206 Ok(AnalysedFunction {
207 name,
208 parameters: params,
209 results,
210 })
211 }
212
213 fn analyse_component_type_idx(
214 &self,
215 component_type_idx: &ComponentTypeIdx,
216 analysed_resource_mode: Option<AnalysedResourceMode>,
217 ) -> AnalysisResult<AnalysedType> {
218 let (component_type_section, next_ctx) = self.get_final_referenced(
219 format!("component type {component_type_idx}"),
220 |component| component.get_component_type(*component_type_idx),
221 )?;
222 match &*component_type_section {
223 ComponentSection::Type(ComponentType::Defined(component_defined_type)) => {
224 next_ctx.analyse_component_defined_type(component_defined_type)
225 }
226 ComponentSection::Type(ComponentType::Func(_)) => Err(AnalysisFailure::failed(
227 "Passing functions in exported functions is not supported",
228 )),
229 ComponentSection::Type(ComponentType::Component(_)) => Err(AnalysisFailure::failed(
230 "Passing components in exported functions is not supported",
231 )),
232 ComponentSection::Type(ComponentType::Instance(_)) => Err(AnalysisFailure::failed(
233 "Passing instances in exported functions is not supported",
234 )),
235 ComponentSection::Type(ComponentType::Resource { .. }) => Err(AnalysisFailure::failed(
236 "Passing resources in exported functions is not supported",
237 )),
238 ComponentSection::Import(ComponentImport { desc, .. }) => match desc {
239 ComponentTypeRef::Type(TypeBounds::Eq(component_type_idx)) => {
240 self.analyse_component_type_idx(component_type_idx, analysed_resource_mode)
241 }
242 ComponentTypeRef::Type(TypeBounds::SubResource) => {
243 match analysed_resource_mode {
244 Some(resource_mode) => {
245 let id = next_ctx.get_resource_id(component_type_idx);
246 Ok(AnalysedType::Handle(TypeHandle {
247 resource_id: id,
248 mode: resource_mode,
249 }))
250 }
251 None => Err(AnalysisFailure::failed("Reached a sub-resource type bound without a surrounding borrowed/owned resource type")),
252 }
253 }
254 _ => Err(AnalysisFailure::failed(format!(
255 "Imports {desc:?} is not supported as a defined type"
256 ))),
257 },
258 _ => Err(AnalysisFailure::failed(format!(
259 "Expected component type, but got {} instead",
260 component_type_section.type_name()
261 ))),
262 }
263 }
264
265 fn analyse_component_val_type(&self, tpe: &ComponentValType) -> AnalysisResult<AnalysedType> {
266 match tpe {
267 ComponentValType::Primitive(primitive_value_type) => Ok(primitive_value_type.into()),
268 ComponentValType::Defined(component_type_idx) => {
269 self.analyse_component_type_idx(component_type_idx, None)
270 }
271 }
272 }
273
274 fn analyse_component_defined_type(
275 &self,
276 defined_type: &ComponentDefinedType,
277 ) -> AnalysisResult<AnalysedType> {
278 match defined_type {
279 ComponentDefinedType::Primitive { typ } => Ok(typ.into()),
280 ComponentDefinedType::Record { fields } => {
281 let mut result = Vec::new();
282 for (name, typ) in fields {
283 result.push(NameTypePair {
284 name: name.clone(),
285 typ: self.analyse_component_val_type(typ)?,
286 });
287 }
288 Ok(AnalysedType::Record(TypeRecord { fields: result }))
289 }
290 ComponentDefinedType::Variant { cases } => {
291 let mut result = Vec::new();
292 for case in cases {
293 result.push(NameOptionTypePair {
294 name: case.name.clone(),
295 typ: case
296 .typ
297 .as_ref()
298 .map(|t| self.analyse_component_val_type(t))
299 .transpose()?,
300 });
301 }
302 Ok(AnalysedType::Variant(TypeVariant { cases: result }))
303 }
304 ComponentDefinedType::List { elem } => Ok(AnalysedType::List(TypeList {
305 inner: Box::new(self.analyse_component_val_type(elem)?),
306 })),
307 ComponentDefinedType::Tuple { elems } => {
308 let mut result = Vec::new();
309 for elem in elems {
310 result.push(self.analyse_component_val_type(elem)?);
311 }
312 Ok(AnalysedType::Tuple(TypeTuple { items: result }))
313 }
314 ComponentDefinedType::Flags { names } => Ok(AnalysedType::Flags(TypeFlags {
315 names: names.clone(),
316 })),
317 ComponentDefinedType::Enum { names } => Ok(AnalysedType::Enum(TypeEnum {
318 cases: names.clone(),
319 })),
320 ComponentDefinedType::Option { typ } => Ok(AnalysedType::Option(TypeOption {
321 inner: Box::new(self.analyse_component_val_type(typ)?),
322 })),
323 ComponentDefinedType::Result { ok, err } => Ok(AnalysedType::Result(TypeResult {
324 ok: ok
325 .as_ref()
326 .map(|t| self.analyse_component_val_type(t).map(Box::new))
327 .transpose()?,
328 err: err
329 .as_ref()
330 .map(|t| self.analyse_component_val_type(t).map(Box::new))
331 .transpose()?,
332 })),
333 ComponentDefinedType::Owned { type_idx } => {
334 self.analyse_component_type_idx(type_idx, Some(AnalysedResourceMode::Owned))
335 }
336 ComponentDefinedType::Borrowed { type_idx } => {
337 self.analyse_component_type_idx(type_idx, Some(AnalysedResourceMode::Borrowed))
338 }
339 ComponentDefinedType::Future { .. } => Err(AnalysisFailure::failed(
340 "Future types are not supported yet",
341 )),
342 ComponentDefinedType::Stream { .. } => Err(AnalysisFailure::failed(
343 "Stream types are not supported yet",
344 )),
345 }
346 }
347
348 fn analyse_instance_export(
349 &self,
350 name: String,
351 idx: InstanceIdx,
352 ) -> AnalysisResult<AnalysedInstance> {
353 let (instance_section, next_ctx) = self
354 .get_final_referenced(format!("instance {idx}"), |component| {
355 component.get_instance_wrapped(idx)
356 })?;
357 match &*instance_section {
358 ComponentSection::Instance(instance) => match instance {
359 ComponentInstance::Instantiate { component_idx, .. } => {
360 let (component_section, next_ctx) = next_ctx.get_final_referenced(
361 format!("component {component_idx}"),
362 |component| component.get_component(*component_idx),
363 )?;
364
365 match &*component_section {
366 ComponentSection::Component(referenced_component) => {
367 let next_ctx = next_ctx.push_component(
368 Mrc::map(component_section.clone(), |c| c.as_component()),
369 *component_idx,
370 );
371 let mut funcs = Vec::new();
372 for export in referenced_component.exports() {
373 match export.kind {
374 ComponentExternalKind::Func => {
375 let func = next_ctx.analyse_func_export(
376 export.name.as_string(),
377 export.idx,
378 )?;
379 funcs.push(func);
380 }
381 _ => next_ctx.warning(AnalysisWarning::UnsupportedExport(
382 UnsupportedExportWarning {
383 kind: export.kind.clone(),
384 name: export.name.as_string(),
385 },
386 )),
387 }
388 }
389
390 Ok(AnalysedInstance {
391 name,
392 functions: funcs,
393 })
394 }
395 _ => Err(AnalysisFailure::failed(format!(
396 "Expected component, but got {} instead",
397 component_section.type_name()
398 ))),
399 }
400 }
401 ComponentInstance::FromExports { .. } => Err(AnalysisFailure::failed(
402 "Instance defined directly from exports are not supported",
403 )),
404 },
405 _ => Err(AnalysisFailure::failed(format!(
406 "Expected instance, but got {} instead",
407 instance_section.type_name()
408 ))),
409 }
410 }
411
412 fn get_component(&self) -> Mrc<Component<Ast>> {
413 self.component_stack.last().unwrap().component.clone()
414 }
415
416 fn get_components_from_stack(&self, count: u32) -> Vec<ComponentStackItem<Ast>> {
417 self.component_stack
418 .iter()
419 .skip(self.component_stack.len() - count as usize - 1)
420 .cloned()
421 .collect()
422 }
423
424 fn push_component(
425 &self,
426 component: Mrc<Component<Ast>>,
427 component_idx: ComponentIdx,
428 ) -> AnalysisContext<Ast> {
429 let mut component_stack = self.component_stack.clone();
430 component_stack.push(ComponentStackItem {
431 component,
432 component_idx: Some(component_idx),
433 });
434 self.with_component_stack(component_stack)
435 }
436
437 fn with_component_stack(
438 &self,
439 component_stack: Vec<ComponentStackItem<Ast>>,
440 ) -> AnalysisContext<Ast> {
441 AnalysisContext {
442 component_stack,
443 warnings: self.warnings.clone(),
444 resource_ids: self.resource_ids.clone(),
445 }
446 }
447
448 fn get_final_referenced<F>(
449 &self,
450 description: impl AsRef<str>,
451 f: F,
452 ) -> AnalysisResult<(Mrc<ComponentSection<Ast>>, AnalysisContext<Ast>)>
453 where
454 F: Fn(&Component<Ast>) -> Option<Mrc<ComponentSection<Ast>>>,
455 {
456 let component = self.get_component();
457 let direct_section = AnalysisFailure::fail_on_missing(f(&component), description)?;
458 self.follow_redirects(direct_section)
459 }
460
461 fn follow_redirects(
462 &self,
463 section: Mrc<ComponentSection<Ast>>,
464 ) -> AnalysisResult<(Mrc<ComponentSection<Ast>>, AnalysisContext<Ast>)> {
465 let component = self.get_component();
466 match &*section {
467 ComponentSection::Export(ComponentExport { kind, idx, .. }) => {
468 let next = match kind {
469 ComponentExternalKind::Module => AnalysisFailure::fail_on_missing(
470 component.get_module(*idx),
471 format!("module {idx}"),
472 )?,
473 ComponentExternalKind::Func => AnalysisFailure::fail_on_missing(
474 component.get_component_func(*idx),
475 format!("function {idx}"),
476 )?,
477 ComponentExternalKind::Value => AnalysisFailure::fail_on_missing(
478 component.get_value(*idx),
479 format!("value {idx}"),
480 )?,
481 ComponentExternalKind::Type => AnalysisFailure::fail_on_missing(
482 component.get_component_type(*idx),
483 format!("type {idx}"),
484 )?,
485 ComponentExternalKind::Instance => AnalysisFailure::fail_on_missing(
486 component.get_instance_wrapped(*idx),
487 format!("instance {idx}"),
488 )?,
489 ComponentExternalKind::Component => AnalysisFailure::fail_on_missing(
490 component.get_component(*idx),
491 format!("component {idx}"),
492 )?,
493 };
494 self.follow_redirects(next)
495 }
496 ComponentSection::Alias(Alias::InstanceExport {
497 instance_idx, name, ..
498 }) => {
499 let (instance_section, next_ctx) = self
500 .get_final_referenced(format!("instance {instance_idx}"), |component| {
501 component.get_instance_wrapped(*instance_idx)
502 })?;
503 next_ctx.find_instance_export(instance_section, name)
504 }
505 ComponentSection::Import(ComponentImport {
506 desc: ComponentTypeRef::Type(TypeBounds::Eq(idx)),
507 ..
508 }) => {
509 let maybe_tpe = component.get_component_type(*idx);
510 let tpe = AnalysisFailure::fail_on_missing(maybe_tpe, format!("type {idx}"))?;
511 self.follow_redirects(tpe)
512 }
513 ComponentSection::Alias(Alias::Outer {
514 kind,
515 target: AliasTarget { count, index },
516 }) => {
517 let referenced_components = self.get_components_from_stack(*count);
518 let referenced_component = referenced_components
519 .first()
520 .ok_or(AnalysisFailure::failed(format!(
521 "Component stack underflow (count={count}, size={}",
522 self.component_stack.len()
523 )))?
524 .component
525 .clone();
526 match kind {
527 OuterAliasKind::CoreModule => Err(AnalysisFailure::failed(
528 "Core module aliases are not supported",
529 )),
530 OuterAliasKind::CoreType => Err(AnalysisFailure::failed(
531 "Core type aliases are not supported",
532 )),
533 OuterAliasKind::Type => {
534 let maybe_tpe = referenced_component.get_component_type(*index);
535 let tpe =
536 AnalysisFailure::fail_on_missing(maybe_tpe, format!("type {index}"))?;
537 let next_ctx = self.with_component_stack(referenced_components);
538 next_ctx.follow_redirects(tpe)
539 }
540 OuterAliasKind::Component => {
541 let maybe_component = referenced_component.get_component(*index);
542 let component = AnalysisFailure::fail_on_missing(
543 maybe_component,
544 format!("component {index}"),
545 )?;
546 let next_ctx = self.with_component_stack(referenced_components);
547 next_ctx.follow_redirects(component)
548 }
549 }
550 }
551 _ => Ok((section, self.clone())),
553 }
554 }
555
556 fn find_instance_export(
557 &self,
558 instance_section: Mrc<ComponentSection<Ast>>,
559 name: &String,
560 ) -> AnalysisResult<(Mrc<ComponentSection<Ast>>, AnalysisContext<Ast>)> {
561 match &*instance_section {
562 ComponentSection::Instance(_) => {
563 let instance = Mrc::map(instance_section, |s| s.as_instance());
564 let (maybe_export, next_ctx) = self.find_export_by_name(&instance, name)?;
565 let export = AnalysisFailure::fail_on_missing(
566 maybe_export,
567 format!("missing aliased instance export {name} from instance"),
568 )?;
569 let wrapped = Mrc::new(ComponentSection::Export((*export).clone()));
570 next_ctx.follow_redirects(wrapped)
571 }
572 ComponentSection::Import(ComponentImport {
573 desc: ComponentTypeRef::Instance(type_idx),
574 ..
575 }) => {
576 let maybe_tpe = self.get_component().get_component_type(*type_idx);
577 let tpe = AnalysisFailure::fail_on_missing(maybe_tpe, format!("type {type_idx}"))?;
578 let (tpe, next_ctx) = self.follow_redirects(tpe)?;
579 next_ctx.find_instance_export(tpe, name)
580 }
581 ComponentSection::Type(ComponentType::Instance(decls)) => {
582 match decls.find_export(name) {
583 Some(decl) => {
584 match decl {
585 ComponentTypeRef::Module(type_idx) => {
586 let maybe_tpe = self.get_component().get_component_type(*type_idx);
587 let tpe = AnalysisFailure::fail_on_missing(
588 maybe_tpe,
589 format!("type {type_idx}"),
590 )?;
591 self.follow_redirects(tpe)
592 }
593 ComponentTypeRef::Func(type_idx) => {
594 let maybe_tpe = self.get_component().get_component_type(*type_idx);
595 let tpe = AnalysisFailure::fail_on_missing(
596 maybe_tpe,
597 format!("type {type_idx}"),
598 )?;
599 self.follow_redirects(tpe)
600 }
601 ComponentTypeRef::Val(_val_type) => {
602 todo!()
603 }
604 ComponentTypeRef::Type(type_bounds) => {
605 match type_bounds {
606 TypeBounds::Eq(component_type_idx) => {
607 let decl = decls.get_component_type(*component_type_idx);
608 let decl = AnalysisFailure::fail_on_missing(decl, format!("type {component_type_idx}"))?;
609
610 match decl {
611 InstanceTypeDeclaration::Core(_) => {
612 Err(AnalysisFailure::failed("Core type aliases are not supported"))
613 }
614 InstanceTypeDeclaration::Type(component_type) => {
615 Ok((Mrc::new(ComponentSection::Type(component_type.clone())), self.clone()))
616 }
617 InstanceTypeDeclaration::Alias(alias) => {
618 let component_idx = self.component_stack.last().expect("Component stack is empty").component_idx.unwrap_or_default();
619 let new_ctx = self.push_component(self.get_component(), component_idx);
620 new_ctx.follow_redirects(Mrc::new(ComponentSection::Alias(alias.clone())))
624 }
625 InstanceTypeDeclaration::Export { .. } => {
626 todo!()
627 }
628 }
629 }
630 TypeBounds::SubResource => {
631 Err(AnalysisFailure::failed("Reached a sub-resource type bound without a surrounding borrowed/owned resource type in find_instance_export"))
632 }
633 }
634 }
635 ComponentTypeRef::Instance(type_idx) => {
636 let maybe_tpe = self.get_component().get_component_type(*type_idx);
637 let tpe = AnalysisFailure::fail_on_missing(
638 maybe_tpe,
639 format!("type {type_idx}"),
640 )?;
641 self.follow_redirects(tpe)
642 }
643 ComponentTypeRef::Component(type_idx) => {
644 let maybe_tpe = self.get_component().get_component_type(*type_idx);
645 let tpe = AnalysisFailure::fail_on_missing(
646 maybe_tpe,
647 format!("type {type_idx}"),
648 )?;
649 self.follow_redirects(tpe)
650 }
651 }
652 }
653 None => Err(AnalysisFailure::failed(format!(
654 "Could not find exported element {name} in instance type declaration"
655 ))),
656 }
657 }
658 _ => Err(AnalysisFailure::failed(format!(
659 "Expected instance or imported instance, but got {} instead",
660 instance_section.type_name()
661 ))),
662 }
663 }
664
665 fn find_export_by_name(
666 &self,
667 instance: &ComponentInstance,
668 name: &String,
669 ) -> AnalysisResult<(Option<Mrc<ComponentExport>>, AnalysisContext<Ast>)> {
670 match instance {
671 ComponentInstance::Instantiate { component_idx, .. } => {
672 let (component, next_ctx) = self
673 .get_final_referenced(format!("component {component_idx}"), |component| {
674 component.get_component(*component_idx)
675 })?;
676 let component = Mrc::map(component, |c| c.as_component());
677 let export = component
678 .exports()
679 .iter()
680 .find(|export| export.name == *name)
681 .cloned();
682 Ok((export, next_ctx.push_component(component, *component_idx)))
683 }
684 ComponentInstance::FromExports { exports } => {
685 let export = exports.iter().find(|export| export.name == *name).cloned();
686 Ok((export.map(Mrc::new), self.clone()))
687 }
688 }
689 }
690}
691
692#[cfg(test)]
693mod tests {
694 use crate::analysis::analysed_type::{f32, field, handle, record, result, str, u32, u64};
695 use crate::analysis::{
696 AnalysedFunction, AnalysedFunctionParameter, AnalysedFunctionResult, AnalysedResourceId,
697 AnalysedResourceMode,
698 };
699 use test_r::test;
700
701 #[test]
702 fn analysed_function_kind() {
703 let cons = AnalysedFunction {
704 name: "[constructor]cart".to_string(),
705 parameters: vec![AnalysedFunctionParameter {
706 name: "user-id".to_string(),
707 typ: str(),
708 }],
709 results: vec![AnalysedFunctionResult {
710 name: None,
711 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Owned),
712 }],
713 };
714 let method = AnalysedFunction {
715 name: "[method]cart.add-item".to_string(),
716 parameters: vec![
717 AnalysedFunctionParameter {
718 name: "self".to_string(),
719 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Borrowed),
720 },
721 AnalysedFunctionParameter {
722 name: "item".to_string(),
723 typ: record(vec![
724 field("product-id", str()),
725 field("name", str()),
726 field("price", f32()),
727 field("quantity", u32()),
728 ]),
729 },
730 ],
731 results: vec![],
732 };
733 let static_method = AnalysedFunction {
734 name: "[static]cart.merge".to_string(),
735 parameters: vec![
736 AnalysedFunctionParameter {
737 name: "self".to_string(),
738 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Borrowed),
739 },
740 AnalysedFunctionParameter {
741 name: "that".to_string(),
742 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Borrowed),
743 },
744 ],
745 results: vec![AnalysedFunctionResult {
746 name: None,
747 typ: handle(AnalysedResourceId(0), AnalysedResourceMode::Owned),
748 }],
749 };
750 let fun = AnalysedFunction {
751 name: "hash".to_string(),
752 parameters: vec![AnalysedFunctionParameter {
753 name: "path".to_string(),
754 typ: str(),
755 }],
756 results: vec![AnalysedFunctionResult {
757 name: None,
758 typ: result(
759 record(vec![field("lower", u64()), field("upper", u64())]),
760 str(),
761 ),
762 }],
763 };
764
765 assert!(cons.is_constructor());
766 assert!(method.is_method());
767 assert!(static_method.is_static_method());
768 assert!(!fun.is_constructor());
769 assert!(!fun.is_method());
770 assert!(!fun.is_static_method());
771 }
772}