|
1 | 1 | use crate::PackageId; |
2 | 2 | use indexmap::IndexMap; |
3 | 3 | use petgraph::graph::NodeIndex; |
| 4 | +use std::borrow::Cow; |
4 | 5 | use std::collections::HashMap; |
5 | 6 | use wac_types::{ |
6 | 7 | CoreExtern, DefinedType, DefinedTypeId, Enum, Flags, FuncTypeId, InterfaceId, ItemKind, |
7 | 8 | ModuleTypeId, PrimitiveType, Record, ResourceId, Type, Types, UsedType, ValueType, Variant, |
8 | 9 | WorldId, |
9 | 10 | }; |
10 | 11 | use wasm_encoder::{ |
11 | | - Alias, ComponentBuilder, ComponentCoreTypeEncoder, ComponentExportKind, |
| 12 | + Alias, ComponentBuilder, ComponentCoreTypeEncoder, ComponentExportKind, ComponentExternName, |
12 | 13 | ComponentOuterAliasKind, ComponentType, ComponentTypeEncoder, ComponentTypeRef, |
13 | 14 | ComponentValType, EntityType, GlobalType, InstanceType, MemoryType, ModuleType, TableType, |
14 | 15 | TagKind, TagType, TypeBounds, |
15 | 16 | }; |
| 17 | +use wasmparser::names::{ComponentName, ComponentNameKind}; |
| 18 | + |
| 19 | +/// Determines whether an instance import/export needs a component model |
| 20 | +/// `(implements "…")` directive, and if so returns the interface name to put |
| 21 | +/// inside it. |
| 22 | +/// |
| 23 | +/// An instance extern is encoded one of two ways depending on its name: |
| 24 | +/// |
| 25 | +/// * Regular interface extern: the extern name *is* the interface name, e.g. |
| 26 | +/// `(import "foo:bar/iface" (instance …))`. Nothing to disambiguate, so this |
| 27 | +/// returns `None`. |
| 28 | +/// |
| 29 | +/// * `implements` extern: the extern name is a plain-name label and the |
| 30 | +/// interface it implements is carried separately, e.g. |
| 31 | +/// `(import "primary" (implements "foo:bar/iface") (instance …))`. This is |
| 32 | +/// how a component imports/exports the same interface more than once under |
| 33 | +/// different labels. Here this returns `Some("foo:bar/iface")`, i.e. the |
| 34 | +/// string the caller should encode as the `implements` directive. |
| 35 | +/// |
| 36 | +/// `name` is the extern name (`primary`) and `interface` identifies the |
| 37 | +/// instance's interface within `types`, whose own name (`foo:bar/iface`) is the |
| 38 | +/// returned value. |
| 39 | +pub(crate) fn implements_directive<'a>( |
| 40 | + types: &'a Types, |
| 41 | + name: &str, |
| 42 | + interface: InterfaceId, |
| 43 | +) -> Option<&'a str> { |
| 44 | + // Only a plain-name label is encoded with an `implements` directive; a |
| 45 | + // regular interface extern already names its interface directly. |
| 46 | + let is_label = matches!( |
| 47 | + ComponentName::new(name, 0).as_ref().map(|n| n.kind()), |
| 48 | + Ok(ComponentNameKind::Label(_)) |
| 49 | + ); |
| 50 | + if !is_label { |
| 51 | + return None; |
| 52 | + } |
| 53 | + types[interface].id.as_deref() |
| 54 | +} |
16 | 55 |
|
17 | 56 | /// A type used to abstract the API differences between a component builder, |
18 | 57 | /// component type, and instance type from `wasm-encoder`. |
@@ -65,7 +104,8 @@ impl Encodable { |
65 | 104 | } |
66 | 105 | } |
67 | 106 |
|
68 | | - fn import_type(&mut self, name: &str, ty: ComponentTypeRef) { |
| 107 | + fn import_type<'a>(&mut self, name: impl Into<ComponentExternName<'a>>, ty: ComponentTypeRef) { |
| 108 | + let name = name.into(); |
69 | 109 | match self { |
70 | 110 | Encodable::Component(t) => { |
71 | 111 | t.import(name, ty); |
@@ -730,13 +770,22 @@ impl<'a> TypeEncoder<'a> { |
730 | 770 | } |
731 | 771 | ItemKind::Instance(id) => { |
732 | 772 | let import_index = state.current.encodable.instance_count(); |
| 773 | + let implements = implements_directive(self.0, name, id); |
| 774 | + let extern_name = ComponentExternName { |
| 775 | + name: Cow::Borrowed(name), |
| 776 | + implements: implements.map(Cow::Borrowed), |
| 777 | + }; |
733 | 778 | state |
734 | 779 | .current |
735 | 780 | .encodable |
736 | | - .import_type(name, ComponentTypeRef::Instance(index)); |
737 | | - if let Some(iid) = &self.0[id].id { |
738 | | - log::debug!("instance index {import_index} ({iid}) is available for aliasing"); |
739 | | - state.current.instances.insert(iid.clone(), import_index); |
| 781 | + .import_type(extern_name, ComponentTypeRef::Instance(index)); |
| 782 | + if implements.is_none() { |
| 783 | + if let Some(iid) = &self.0[id].id { |
| 784 | + log::debug!( |
| 785 | + "instance index {import_index} ({iid}) is available for aliasing" |
| 786 | + ); |
| 787 | + state.current.instances.insert(iid.clone(), import_index); |
| 788 | + } |
740 | 789 | } |
741 | 790 | } |
742 | 791 | _ => panic!("expected only types, functions, and instance types"), |
@@ -827,9 +876,19 @@ impl<'a> TypeEncoder<'a> { |
827 | 876 |
|
828 | 877 | let ty = kind.ty(); |
829 | 878 | let index = self.ty(state, ty, Some(name)); |
| 879 | + // Preserve the `(implements "I")` directive when re-encoding an |
| 880 | + // instance export that uses a plain-name label. |
| 881 | + let implements = match kind { |
| 882 | + ItemKind::Instance(id) => implements_directive(self.0, name, id), |
| 883 | + _ => None, |
| 884 | + }; |
| 885 | + let extern_name = ComponentExternName { |
| 886 | + name: Cow::Borrowed(name), |
| 887 | + implements: implements.map(Cow::Borrowed), |
| 888 | + }; |
830 | 889 | let index = Self::export_type( |
831 | 890 | state, |
832 | | - name, |
| 891 | + extern_name, |
833 | 892 | match kind { |
834 | 893 | ItemKind::Type(_) => ComponentTypeRef::Type(TypeBounds::Eq(index)), |
835 | 894 | ItemKind::Func(_) => ComponentTypeRef::Func(index), |
@@ -880,7 +939,12 @@ impl<'a> TypeEncoder<'a> { |
880 | 939 | index |
881 | 940 | } |
882 | 941 |
|
883 | | - fn export_type(state: &mut State, name: &str, ty: ComponentTypeRef) -> u32 { |
| 942 | + fn export_type<'b>( |
| 943 | + state: &mut State, |
| 944 | + name: impl Into<ComponentExternName<'b>>, |
| 945 | + ty: ComponentTypeRef, |
| 946 | + ) -> u32 { |
| 947 | + let name = name.into(); |
884 | 948 | match &mut state.current.encodable { |
885 | 949 | Encodable::Component(t) => { |
886 | 950 | let index = t.type_count(); |
|
0 commit comments