improve error handling

This commit is contained in:
vms 2020-07-09 20:27:50 +03:00
parent 8eb4a1da7c
commit 37ce9dafb3
13 changed files with 237 additions and 191 deletions

38
Cargo.lock generated
View File

@ -340,10 +340,6 @@ dependencies = [
"failure", "failure",
] ]
[[package]]
name = "export_test"
version = "0.1.0"
[[package]] [[package]]
name = "failure" name = "failure"
version = "0.1.8" version = "0.1.8"
@ -404,6 +400,15 @@ dependencies = [
"wit-parser", "wit-parser",
] ]
[[package]]
name = "fluence"
version = "0.2.0"
source = "git+https://github.com/fluencelabs/rust-sdk#cc2b8249fc43d9667a4ec82a2e767f27df100c55"
dependencies = [
"fluence-sdk-macro",
"fluence-sdk-main",
]
[[package]] [[package]]
name = "fluence-faas" name = "fluence-faas"
version = "0.1.0" version = "0.1.0"
@ -420,10 +425,26 @@ dependencies = [
"wasmer-wasi", "wasmer-wasi",
] ]
[[package]]
name = "fluence-sdk-macro"
version = "0.2.0"
source = "git+https://github.com/fluencelabs/rust-sdk#cc2b8249fc43d9667a4ec82a2e767f27df100c55"
dependencies = [
"fluence-sdk-wit",
]
[[package]]
name = "fluence-sdk-main"
version = "0.2.0"
source = "git+https://github.com/fluencelabs/rust-sdk#cc2b8249fc43d9667a4ec82a2e767f27df100c55"
dependencies = [
"log",
]
[[package]] [[package]]
name = "fluence-sdk-wit" name = "fluence-sdk-wit"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/fluencelabs/rust-sdk#d5d1207a3db623876bdd6697ca9b1171b46f0813" source = "git+https://github.com/fluencelabs/rust-sdk#cc2b8249fc43d9667a4ec82a2e767f27df100c55"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -496,6 +517,13 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]]
name = "greeting"
version = "0.1.0"
dependencies = [
"fluence",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.3.1" version = "0.3.1"

View File

@ -0,0 +1,48 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::error::Error;
use serde_json::Error as SerdeDeserializationError;
#[derive(Debug)]
pub enum WITGeneratorError {
/// An error related to serde deserialization.
DeserializationError(SerdeDeserializationError),
/// Various errors occurred during the parsing/emitting a Wasm file.
IOError(String),
}
impl Error for WITGeneratorError {}
impl std::fmt::Display for WITGeneratorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
WITGeneratorError::DeserializationError(err) => write!(
f,
"Embedded by rust-sdk metadata could't be parsed by serde: {:?}",
err
),
WITGeneratorError::IOError(err) => write!(f, "I/O error occurred: {:?}", err),
}
}
}
impl From<SerdeDeserializationError> for WITGeneratorError {
fn from(err: SerdeDeserializationError) -> Self {
WITGeneratorError::DeserializationError(err)
}
}

View File

@ -14,27 +14,31 @@
* limitations under the License. * limitations under the License.
*/ */
use crate::instructions_generator::WITGenerator;
use crate::default_export_api_config::*; use crate::default_export_api_config::*;
use crate::errors::WITGeneratorError;
use crate::instructions_generator::WITGenerator;
use crate::Result;
pub use fluence_sdk_wit::FCEAst; pub use fluence_sdk_wit::FCEAst;
use wasmer_wit::ast::Interfaces; use wasmer_wit::ast::Interfaces;
// TODO: add error handling
/// Parse generated by rust-sdk AST types, generate instructions and embed them to Wasm file. /// Parse generated by rust-sdk AST types, generate instructions and embed them to Wasm file.
pub fn embed_wit(path: std::path::PathBuf) { pub fn embed_wit(path: std::path::PathBuf) -> Result<()> {
let wasm_module = walrus::ModuleConfig::new() let wasm_module = walrus::ModuleConfig::new()
.parse_file(path.clone()) .parse_file(path.clone())
.unwrap(); .map_err(|e| WITGeneratorError::IOError(format!("{:?} can't be parsed: {:?}", path, e)))?;
let ast_set = wasm_ast_extractor(&wasm_module);
let ast_set = wasm_ast_extractor(&wasm_module)?;
let interfaces = generate_interfaces(&ast_set); let interfaces = generate_interfaces(&ast_set);
let mut wasm_module = wit_parser::embed_wit(wasm_module, &interfaces); let mut wasm_module = wit_parser::embed_wit(wasm_module, &interfaces);
wasm_module.emit_wasm_file(path).unwrap(); wasm_module.emit_wasm_file(path).map_err(|e| {
WITGeneratorError::IOError(format!("resulted Wasm fule can't be emitted: {:?}", e))
})
} }
/// Extract all custom AST types previously embedded by rust-sdk from compiled binary. /// Extract all custom AST types previously embedded by rust-sdk from compiled binary.
fn wasm_ast_extractor(wasm_module: &walrus::Module) -> Vec<fluence_sdk_wit::FCEAst> { fn wasm_ast_extractor(wasm_module: &walrus::Module) -> Result<Vec<fluence_sdk_wit::FCEAst>> {
let mut extracted_ast = Vec::new(); let mut extracted_ast = Vec::new();
// consider only sections name of that starts with GENERATED_SECTION_PREFIX // consider only sections name of that starts with GENERATED_SECTION_PREFIX
@ -45,11 +49,11 @@ fn wasm_ast_extractor(wasm_module: &walrus::Module) -> Vec<fluence_sdk_wit::FCEA
}) { }) {
let default_ids = walrus::IdsToIndices::default(); let default_ids = walrus::IdsToIndices::default();
let raw_data = custom_module.1.data(&default_ids); let raw_data = custom_module.1.data(&default_ids);
let decoded_json: fluence_sdk_wit::FCEAst = serde_json::from_slice(&raw_data).unwrap(); let decoded_json: fluence_sdk_wit::FCEAst = serde_json::from_slice(&raw_data)?;
extracted_ast.push(decoded_json); extracted_ast.push(decoded_json);
} }
extracted_ast Ok(extracted_ast)
} }
fn generate_interfaces(ast_set: &[FCEAst]) -> Interfaces<'_> { fn generate_interfaces(ast_set: &[FCEAst]) -> Interfaces<'_> {

View File

@ -15,7 +15,11 @@
*/ */
mod default_export_api_config; mod default_export_api_config;
mod errors;
mod instructions_generator; mod instructions_generator;
mod interface_generator; mod interface_generator;
pub use interface_generator::embed_wit; pub use interface_generator::embed_wit;
pub use errors::WITGeneratorError;
pub(crate) type Result<T> = std::result::Result<T, WITGeneratorError>;

View File

@ -1,10 +1,13 @@
[package] [package]
name = "export_test" name = "greeting"
version = "0.1.0" version = "0.1.0"
authors = ["Fluence Labs"] authors = ["Fluence Labs"]
edition = "2018" edition = "2018"
[lib] [lib]
name = "export_test" name = "greeting"
path = "src/lib.rs" path = "src/lib.rs"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies]
fluence = { git = "https://github.com/fluencelabs/rust-sdk" }

View File

@ -13,33 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#![allow(clippy::missing_safety_doc)]
mod mem; use fluence::fce;
mod result;
use crate::result::{RESULT_PTR, RESULT_SIZE}; #[fce]
pub fn greeting(name: String) -> String {
#[no_mangle] format!("Hi, {}\n", name)
pub unsafe fn strlen(str_ptr: *mut u8, str_len: usize) -> usize {
let user_name = String::from_raw_parts(str_ptr, str_len, str_len);
user_name.len()
}
#[no_mangle]
pub unsafe fn greeting(user_name_ptr: *mut u8, user_name_size: usize) {
let user_name = String::from_raw_parts(user_name_ptr, user_name_size, user_name_size);
let user_name = format!("Hi, {}\n", user_name);
log_utf8_string(user_name.as_ptr() as i32, user_name.len() as i32);
*RESULT_PTR.get_mut() = user_name.as_ptr() as _;
*RESULT_SIZE.get_mut() = user_name.len();
std::mem::forget(user_name);
}
#[link(wasm_import_module = "logger")]
extern "C" {
// Writes a byte string of size bytes that starts from ptr to a logger
fn log_utf8_string(ptr: i32, size: i32);
} }

View File

@ -1,32 +0,0 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::alloc::{alloc as global_alloc, dealloc as global_dealloc, Layout};
use std::ptr::NonNull;
/// Allocates memory area of specified size and returns its address.
#[no_mangle]
pub unsafe fn allocate(size: usize) -> NonNull<u8> {
let layout: Layout = Layout::from_size_align(size, std::mem::align_of::<u8>()).unwrap();
NonNull::new_unchecked(global_alloc(layout))
}
/// Deallocates memory area for provided memory pointer and size.
#[no_mangle]
pub unsafe fn deallocate(ptr: NonNull<u8>, size: usize) {
let layout = Layout::from_size_align(size, std::mem::align_of::<u8>()).unwrap();
global_dealloc(ptr.as_ptr(), layout);
}

View File

@ -1,30 +0,0 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::sync::atomic::AtomicUsize;
pub static mut RESULT_PTR: AtomicUsize = AtomicUsize::new(0);
pub static mut RESULT_SIZE: AtomicUsize = AtomicUsize::new(0);
#[no_mangle]
pub unsafe fn get_result_ptr() -> usize {
*RESULT_PTR.get_mut()
}
#[no_mangle]
pub unsafe fn get_result_size() -> usize {
*RESULT_SIZE.get_mut()
}

View File

@ -1,51 +0,0 @@
;; allocate function
(@interface type (func (param i32) (result i32)))
;; deallocate function
(@interface type (func (param i32 i32)))
;; greeting function
(@interface type (func (param string) (result string)))
;; strlen function
(@interface type (func (param string) (result i32)))
;; result extractor functions
(@interface type (func (result i32)))
(@interface export "allocate" (func 0))
(@interface export "deallocate" (func 1))
(@interface export "greeting" (func 2))
(@interface export "strlen" (func 3))
(@interface export "get_result_size" (func 4))
(@interface export "get_result_ptr" (func 4))
;; greeting export adapter
(@interface func (type 2)
arg.get 0
string.size
call-core 0 ;; call allocate
arg.get 0
string.lower_memory
call-core 2 ;; call greeting
call-core 5 ;; call get_result_ptr
call-core 4 ;; call get_result_size
string.lift_memory
call-core 5 ;; call get_result_ptr
call-core 4 ;; call get_result_size
call-core 1 ;; call deallocate
)
;; strlen export adapter
(@interface func (type 3)
arg.get 0
string.size
call-core 0 ;; call allocate
arg.get 0
string.lower_memory
call-core 3 ;; call strlen
)
;; Implementations
(@interface implement (func 2) (func 2))
(@interface implement (func 3) (func 3))

View File

@ -1,3 +1,19 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use clap::{App, Arg, SubCommand}; use clap::{App, Arg, SubCommand};
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");
@ -24,13 +40,3 @@ pub fn show_wit<'a, 'b>() -> App<'a, 'b> {
.short("i") .short("i")
.help("path to the Wasm file")]) .help("path to the Wasm file")])
} }
pub fn validate<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("validate")
.about("validates provided Wasm file to suite Fluence network requirements")
.args(&[Arg::with_name(IN_WASM_PATH)
.required(true)
.takes_value(true)
.short("i")
.help("path to the wasm file")])
}

View File

@ -14,6 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
use crate::Result;
use crate::errors::CLIError;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
@ -22,12 +25,11 @@ use std::process::Command;
enum DiagnosticMessage { enum DiagnosticMessage {
BuildScriptExecuted, BuildScriptExecuted,
BuildFinished, BuildFinished,
CompilerArtifact { executable: String }, CompilerArtifact { filenames: Vec<String> },
RunWithArgs, RunWithArgs,
} }
// TODO: add error handling pub(crate) fn build(manifest_path: Option<PathBuf>) -> Result<()> {
pub(crate) fn build(manifest_path: Option<PathBuf>) {
use std::io::Read; use std::io::Read;
let mut cargo = Command::new("cargo"); let mut cargo = Command::new("cargo");
@ -37,25 +39,45 @@ pub(crate) fn build(manifest_path: Option<PathBuf>) {
cargo.arg("--manifest-path").arg(wasm_path); cargo.arg("--manifest-path").arg(wasm_path);
} }
let mut process = cargo.stdout(std::process::Stdio::piped()).spawn().unwrap(); let mut process = cargo.stdout(std::process::Stdio::piped()).spawn()?;
let mut output = String::new(); let mut output = String::new();
process process
.stdout .stdout
.take() .take()
.unwrap() .ok_or_else(|| CLIError::WasmCompilationError("Compilation failed: no output".to_string()))?
.read_to_string(&mut output) .read_to_string(&mut output)?;
.unwrap();
let _status = process.wait().unwrap();
let mut wasms = Vec::new(); let status = process.wait()?;
if !status.success() {
return Err(CLIError::WasmCompilationError(format!(
"Compilation failed with status {}",
status
)));
}
let mut wasms: Vec<String> = Vec::new();
for line in output.lines() { for line in output.lines() {
if let Ok(DiagnosticMessage::CompilerArtifact { executable }) = serde_json::from_str(line) { if let Ok(DiagnosticMessage::CompilerArtifact { filenames }) = serde_json::from_str(line) {
wasms.push(executable) wasms.extend(
filenames
.into_iter()
.filter(|name| name.ends_with(".wasm"))
.collect::<Vec<_>>(),
)
} }
} }
let wasm_path = std::path::PathBuf::from(wasms.first().unwrap()); if wasms.is_empty() {
wit_generator::embed_wit(wasm_path); return Err(CLIError::WasmCompilationError(
"Compilation failed: no .wasm files was generated".to_string(),
));
}
for wasm in wasms {
let wasm_path = std::path::PathBuf::from(wasm);
wit_generator::embed_wit(wasm_path)?;
}
Ok(())
} }

71
tools/cli/src/errors.rs Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use wit_generator::WITGeneratorError;
use wit_parser::WITParserError;
use std::io::Error as StdIOError;
use std::error::Error;
#[derive(Debug)]
pub enum CLIError {
/// Unknown command was entered by user.
NoSuchCommand(String),
/// An error occurred while generating interface types.
WITGeneratorError(WITGeneratorError),
/// An error occurred while parsing interface types.
WITParserError(WITParserError),
/// An error occurred when no Wasm file was compiled.
WasmCompilationError(String),
/// Various errors related to I/O operations.
IOError(StdIOError),
}
impl Error for CLIError {}
impl std::fmt::Display for CLIError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
CLIError::NoSuchCommand(cmd) => write!(f, "{} is unknown command", cmd),
CLIError::WITGeneratorError(err) => write!(f, "{}", err),
CLIError::WITParserError(err) => write!(f, "{}", err),
CLIError::WasmCompilationError(err) => write!(f, "{}", err),
CLIError::IOError(err) => write!(f, "{:?}", err),
}
}
}
impl From<WITGeneratorError> for CLIError {
fn from(err: WITGeneratorError) -> Self {
CLIError::WITGeneratorError(err)
}
}
impl From<WITParserError> for CLIError {
fn from(err: WITParserError) -> Self {
CLIError::WITParserError(err)
}
}
impl From<StdIOError> for CLIError {
fn from(err: StdIOError) -> Self {
CLIError::IOError(err)
}
}

View File

@ -27,28 +27,29 @@
mod args; mod args;
mod build; mod build;
mod errors;
use clap::App; use clap::App;
use clap::AppSettings; use clap::AppSettings;
pub fn main() -> Result<(), exitfailure::ExitFailure> { pub(crate) type Result<T> = std::result::Result<T, crate::errors::CLIError>;
pub fn main() -> Result<()> {
let app = App::new("CLI tool for embedding WIT to provided Wasm file") let app = App::new("CLI tool for embedding WIT to provided Wasm file")
.version(args::VERSION) .version(args::VERSION)
.author(args::AUTHORS) .author(args::AUTHORS)
.about(args::DESCRIPTION) .about(args::DESCRIPTION)
.setting(AppSettings::ArgRequiredElseHelp) .setting(AppSettings::ArgRequiredElseHelp)
.subcommand(args::build()) .subcommand(args::build())
.subcommand(args::show_wit()) .subcommand(args::show_wit());
.subcommand(args::validate());
match app.get_matches().subcommand() { match app.get_matches().subcommand() {
("build", Some(arg)) => { ("build", Some(arg)) => {
let manifest_path = arg let manifest_path = arg
.value_of(args::IN_WASM_PATH) .value_of(args::IN_WASM_PATH)
.map(std::path::PathBuf::from); .map(std::path::PathBuf::from);
crate::build::build(manifest_path);
Ok(()) crate::build::build(manifest_path)
} }
("show", Some(arg)) => { ("show", Some(arg)) => {
let wasm_path = arg.value_of(args::IN_WASM_PATH).unwrap(); let wasm_path = arg.value_of(args::IN_WASM_PATH).unwrap();
@ -59,11 +60,6 @@ pub fn main() -> Result<(), exitfailure::ExitFailure> {
Ok(()) Ok(())
} }
("validate", Some(arg)) => { c => Err(crate::errors::CLIError::NoSuchCommand(c.0.to_string())),
let _wasm_path = arg.value_of(args::IN_WASM_PATH).unwrap();
Ok(())
}
c => Err(failure::err_msg(format!("Unexpected command: {}", c.0)).into()),
} }
} }