From fc9389d9321317cfe74b6e5ccbdd1709e291134f Mon Sep 17 00:00:00 2001
From: Ivan Enderlin <ivan.enderlin@hoa-project.net>
Date: Wed, 18 Sep 2019 18:02:05 +0200
Subject: [PATCH] feat(interface-types) Encode `Interfaces` to WAT.

---
 lib/interface-types/src/encoders/wat.rs | 160 +++++++++++++++++++++++-
 lib/interface-types/src/lib.rs          |  46 ++++++-
 2 files changed, 204 insertions(+), 2 deletions(-)

diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs
index 41f73b473..e7e02a3fe 100644
--- a/lib/interface-types/src/encoders/wat.rs
+++ b/lib/interface-types/src/encoders/wat.rs
@@ -1,4 +1,6 @@
-use crate::ast::{Adapter, Export, Forward, ImportedFunction, Instruction, InterfaceType, Type};
+use crate::ast::{
+    Adapter, Export, Forward, ImportedFunction, Instruction, InterfaceType, Interfaces, Type,
+};
 
 impl From<&InterfaceType> for String {
     fn from(interface_type: &InterfaceType) -> Self {
@@ -186,6 +188,83 @@ impl<'input> From<&Forward<'input>> for String {
     }
 }
 
+impl<'input> From<&Interfaces<'input>> for String {
+    fn from(interfaces: &Interfaces) -> Self {
+        let mut output = String::from(";; Interfaces");
+
+        let exports = interfaces
+            .exports
+            .iter()
+            .fold(String::new(), |mut accumulator, export| {
+                accumulator.push_str(&format!("\n\n;; Interface, Export {}\n", export.name));
+                accumulator.push_str(&String::from(export));
+                accumulator
+            });
+
+        let types = interfaces
+            .types
+            .iter()
+            .fold(String::new(), |mut accumulator, ty| {
+                accumulator.push_str(&format!("\n\n;; Interface, Ty {}\n", ty.name));
+                accumulator.push_str(&String::from(ty));
+                accumulator
+            });
+
+        let imported_functions = interfaces.imported_functions.iter().fold(
+            String::new(),
+            |mut accumulator, imported_function| {
+                accumulator.push_str(&format!(
+                    "\n\n;; Interface, Imported function {}.{}\n",
+                    imported_function.namespace, imported_function.name
+                ));
+                accumulator.push_str(&String::from(imported_function));
+                accumulator
+            },
+        );
+
+        let adapters =
+            interfaces
+                .adapters
+                .iter()
+                .fold(String::new(), |mut accumulator, adapter| {
+                    match adapter {
+                        Adapter::Import {
+                            namespace, name, ..
+                        } => accumulator.push_str(&format!(
+                            "\n\n;; Interface, Adapter {}.{}\n",
+                            namespace, name
+                        )),
+
+                        Adapter::Export { name, .. } => {
+                            accumulator.push_str(&format!("\n\n;; Interface, Adapter {}\n", name))
+                        }
+
+                        _ => unimplemented!(),
+                    }
+                    accumulator.push_str(&String::from(adapter));
+                    accumulator
+                });
+
+        let forwards =
+            interfaces
+                .forwards
+                .iter()
+                .fold(String::new(), |mut accumulator, forward| {
+                    accumulator.push_str(&format!("\n\n;; Interface, Forward {}\n", forward.name));
+                    accumulator.push_str(&String::from(forward));
+                    accumulator
+                });
+
+        output.push_str(&exports);
+        output.push_str(&types);
+        output.push_str(&imported_functions);
+        output.push_str(&adapters);
+        output.push_str(&forwards);
+
+        output
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use crate::ast::*;
@@ -443,4 +522,83 @@ mod tests {
 
         assert_eq!(input, output);
     }
+
+    #[test]
+    fn test_interfaces() {
+        let input: String = (&Interfaces {
+            exports: vec![
+                Export {
+                    name: "foo",
+                    input_types: vec![InterfaceType::I32],
+                    output_types: vec![],
+                },
+                Export {
+                    name: "bar",
+                    input_types: vec![],
+                    output_types: vec![],
+                },
+            ],
+            types: vec![],
+            imported_functions: vec![
+                ImportedFunction {
+                    namespace: "ns",
+                    name: "foo",
+                    input_types: vec![],
+                    output_types: vec![InterfaceType::I32],
+                },
+                ImportedFunction {
+                    namespace: "ns",
+                    name: "bar",
+                    input_types: vec![],
+                    output_types: vec![],
+                },
+            ],
+            adapters: vec![
+                Adapter::Import {
+                    namespace: "ns",
+                    name: "foo",
+                    input_types: vec![InterfaceType::I32],
+                    output_types: vec![],
+                    instructions: vec![Instruction::ArgumentGet(42)],
+                },
+                Adapter::Export {
+                    name: "bar",
+                    input_types: vec![],
+                    output_types: vec![],
+                    instructions: vec![Instruction::ArgumentGet(42)],
+                },
+            ],
+            forwards: vec![Forward { name: "main" }],
+        })
+            .into();
+        let output = r#";; Interfaces
+
+;; Interface, Export foo
+(@interface export "foo"
+  (param i32))
+
+;; Interface, Export bar
+(@interface export "bar")
+
+;; Interface, Imported function ns.foo
+(@interface func $ns_foo (import "ns" "foo")
+  (result i32))
+
+;; Interface, Imported function ns.bar
+(@interface func $ns_bar (import "ns" "bar"))
+
+;; Interface, Adapter ns.foo
+(@interface adapt (import "ns" "foo")
+  (param i32)
+  arg.get 42)
+
+;; Interface, Adapter bar
+(@interface adapt (export "bar")
+  arg.get 42)
+
+;; Interface, Forward main
+(@interface forward (export "main"))"#;
+
+        assert_eq!(input, output);
+    }
 }
diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs
index f6c4656db..d9fc90ea6 100644
--- a/lib/interface-types/src/lib.rs
+++ b/lib/interface-types/src/lib.rs
@@ -8,7 +8,7 @@ pub use decoders::binary::parse as parse_binary;
 
 #[cfg(test)]
 mod tests {
-    use crate::{ast::*, parse_binary};
+    use crate::{ast::*, encoders::wat::*, parse_binary};
     use std::fs;
     use wasmer_clif_backend::CraneliftCompiler;
     use wasmer_runtime_core as runtime;
@@ -103,6 +103,50 @@ mod tests {
                         forwards: vec![Forward { name: "main" }]
                     }
                 );
+
+                let wat = String::from(&interfaces);
+
+                assert_eq!(
+                    wat,
+                    r#";; Interfaces
+
+;; Interface, Export strlen
+(@interface export "strlen"
+  (param i32)
+  (result i32))
+
+;; Interface, Export write_null_byte
+(@interface export "write_null_byte"
+  (param i32 i32)
+  (result i32))
+
+;; Interface, Imported function host.console_log
+(@interface func $host_console_log (import "host" "console_log")
+  (param String))
+
+;; Interface, Imported function host.document_title
+(@interface func $host_document_title (import "host" "document_title")
+  (result String))
+
+;; Interface, Adapter host.console_log
+(@interface adapt (import "host" "console_log")
+  (param i32)
+  arg.get 0
+  arg.get 0
+  call-export "strlen"
+  read-utf8
+  call 0)
+
+;; Interface, Adapter host.document_title
+(@interface adapt (import "host" "document_title")
+  (result i32)
+  call 1
+  write-utf8 "alloc"
+  call-export "write_null_byte")
+
+;; Interface, Forward main
+(@interface forward (export "main"))"#,
+                );
             }
 
             Err(_) => assert!(false),