Repository: dprint/dprint (language: rust)

Issue:
Include dprint file path location in the "your dprint is old" error message
This will help people diagnose this problem a little quicker in the future.


Discussion / hints:

Fix patch:
diff --git a/crates/core/src/plugins/process/communicator.rs b/crates/core/src/plugins/process/communicator.rs
index f2bc49afa..bdcd566bc 100644
--- a/crates/core/src/plugins/process/communicator.rs
+++ b/crates/core/src/plugins/process/communicator.rs
@@ -130,7 +130,7 @@ impl ProcessPluginCommunicator {
     let mut stdout_reader = MessageReader::new(child.stdout.take().unwrap());
     let mut stdin_writer = MessageWriter::new(child.stdin.take().unwrap());
 
-    let (mut stdout_reader, stdin_writer, schema_version) = tokio::task::spawn_blocking(move || {
+    let (mut stdout_reader, stdin_writer, schema_version) = crate::async_runtime::spawn_blocking(move || {
       let schema_version = get_plugin_schema_version(&mut stdout_reader, &mut stdin_writer)
         .context("Failed plugin schema verification. This may indicate you are using an old version of the dprint CLI or plugin and should upgrade")?;
       Ok::<_, anyhow::Error>((stdout_reader, stdin_writer, schema_version))
diff --git a/crates/dprint/src/plugins/implementations/wasm/compile.rs b/crates/dprint/src/plugins/implementations/wasm/compile.rs
index 22bd32cc0..453006329 100644
--- a/crates/dprint/src/plugins/implementations/wasm/compile.rs
+++ b/crates/dprint/src/plugins/implementations/wasm/compile.rs
@@ -2,10 +2,9 @@ use anyhow::bail;
 use anyhow::Result;
 
 use super::create_identity_import_object;
-use super::functions::WasmFunctions;
+use super::create_wasm_plugin_instance;
 use super::load_instance::load_instance;
 use super::load_instance::WasmModuleCreator;
-use super::InitializedWasmPluginInstance;
 use crate::plugins::CompilationResult;
 
 /// Compiles a Wasm module.
@@ -13,7 +12,7 @@ pub fn compile(wasm_bytes: &[u8]) -> Result<CompilationResult> {
   let wasm_module_creator = WasmModuleCreator::default();
   let module = wasm_module_creator.create_from_wasm_bytes(wasm_bytes)?;
 
-  let bytes = match module.inner.serialize() {
+  let bytes = match module.inner().serialize() {
     Ok(bytes) => bytes,
     Err(err) => bail!("Error serializing wasm module: {:#}", err),
   };
@@ -22,8 +21,7 @@ pub fn compile(wasm_bytes: &[u8]) -> Result<CompilationResult> {
   let mut store = wasmer::Store::default();
   let imports = create_identity_import_object(&mut store);
   let instance = load_instance(&mut store, &module, &imports)?;
-  let wasm_functions = WasmFunctions::new(store, instance)?;
-  let mut instance = InitializedWasmPluginInstance::new(wasm_functions)?;
+  let mut instance = create_wasm_plugin_instance(store, instance)?;
 
   Ok(CompilationResult {
     bytes: bytes.into(),
diff --git a/crates/dprint/src/plugins/implementations/wasm/functions.rs b/crates/dprint/src/plugins/implementations/wasm/functions.rs
deleted file mode 100644
index 077bffbae..000000000
--- a/crates/dprint/src/plugins/implementations/wasm/functions.rs
+++ /dev/null
@@ -1,202 +0,0 @@
-use anyhow::bail;
-use anyhow::Result;
-use wasmer::Engine;
-use wasmer::Instance;
-use wasmer::Memory;
-use wasmer::MemoryView;
-use wasmer::RuntimeError;
-use wasmer::Store;
-use wasmer::TypedFunction;
-use wasmer::WasmPtr;
-use wasmer::WasmTypeList;
-
-use dprint_core::plugins::wasm::PLUGIN_SYSTEM_SCHEMA_VERSION;
-
-use super::load_instance::WasmInstance;
-
-pub enum WasmFormatResult {
-  NoChange = 0,
-  Change = 1,
-  Error = 2,
-}
-
-impl From<u8> for WasmFormatResult {
-  fn from(orig: u8) -> Self {
-    match orig {
-      0 => WasmFormatResult::NoChange,
-      1 => WasmFormatResult::Change,
-      2 => WasmFormatResult::Error,
-      _ => unreachable!(),
-    }
-  }
-}
-
-pub struct WasmFunctions {
-  store: Store,
-  instance: Instance,
-  memory: Memory,
-  // keep this alive for the duration of the engine otherwise it
-  // could be cleaned up before the instance is dropped
-  _engine: Engine,
-}
-
-impl WasmFunctions {
-  pub fn new(mut store: Store, instance: WasmInstance) -> Result<Self> {
-    match get_plugin_schema_version(&mut store, &instance.inner) {
-      Ok(plugin_schema_version) => {
-        if plugin_schema_version != PLUGIN_SYSTEM_SCHEMA_VERSION {
-          bail!(
-            "Invalid schema version: {} -- Expected: {}. This may indicate you should upgrade your dprint CLI or plugin.",
-            plugin_schema_version,
-            PLUGIN_SYSTEM_SCHEMA_VERSION
-          );
-        }
-      }
-      Err(err) => {
-        bail!(
-          "Error determining plugin schema version. Are you sure this is a dprint plugin? {}",
-          err.to_string()
-        );
-      }
-    }
-    let memory = instance.inner.exports.get_memory("memory")?.clone();
-
-    Ok(WasmFunctions {
-      instance: instance.inner,
-      memory,
-      store,
-      _engine: instance.engine,
-    })
-  }
-
-  #[inline]
-  pub fn set_global_config(&mut self) -> Result<()> {
-    let set_global_config_func = self.get_export::<(), ()>("set_global_config")?;
-    wasm_runtime_error_to_err_box(set_global_config_func.call(&mut self.store))
-  }
-
-  #[inline]
-  pub fn set_plugin_config(&mut self) -> Result<()> {
-    let set_plugin_config_func = self.get_export::<(), ()>("set_plugin_config")?;
-    wasm_runtime_error_to_err_box(set_plugin_config_func.call(&mut self.store))
-  }
-
-  #[inline]
-  pub fn get_plugin_info(&mut self) -> Result<usize> {
-    let get_plugin_info_func = self.get_export::<(), u32>("get_plugin_info")?;
-    wasm_runtime_error_to_err_box(get_plugin_info_func.call(&mut self.store)).map(|value| value as usize)
-  }
-
-  #[inline]
-  pub fn get_license_text(&mut self) -> Result<usize> {
-    let get_license_text_func = self.get_export::<(), u32>("get_license_text")?;
-    wasm_runtime_error_to_err_box(get_license_text_func.call(&mut self.store)).map(|value| value as usize)
-  }
-
-  #[inline]
-  pub fn get_resolved_config(&mut self) -> Result<usize> {
-    let get_resolved_config_func = self.get_export::<(), u32>("get_resolved_config")?;
-    wasm_runtime_error_to_err_box(get_resolved_config_func.call(&mut self.store)).map(|value| value as usize)
-  }
-
-  #[inline]
-  pub fn get_config_diagnostics(&mut self) -> Result<usize> {
-    let get_config_diagnostics_func = self.get_export::<(), u32>("get_config_diagnostics")?;
-    wasm_runtime_error_to_err_box(get_config_diagnostics_func.call(&mut self.store)).map(|value| value as usize)
-  }
-
-  #[inline]
-  pub fn set_override_config(&mut self) -> Result<()> {
-    let set_override_config_func = self.get_export::<(), ()>("set_override_config")?;
-    wasm_runtime_error_to_err_box(set_override_config_func.call(&mut self.store))
-  }
-
-  #[inline]
-  pub fn set_file_path(&mut self) -> Result<()> {
-    let set_file_path_func = self.get_export::<(), ()>("set_file_path")?;
-    wasm_runtime_error_to_err_box(set_file_path_func.call(&mut self.store))
-  }
-
-  #[inline]
-  pub fn format(&mut self) -> Result<WasmFormatResult> {
-    let format_func = self.get_export::<(), u8>("format")?;
-    wasm_runtime_error_to_err_box(format_func.call(&mut self.store)).map(|value| value.into())
-  }
-
-  #[inline]
-  pub fn get_formatted_text(&mut self) -> Result<usize> {
-    let get_formatted_text_func = self.get_export::<(), u32>("get_formatted_text")?;
-    wasm_runtime_error_to_err_box(get_formatted_text_func.call(&mut self.store)).map(|value| value as usize)
-  }
-
-  #[inline]
-  pub fn get_error_text(&mut self) -> Result<usize> {
-    let get_error_text_func = self.get_export::<(), u32>("get_error_text")?;
-    wasm_runtime_error_to_err_box(get_error_text_func.call(&mut self.store)).map(|value| value as usize)
-  }
-
-  #[inline]
-  pub fn get_memory_view(&self) -> MemoryView {
-    self.memory.view(&self.store)
-  }
-
-  #[inline]
-  pub fn clear_shared_bytes(&mut self, capacity: usize) -> Result<()> {
-    let clear_shared_bytes_func = self.get_export::<u32, ()>("clear_shared_bytes")?;
-    wasm_runtime_error_to_err_box(clear_shared_bytes_func.call(&mut self.store, capacity as u32))
-  }
-
-  #[inline]
-  pub fn get_wasm_memory_buffer_size(&mut self) -> Result<usize> {
-    let get_wasm_memory_buffer_size_func = self.get_export::<(), u32>("get_wasm_memory_buffer_size")?;
-    wasm_runtime_error_to_err_box(get_wasm_memory_buffer_size_func.call(&mut self.store)).map(|value| value as usize)
-  }
-
-  #[inline]
-  pub fn get_wasm_memory_buffer_ptr(&mut self) -> Result<WasmPtr<u32>> {
-    let get_wasm_memory_buffer_func = self.get_export::<(), WasmPtr<u32>>("get_wasm_memory_buffer")?;
-    wasm_runtime_error_to_err_box(get_wasm_memory_buffer_func.call(&mut self.store))
-  }
-
-  #[inline]
-  pub fn set_buffer_with_shared_bytes(&mut self, offset: usize, length: usize) -> Result<()> {
-    let set_buffer_with_shared_bytes_func = self.get_export::<(u32, u32), ()>("set_buffer_with_shared_bytes")?;
-    wasm_runtime_error_to_err_box(set_buffer_with_shared_bytes_func.call(&mut self.store, offset as u32, length as u32))
-  }
-
-  #[inline]
-  pub fn add_to_shared_bytes_from_buffer(&mut self, length: usize) -> Result<()> {
-    let add_to_shared_bytes_from_buffer_func = self.get_export::<u32, ()>("add_to_shared_bytes_from_buffer")?;
-
-    wasm_runtime_error_to_err_box(add_to_shared_bytes_from_buffer_func.call(&mut self.store, length as u32))
-  }
-
-  fn get_export<Args, Rets>(&mut self, name: &str) -> Result<TypedFunction<Args, Rets>>
-  where
-    Args: WasmTypeList,
-    Rets: WasmTypeList,
-  {
-    match self.instance.exports.get_function(name) {
-      Ok(func) => match func.typed::<Args, Rets>(&self.store) {
-        Ok(native_func) => Ok(native_func),
-        Err(err) => bail!("Error creating function '{}'. Message: {:#}", name, err),
-      },
-      Err(err) => bail!("Could not find export in plugin with name '{}'. Message: {:#}", name, err),
-    }
-  }
-}
-
-fn get_plugin_schema_version(store: &mut Store, instance: &Instance) -> Result<u32> {
-  let plugin_schema_version_func = instance.exports.get_function("get_plugin_schema_version")?;
-  let plugin_schema_version_func = plugin_schema_version_func.typed::<(), u32>(store)?;
-  Ok(plugin_schema_version_func.call(store)?)
-}
-
-#[inline]
-fn wasm_runtime_error_to_err_box<T>(result: Result<T, RuntimeError>) -> Result<T> {
-  // need to do this because RuntimeError can't be sent between threads safely
-  match result {
-    Ok(value) => Ok(value),
-    Err(err) => Err(err.into()),
-  }
-}
diff --git a/crates/dprint/src/plugins/implementations/wasm/instance/mod.rs b/crates/dprint/src/plugins/implementations/wasm/instance/mod.rs
new file mode 100644
index 000000000..af68220a7
--- /dev/null
+++ b/crates/dprint/src/plugins/implementations/wasm/instance/mod.rs
@@ -0,0 +1,84 @@
+use std::path::Path;
+
+use anyhow::bail;
+use anyhow::Result;
+use dprint_core::configuration::ConfigKeyMap;
+use dprint_core::configuration::ConfigurationDiagnostic;
+use dprint_core::plugins::wasm::PLUGIN_SYSTEM_SCHEMA_VERSION;
+use dprint_core::plugins::FileMatchingInfo;
+use dprint_core::plugins::FormatResult;
+use dprint_core::plugins::PluginInfo;
+use v1::InitializedWasmPluginInstanceV1;
+use wasmer::Store;
+
+use crate::plugins::FormatConfig;
+
+use super::WasmInstance;
+
+mod v1;
+
+pub trait InitializedWasmPluginInstance {
+  fn plugin_info(&mut self) -> Result<PluginInfo>;
+  fn license_text(&mut self) -> Result<String>;
+  fn resolved_config(&mut self, config: &FormatConfig) -> Result<String>;
+  fn config_diagnostics(&mut self, config: &FormatConfig) -> Result<Vec<ConfigurationDiagnostic>>;
+  fn file_matching_info(&mut self, _config: &FormatConfig) -> Result<FileMatchingInfo>;
+  fn format_text(&mut self, file_path: &Path, file_bytes: &[u8], config: &FormatConfig, override_config: &ConfigKeyMap) -> FormatResult;
+}
+
+pub fn create_wasm_plugin_instance(store: Store, instance: WasmInstance) -> Result<Box<dyn InitializedWasmPluginInstance>> {
+  match instance.version() {
+    PluginSchemaVersion::V3 => Ok(Box::new(InitializedWasmPluginInstanceV1::new(store, instance)?)),
+  }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum PluginSchemaVersion {
+  V3,
+}
+
+pub fn get_current_plugin_schema_version(module: &wasmer::Module) -> Result<PluginSchemaVersion> {
+  fn from_exports(module: &wasmer::Module) -> Result<u32> {
+    for export in module.exports() {
+      let name = export.name();
+      if matches!(name, "get_plugin_schema_version") {
+        // not exactly correct, but practically ok because this has been returning v3 for many years
+        return Ok(3);
+      } else if let Some(version) = name.strip_prefix("dprint_plugin_version_") {
+        // this is what dprint will use in the future
+        if let Ok(version) = version.parse() {
+          return Ok(version);
+        }
+      }
+    }
+    bail!("Error determining plugin schema version. Are you sure this is a dprint plugin? If so, maybe try upgrading dprint.");
+  }
+
+  let plugin_schema_version = from_exports(module)?;
+  match plugin_schema_version {
+    PLUGIN_SYSTEM_SCHEMA_VERSION => Ok(PluginSchemaVersion::V3),
+    version if version > PLUGIN_SYSTEM_SCHEMA_VERSION => {
+      bail!(
+        "Invalid schema version: {} -- Expected: {}. Upgrade your dprint CLI ({}).",
+        plugin_schema_version,
+        PLUGIN_SYSTEM_SCHEMA_VERSION,
+        get_current_exe_display(),
+      );
+    }
+    plugin_schema_version => {
+      bail!(
+        "Invalid schema version: {} -- Expected: {}. This plugin is too old for your version of dprint ({}). Please update the plugin manually.",
+        plugin_schema_version,
+        PLUGIN_SYSTEM_SCHEMA_VERSION,
+        get_current_exe_display(),
+      );
+    }
+  }
+}
+
+fn get_current_exe_display() -> String {
+  std::env::current_exe()
+    .ok()
+    .map(|p| p.display().to_string())
+    .unwrap_or_else(|| "<unknown path>".to_string())
+}
diff --git a/crates/dprint/src/plugins/implementations/wasm/instance/v1.rs b/crates/dprint/src/plugins/implementations/wasm/instance/v1.rs
new file mode 100644
index 000000000..93eb50170
--- /dev/null
+++ b/crates/dprint/src/plugins/implementations/wasm/instance/v1.rs
@@ -0,0 +1,354 @@
+use std::path::Path;
+
+use anyhow::anyhow;
+use anyhow::bail;
+use anyhow::Result;
+use dprint_core::configuration::ConfigKeyMap;
+use dprint_core::configuration::ConfigurationDiagnostic;
+use dprint_core::configuration::GlobalConfiguration;
+use dprint_core::plugins::CriticalFormatError;
+use dprint_core::plugins::FileMatchingInfo;
+use dprint_core::plugins::FormatConfigId;
+use dprint_core::plugins::FormatResult;
+use dprint_core::plugins::PluginInfo;
+use dprint_core::plugins::SyncPluginInfo;
+use wasmer::Engine;
+use wasmer::Instance;
+use wasmer::Memory;
+use wasmer::MemoryView;
+use wasmer::Store;
+use wasmer::TypedFunction;
+use wasmer::WasmPtr;
+use wasmer::WasmTypeList;
+
+use crate::plugins::implementations::wasm::WasmInstance;
+use crate::plugins::FormatConfig;
+
+use super::InitializedWasmPluginInstance;
+
+enum WasmFormatResult {
+  NoChange,
+  Change,
+  Error,
+}
+
+pub struct InitializedWasmPluginInstanceV1 {
+  wasm_functions: WasmFunctions,
+  buffer_size: usize,
+  current_config_id: FormatConfigId,
+}
+
+impl InitializedWasmPluginInstanceV1 {
+  pub fn new(store: Store, instance: WasmInstance) -> Result<Self> {
+    let mut wasm_functions = WasmFunctions::new(store, instance)?;
+    let buffer_size = wasm_functions.get_wasm_memory_buffer_size()?;
+    Ok(Self {
+      wasm_functions,
+      buffer_size,
+      current_config_id: FormatConfigId::uninitialized(),
+    })
+  }
+
+  fn set_global_config(&mut self, global_config: &GlobalConfiguration) -> Result<()> {
+    let json = serde_json::to_string(global_config)?;
+    self.send_string(&json)?;
+    self.wasm_functions.set_global_config()?;
+    Ok(())
+  }
+
+  fn set_plugin_config(&mut self, plugin_config: &ConfigKeyMap) -> Result<()> {
+    let json = serde_json::to_string(plugin_config)?;
+    self.send_string(&json)?;
+    self.wasm_functions.set_plugin_config()?;
+    Ok(())
+  }
+
+  fn sync_plugin_info(&mut self) -> Result<SyncPluginInfo> {
+    let len = self.wasm_functions.get_plugin_info()?;
+    let json_text = self.receive_string(len)?;
+    Ok(serde_json::from_str(&json_text)?)
+  }
+
+  fn inner_format_text(&mut self, file_path: &Path, file_bytes: &[u8], override_config: &ConfigKeyMap) -> Result<FormatResult> {
+    // send override config if necessary
+    if !override_config.is_empty() {
+      self.send_string(&match serde_json::to_string(override_config) {
+        Ok(text) => text,
+        Err(err) => return Ok(Err(err.into())),
+      })?;
+      self.wasm_functions.set_override_config()?;
+    }
+
+    // send file path
+    self.send_string(&file_path.to_string_lossy())?;
+    self.wasm_functions.set_file_path()?;
+
+    // send file text and format
+    self.send_bytes(file_bytes)?;
+    let response_code = self.wasm_functions.format()?;
+
+    // handle the response
+    match response_code {
+      WasmFormatResult::NoChange => Ok(Ok(None)),
+      WasmFormatResult::Change => {
+        let len = self.wasm_functions.get_formatted_text()?;
+        let text_bytes = self.receive_bytes(len)?;
+        Ok(Ok(Some(text_bytes)))
+      }
+      WasmFormatResult::Error => {
+        let len = self.wasm_functions.get_error_text()?;
+        let text = self.receive_string(len)?;
+        Ok(Err(anyhow!("{}", text)))
+      }
+    }
+  }
+
+  fn ensure_config(&mut self, config: &FormatConfig) -> Result<()> {
+    if self.current_config_id != config.id {
+      // set this to uninitialized in case it errors below
+      self.current_config_id = FormatConfigId::uninitialized();
+      // update the plugin
+      self.set_global_config(&config.global)?;
+      self.set_plugin_config(&config.raw)?;
+      // now mark this as successfully set
+      self.current_config_id = config.id;
+    }
+    Ok(())
+  }
+
+  /* LOW LEVEL SENDING AND RECEIVING */
+
+  // These methods should panic when failing because that may indicate
+  // a major problem where the CLI is out of sync with the plugin.
+
+  fn send_string(&mut self, text: &str) -> Result<()> {
+    self.send_bytes(text.as_bytes())
+  }
+
+  fn send_bytes(&mut self, bytes: &[u8]) -> Result<()> {
+    let mut index = 0;
+    let len = bytes.len();
+    self.wasm_functions.clear_shared_bytes(len)?;
+    while index < len {
+      let write_count = std::cmp::min(len - index, self.buffer_size);
+      self.write_bytes_to_memory_buffer(&bytes[index..(index + write_count)])?;
+      self.wasm_functions.add_to_shared_bytes_from_buffer(write_count)?;
+      index += write_count;
+    }
+    Ok(())
+  }
+
+  fn write_bytes_to_memory_buffer(&mut self, bytes: &[u8]) -> Result<()> {
+    let wasm_buffer_pointer = self.wasm_functions.get_wasm_memory_buffer_ptr()?;
+    let memory_view = self.wasm_functions.get_memory_view();
+    memory_view.write(wasm_buffer_pointer.offset() as u64, bytes)?;
+    Ok(())
+  }
+
+  fn receive_string(&mut self, len: usize) -> Result<String> {
+    let bytes = self.receive_bytes(len)?;
+    Ok(String::from_utf8(bytes)?)
+  }
+
+  fn receive_bytes(&mut self, len: usize) -> Result<Vec<u8>> {
+    let mut index = 0;
+    let mut bytes: Vec<u8> = vec![0; len];
+    while index < len {
+      let read_count = std::cmp::min(len - index, self.buffer_size);
+      self.wasm_functions.set_buffer_with_shared_bytes(index, read_count)?;
+      self.read_bytes_from_memory_buffer(&mut bytes[index..(index + read_count)])?;
+      index += read_count;
+    }
+    Ok(bytes)
+  }
+
+  fn read_bytes_from_memory_buffer(&mut self, bytes: &mut [u8]) -> Result<()> {
+    let wasm_buffer_pointer = self.wasm_functions.get_wasm_memory_buffer_ptr()?;
+    let memory_view = self.wasm_functions.get_memory_view();
+    memory_view.read(wasm_buffer_pointer.offset() as u64, bytes)?;
+    Ok(())
+  }
+}
+
+impl InitializedWasmPluginInstance for InitializedWasmPluginInstanceV1 {
+  fn plugin_info(&mut self) -> Result<PluginInfo> {
+    self.sync_plugin_info().map(|i| i.info)
+  }
+
+  fn license_text(&mut self) -> Result<String> {
+    let len = self.wasm_functions.get_license_text()?;
+    self.receive_string(len)
+  }
+
+  fn resolved_config(&mut self, config: &FormatConfig) -> Result<String> {
+    self.ensure_config(config)?;
+    let len = self.wasm_functions.get_resolved_config()?;
+    self.receive_string(len)
+  }
+
+  fn config_diagnostics(&mut self, config: &FormatConfig) -> Result<Vec<ConfigurationDiagnostic>> {
+    self.ensure_config(config)?;
+    let len = self.wasm_functions.get_config_diagnostics()?;
+    let json_text = self.receive_string(len)?;
+    Ok(serde_json::from_str(&json_text)?)
+  }
+
+  fn file_matching_info(&mut self, _config: &FormatConfig) -> Result<FileMatchingInfo> {
+    self.sync_plugin_info().map(|i| i.file_matching)
+  }
+
+  fn format_text(&mut self, file_path: &Path, file_bytes: &[u8], config: &FormatConfig, override_config: &ConfigKeyMap) -> FormatResult {
+    self.ensure_config(config)?;
+    match self.inner_format_text(file_path, file_bytes, override_config) {
+      Ok(inner) => inner,
+      Err(err) => Err(CriticalFormatError(err).into()),
+    }
+  }
+}
+
+struct WasmFunctions {
+  store: Store,
+  instance: Instance,
+  memory: Memory,
+  // keep this alive for the duration of the engine otherwise it
+  // could be cleaned up before the instance is dropped
+  _engine: Engine,
+}
+
+impl WasmFunctions {
+  pub fn new(store: Store, instance: WasmInstance) -> Result<Self> {
+    let memory = instance.inner.exports.get_memory("memory")?.clone();
+
+    Ok(WasmFunctions {
+      instance: instance.inner,
+      memory,
+      store,
+      _engine: instance.engine,
+    })
+  }
+
+  #[inline]
+  pub fn set_global_config(&mut self) -> Result<()> {
+    let set_global_config_func = self.get_export::<(), ()>("set_global_config")?;
+    Ok(set_global_config_func.call(&mut self.store)?)
+  }
+
+  #[inline]
+  pub fn set_plugin_config(&mut self) -> Result<()> {
+    let set_plugin_config_func = self.get_export::<(), ()>("set_plugin_config")?;
+    Ok(set_plugin_config_func.call(&mut self.store)?)
+  }
+
+  #[inline]
+  pub fn get_plugin_info(&mut self) -> Result<usize> {
+    let get_plugin_info_func = self.get_export::<(), u32>("get_plugin_info")?;
+    Ok(get_plugin_info_func.call(&mut self.store).map(|value| value as usize)?)
+  }
+
+  #[inline]
+  pub fn get_license_text(&mut self) -> Result<usize> {
+    let get_license_text_func = self.get_export::<(), u32>("get_license_text")?;
+    Ok(get_license_text_func.call(&mut self.store).map(|value| value as usize)?)
+  }
+
+  #[inline]
+  pub fn get_resolved_config(&mut self) -> Result<usize> {
+    let get_resolved_config_func = self.get_export::<(), u32>("get_resolved_config")?;
+    Ok(get_resolved_config_func.call(&mut self.store).map(|value| value as usize)?)
+  }
+
+  #[inline]
+  pub fn get_config_diagnostics(&mut self) -> Result<usize> {
+    let get_config_diagnostics_func = self.get_export::<(), u32>("get_config_diagnostics")?;
+    Ok(get_config_diagnostics_func.call(&mut self.store).map(|value| value as usize)?)
+  }
+
+  #[inline]
+  pub fn set_override_config(&mut self) -> Result<()> {
+    let set_override_config_func = self.get_export::<(), ()>("set_override_config")?;
+    Ok(set_override_config_func.call(&mut self.store)?)
+  }
+
+  #[inline]
+  pub fn set_file_path(&mut self) -> Result<()> {
+    let set_file_path_func = self.get_export::<(), ()>("set_file_path")?;
+    Ok(set_file_path_func.call(&mut self.store)?)
+  }
+
+  #[inline]
+  pub fn format(&mut self) -> Result<WasmFormatResult> {
+    let format_func = self.get_export::<(), u8>("format")?;
+    Ok(format_func.call(&mut self.store).map(u8_to_format_result)?)
+  }
+
+  #[inline]
+  pub fn get_formatted_text(&mut self) -> Result<usize> {
+    let get_formatted_text_func = self.get_export::<(), u32>("get_formatted_text")?;
+    Ok(get_formatted_text_func.call(&mut self.store).map(|value| value as usize)?)
+  }
+
+  #[inline]
+  pub fn get_error_text(&mut self) -> Result<usize> {
+    let get_error_text_func = self.get_export::<(), u32>("get_error_text")?;
+    Ok(get_error_text_func.call(&mut self.store).map(|value| value as usize)?)
+  }
+
+  #[inline]
+  pub fn get_memory_view(&self) -> MemoryView {
+    self.memory.view(&self.store)
+  }
+
+  #[inline]
+  pub fn clear_shared_bytes(&mut self, capacity: usize) -> Result<()> {
+    let clear_shared_bytes_func = self.get_export::<u32, ()>("clear_shared_bytes")?;
+    Ok(clear_shared_bytes_func.call(&mut self.store, capacity as u32)?)
+  }
+
+  #[inline]
+  pub fn get_wasm_memory_buffer_size(&mut self) -> Result<usize> {
+    let get_wasm_memory_buffer_size_func = self.get_export::<(), u32>("get_wasm_memory_buffer_size")?;
+    Ok(get_wasm_memory_buffer_size_func.call(&mut self.store).map(|value| value as usize)?)
+  }
+
+  #[inline]
+  pub fn get_wasm_memory_buffer_ptr(&mut self) -> Result<WasmPtr<u32>> {
+    let get_wasm_memory_buffer_func = self.get_export::<(), WasmPtr<u32>>("get_wasm_memory_buffer")?;
+    Ok(get_wasm_memory_buffer_func.call(&mut self.store)?)
+  }
+
+  #[inline]
+  pub fn set_buffer_with_shared_bytes(&mut self, offset: usize, length: usize) -> Result<()> {
+    let set_buffer_with_shared_bytes_func = self.get_export::<(u32, u32), ()>("set_buffer_with_shared_bytes")?;
+    Ok(set_buffer_with_shared_bytes_func.call(&mut self.store, offset as u32, length as u32)?)
+  }
+
+  #[inline]
+  pub fn add_to_shared_bytes_from_buffer(&mut self, length: usize) -> Result<()> {
+    let add_to_shared_bytes_from_buffer_func = self.get_export::<u32, ()>("add_to_shared_bytes_from_buffer")?;
+
+    Ok(add_to_shared_bytes_from_buffer_func.call(&mut self.store, length as u32)?)
+  }
+
+  fn get_export<Args, Rets>(&mut self, name: &str) -> Result<TypedFunction<Args, Rets>>
+  where
+    Args: WasmTypeList,
+    Rets: WasmTypeList,
+  {
+    match self.instance.exports.get_function(name) {
+      Ok(func) => match func.typed::<Args, Rets>(&self.store) {
+        Ok(native_func) => Ok(native_func),
+        Err(err) => bail!("Error creating function '{}'. Message: {:#}", name, err),
+      },
+      Err(err) => bail!("Could not find export in plugin with name '{}'. Message: {:#}", name, err),
+    }
+  }
+}
+
+fn u8_to_format_result(orig: u8) -> WasmFormatResult {
+  match orig {
+    0 => WasmFormatResult::NoChange,
+    1 => WasmFormatResult::Change,
+    2 => WasmFormatResult::Error,
+    _ => unreachable!(),
+  }
+}
diff --git a/crates/dprint/src/plugins/implementations/wasm/load_instance.rs b/crates/dprint/src/plugins/implementations/wasm/load_instance.rs
index 3f259edd9..6384213e4 100644
--- a/crates/dprint/src/plugins/implementations/wasm/load_instance.rs
+++ b/crates/dprint/src/plugins/implementations/wasm/load_instance.rs
@@ -8,10 +8,20 @@ use wasmer::Instance;
 use wasmer::Module;
 use wasmer::Store;
 
+use super::instance::get_current_plugin_schema_version;
+use super::PluginSchemaVersion;
+
 #[derive(Clone)]
 pub struct WasmInstance {
   pub inner: wasmer::Instance,
   pub engine: wasmer::Engine,
+  version: PluginSchemaVersion,
+}
+
+impl WasmInstance {
+  pub fn version(&self) -> PluginSchemaVersion {
+    self.version
+  }
 }
 
 /// Loads a compiled wasm module from the specified bytes.
@@ -21,6 +31,7 @@ pub fn load_instance(store: &mut Store, module: &WasmModule, import_object: &Imp
     Ok(instance) => Ok(WasmInstance {
       inner: instance,
       engine: module.engine.clone(),
+      version: module.version,
     }),
     Err(err) => bail!("Error instantiating module: {:#}", err),
   }
@@ -28,8 +39,23 @@ pub fn load_instance(store: &mut Store, module: &WasmModule, import_object: &Imp
 
 #[derive(Clone)]
 pub struct WasmModule {
-  pub inner: wasmer::Module,
+  inner: wasmer::Module,
   engine: wasmer::Engine,
+  version: PluginSchemaVersion,
+}
+
+impl WasmModule {
+  pub fn new(module: wasmer::Module, engine: wasmer::Engine) -> Result<Self> {
+    Ok(Self {
+      version: get_current_plugin_schema_version(&module)?,
+      inner: module,
+      engine,
+    })
+  }
+
+  pub fn inner(&self) -> &wasmer::Module {
+    &self.inner
+  }
 }
 
 // https://github.com/wasmerio/wasmer/pull/3378#issuecomment-1327679422
@@ -49,20 +75,15 @@ impl Default for WasmModuleCreator {
 impl WasmModuleCreator {
   pub fn create_from_wasm_bytes(&self, wasm_bytes: &[u8]) -> Result<WasmModule> {
     let engine_ref = EngineRef::new(&self.engine);
-    Ok(WasmModule {
-      inner: Module::new(&engine_ref, wasm_bytes)?,
-      engine: self.engine.clone(),
-    })
+    let module = Module::new(&engine_ref, wasm_bytes)?;
+    WasmModule::new(module, self.engine.clone())
   }
 
   pub fn create_from_serialized(&self, compiled_module_bytes: &[u8]) -> Result<WasmModule> {
     unsafe {
       let engine_ref = EngineRef::new(&self.engine);
       match Module::deserialize(&engine_ref, compiled_module_bytes) {
-        Ok(module) => Ok(WasmModule {
-          inner: module,
-          engine: self.engine.clone(),
-        }),
+        Ok(module) => WasmModule::new(module, self.engine.clone()),
         Err(err) => bail!("Error deserializing compiled wasm module: {:#}", err),
       }
     }
diff --git a/crates/dprint/src/plugins/implementations/wasm/mod.rs b/crates/dprint/src/plugins/implementations/wasm/mod.rs
index 05fd23cea..d9cf21293 100644
--- a/crates/dprint/src/plugins/implementations/wasm/mod.rs
+++ b/crates/dprint/src/plugins/implementations/wasm/mod.rs
@@ -1,13 +1,13 @@
 mod compile;
-mod functions;
 mod import_object;
+mod instance;
 mod load_instance;
 mod plugin;
 mod setup_wasm_plugin;
 
 pub use compile::*;
-use functions::*;
 pub use import_object::*;
+use instance::*;
 pub use load_instance::WasmModuleCreator;
 use load_instance::*;
 pub use plugin::*;
diff --git a/crates/dprint/src/plugins/implementations/wasm/plugin.rs b/crates/dprint/src/plugins/implementations/wasm/plugin.rs
index 59bda1ece..aaa68114a 100644
--- a/crates/dprint/src/plugins/implementations/wasm/plugin.rs
+++ b/crates/dprint/src/plugins/implementations/wasm/plugin.rs
@@ -8,12 +8,9 @@ use dprint_core::plugins::CancellationToken;
 use dprint_core::plugins::ConfigChange;
 use dprint_core::plugins::CriticalFormatError;
 use dprint_core::plugins::FileMatchingInfo;
-use dprint_core::plugins::FormatConfigId;
 use dprint_core::plugins::FormatResult;
 use dprint_core::plugins::HostFormatRequest;
-use dprint_core::plugins::SyncPluginInfo;
 use std::cell::RefCell;
-use std::path::Path;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::sync::Arc;
@@ -21,18 +18,16 @@ use std::time::Instant;
 
 use dprint_core::configuration::ConfigKeyMap;
 use dprint_core::configuration::ConfigurationDiagnostic;
-use dprint_core::configuration::GlobalConfiguration;
 use dprint_core::plugins::PluginInfo;
 
 use super::create_pools_import_object;
 use super::load_instance;
 use super::load_instance::WasmInstance;
 use super::load_instance::WasmModule;
-use super::WasmFormatResult;
-use super::WasmFunctions;
 use super::WasmHostFormatSender;
 use super::WasmModuleCreator;
 use crate::environment::Environment;
+use crate::plugins::implementations::wasm::create_wasm_plugin_instance;
 use crate::plugins::FormatConfig;
 use crate::plugins::InitializedPlugin;
 use crate::plugins::InitializedPluginFormatRequest;
@@ -114,177 +109,6 @@ struct WasmPluginSenderWithState {
   instance_state_cell: Rc<RefCell<Option<InstanceState>>>,
 }
 
-pub(super) struct InitializedWasmPluginInstance {
-  wasm_functions: WasmFunctions,
-  buffer_size: usize,
-  current_config_id: FormatConfigId,
-}
-
-impl InitializedWasmPluginInstance {
-  pub fn new(mut wasm_functions: WasmFunctions) -> Result<Self> {
-    let buffer_size = wasm_functions.get_wasm_memory_buffer_size()?;
-    Ok(Self {
-      wasm_functions,
-      buffer_size,
-      current_config_id: FormatConfigId::uninitialized(),
-    })
-  }
-
-  pub fn set_global_config(&mut self, global_config: &GlobalConfiguration) -> Result<()> {
-    let json = serde_json::to_string(global_config)?;
-    self.send_string(&json)?;
-    self.wasm_functions.set_global_config()?;
-    Ok(())
-  }
-
-  pub fn set_plugin_config(&mut self, plugin_config: &ConfigKeyMap) -> Result<()> {
-    let json = serde_json::to_string(plugin_config)?;
-    self.send_string(&json)?;
-    self.wasm_functions.set_plugin_config()?;
-    Ok(())
-  }
-
-  pub fn plugin_info(&mut self) -> Result<PluginInfo> {
-    self.sync_plugin_info().map(|i| i.info)
-  }
-
-  fn sync_plugin_info(&mut self) -> Result<SyncPluginInfo> {
-    let len = self.wasm_functions.get_plugin_info()?;
-    let json_text = self.receive_string(len)?;
-    Ok(serde_json::from_str(&json_text)?)
-  }
-
-  fn license_text(&mut self) -> Result<String> {
-    let len = self.wasm_functions.get_license_text()?;
-    self.receive_string(len)
-  }
-
-  fn resolved_config(&mut self, config: &FormatConfig) -> Result<String> {
-    self.ensure_config(config)?;
-    let len = self.wasm_functions.get_resolved_config()?;
-    self.receive_string(len)
-  }
-
-  fn config_diagnostics(&mut self, config: &FormatConfig) -> Result<Vec<ConfigurationDiagnostic>> {
-    self.ensure_config(config)?;
-    let len = self.wasm_functions.get_config_diagnostics()?;
-    let json_text = self.receive_string(len)?;
-    Ok(serde_json::from_str(&json_text)?)
-  }
-
-  fn file_matching_info(&mut self, _config: &FormatConfig) -> Result<FileMatchingInfo> {
-    self.sync_plugin_info().map(|i| i.file_matching)
-  }
-
-  fn format_text(&mut self, file_path: &Path, file_bytes: &[u8], config: &FormatConfig, override_config: &ConfigKeyMap) -> FormatResult {
-    self.ensure_config(config)?;
-    match self.inner_format_text(file_path, file_bytes, override_config) {
-      Ok(inner) => inner,
-      Err(err) => Err(CriticalFormatError(err).into()),
-    }
-  }
-
-  fn inner_format_text(&mut self, file_path: &Path, file_bytes: &[u8], override_config: &ConfigKeyMap) -> Result<FormatResult> {
-    // send override config if necessary
-    if !override_config.is_empty() {
-      self.send_string(&match serde_json::to_string(override_config) {
-        Ok(text) => text,
-        Err(err) => return Ok(Err(err.into())),
-      })?;
-      self.wasm_functions.set_override_config()?;
-    }
-
-    // send file path
-    self.send_string(&file_path.to_string_lossy())?;
-    self.wasm_functions.set_file_path()?;
-
-    // send file text and format
-    self.send_bytes(file_bytes)?;
-    let response_code = self.wasm_functions.format()?;
-
-    // handle the response
-    match response_code {
-      WasmFormatResult::NoChange => Ok(Ok(None)),
-      WasmFormatResult::Change => {
-        let len = self.wasm_functions.get_formatted_text()?;
-        let text_bytes = self.receive_bytes(len)?;
-        Ok(Ok(Some(text_bytes)))
-      }
-      WasmFormatResult::Error => {
-        let len = self.wasm_functions.get_error_text()?;
-        let text = self.receive_string(len)?;
-        Ok(Err(anyhow!("{}", text)))
-      }
-    }
-  }
-
-  fn ensure_config(&mut self, config: &FormatConfig) -> Result<()> {
-    if self.current_config_id != config.id {
-      // set this to uninitialized in case it errors below
-      self.current_config_id = FormatConfigId::uninitialized();
-      // update the plugin
-      self.set_global_config(&config.global)?;
-      self.set_plugin_config(&config.raw)?;
-      // now mark this as successfully set
-      self.current_config_id = config.id;
-    }
-    Ok(())
-  }
-
-  /* LOW LEVEL SENDING AND RECEIVING */
-
-  // These methods should panic when failing because that may indicate
-  // a major problem where the CLI is out of sync with the plugin.
-
-  fn send_string(&mut self, text: &str) -> Result<()> {
-    self.send_bytes(text.as_bytes())
-  }
-
-  fn send_bytes(&mut self, bytes: &[u8]) -> Result<()> {
-    let mut index = 0;
-    let len = bytes.len();
-    self.wasm_functions.clear_shared_bytes(len)?;
-    while index < len {
-      let write_count = std::cmp::min(len - index, self.buffer_size);
-      self.write_bytes_to_memory_buffer(&bytes[index..(index + write_count)])?;
-      self.wasm_functions.add_to_shared_bytes_from_buffer(write_count)?;
-      index += write_count;
-    }
-    Ok(())
-  }
-
-  fn write_bytes_to_memory_buffer(&mut self, bytes: &[u8]) -> Result<()> {
-    let wasm_buffer_pointer = self.wasm_functions.get_wasm_memory_buffer_ptr()?;
-    let memory_view = self.wasm_functions.get_memory_view();
-    memory_view.write(wasm_buffer_pointer.offset() as u64, bytes)?;
-    Ok(())
-  }
-
-  fn receive_string(&mut self, len: usize) -> Result<String> {
-    let bytes = self.receive_bytes(len)?;
-    Ok(String::from_utf8(bytes)?)
-  }
-
-  fn receive_bytes(&mut self, len: usize) -> Result<Vec<u8>> {
-    let mut index = 0;
-    let mut bytes: Vec<u8> = vec![0; len];
-    while index < len {
-      let read_count = std::cmp::min(len - index, self.buffer_size);
-      self.wasm_functions.set_buffer_with_shared_bytes(index, read_count)?;
-      self.read_bytes_from_memory_buffer(&mut bytes[index..(index + read_count)])?;
-      index += read_count;
-    }
-    Ok(bytes)
-  }
-
-  fn read_bytes_from_memory_buffer(&mut self, bytes: &mut [u8]) -> Result<()> {
-    let wasm_buffer_pointer = self.wasm_functions.get_wasm_memory_buffer_ptr()?;
-    let memory_view = self.wasm_functions.get_memory_view();
-    memory_view.read(wasm_buffer_pointer.offset() as u64, bytes)?;
-    Ok(())
-  }
-}
-
 type LoadInstanceFn = dyn Fn(&mut wasmer::Store, &WasmModule, WasmHostFormatSender) -> Result<WasmInstance> + Send + Sync;
 
 pub struct InitializedWasmPlugin<TEnvironment: Environment> {
@@ -438,8 +262,7 @@ impl<TEnvironment: Environment> InitializedWasmPlugin<TEnvironment> {
       move || {
         let initialize = || {
           let instance = (load_instance)(&mut store, &module, host_format_tx)?;
-          let wasm_functions = WasmFunctions::new(store, instance)?;
-          let instance = InitializedWasmPluginInstance::new(wasm_functions)?;
+          let instance = create_wasm_plugin_instance(store, instance)?;
           Ok(instance)
         };
         let mut instance = match initialize() {
diff --git a/crates/dprint/src/utils/certs.rs b/crates/dprint/src/utils/certs.rs
index dcf352622..ffdd98f90 100644
--- a/crates/dprint/src/utils/certs.rs
+++ b/crates/dprint/src/utils/certs.rs
@@ -55,7 +55,7 @@ fn create_root_cert_store(info: CertInfo) -> RootCertStore {
   }
 
   if let Some(ca_file) = info.ca_file {
-    root_cert_store.add_parsable_certificates(ca_file.into_iter());
+    root_cert_store.add_parsable_certificates(ca_file);
   }
 
   root_cert_store


Test patch: