From 490cb9ea20ec44d82cf0802bc34ac035b0202cd3 Mon Sep 17 00:00:00 2001 From: David Meister Date: Fri, 25 Jan 2019 03:31:01 +1100 Subject: [PATCH 01/10] WIP on u64 wasm memory --- container_api/src/container/base.rs | 90 +++++------ container_api/src/holochain.rs | 61 +++---- container_api/wasm-test/src/lib.rs | 3 +- core/src/instance.rs | 10 +- core/src/network/test_utils.rs | 152 +++++++++--------- .../src/nucleus/ribosome/api/entry_address.rs | 9 +- core/src/nucleus/ribosome/api/get_entry.rs | 48 +++--- core/src/nucleus/ribosome/memory.rs | 4 +- core/src/nucleus/ribosome/run_dna.rs | 12 +- core/src/nucleus/ribosome/runtime.rs | 2 +- core_types/src/bits_n_pieces.rs | 17 ++ core_types/src/error/ribosome_error.rs | 20 +-- .../src/zome/intro_to_webassembly.md | 6 +- test_utils/src/lib.rs | 8 +- wasm_utils/src/macros.rs | 4 +- wasm_utils/src/memory/allocation.rs | 2 +- wasm_utils/src/memory/mod.rs | 8 +- wasm_utils/src/memory/ribosome.rs | 16 +- wasm_utils/src/memory/stack.rs | 6 +- 19 files changed, 249 insertions(+), 229 deletions(-) diff --git a/container_api/src/container/base.rs b/container_api/src/container/base.rs index d9e6548fe8..f56b319f19 100644 --- a/container_api/src/container/base.rs +++ b/container_api/src/container/base.rs @@ -844,81 +844,81 @@ pub mod tests { (func (export "__hdk_validate_app_entry") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const 0) + (i64.const 0) ) (func (export "__hdk_validate_link") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const 0) + (i64.const 0) ) (func (export "__hdk_get_validation_package_for_entry_type") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "Entry" into memory - (i32.store (i32.const 0) (i32.const 34)) - (i32.store (i32.const 1) (i32.const 69)) - (i32.store (i32.const 2) (i32.const 110)) - (i32.store (i32.const 3) (i32.const 116)) - (i32.store (i32.const 4) (i32.const 114)) - (i32.store (i32.const 5) (i32.const 121)) - (i32.store (i32.const 6) (i32.const 34)) - - (i32.const 7) + (i64.store (i32.const 0) (i64.const 34)) + (i64.store (i32.const 1) (i64.const 69)) + (i64.store (i32.const 2) (i64.const 110)) + (i64.store (i32.const 3) (i64.const 116)) + (i64.store (i32.const 4) (i64.const 114)) + (i64.store (i32.const 5) (i64.const 121)) + (i64.store (i32.const 6) (i64.const 34)) + + (i64.const 7) ) (func (export "__hdk_get_validation_package_for_link") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "Entry" into memory - (i32.store (i32.const 0) (i32.const 34)) - (i32.store (i32.const 1) (i32.const 69)) - (i32.store (i32.const 2) (i32.const 110)) - (i32.store (i32.const 3) (i32.const 116)) - (i32.store (i32.const 4) (i32.const 114)) - (i32.store (i32.const 5) (i32.const 121)) - (i32.store (i32.const 6) (i32.const 34)) - - (i32.const 7) + (i64.store (i32.const 0) (i64.const 34)) + (i64.store (i32.const 1) (i64.const 69)) + (i64.store (i32.const 2) (i64.const 110)) + (i64.store (i32.const 3) (i64.const 116)) + (i64.store (i32.const 4) (i64.const 114)) + (i64.store (i32.const 5) (i64.const 121)) + (i64.store (i32.const 6) (i64.const 34)) + + (i64.const 7) ) (func (export "__list_capabilities") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const 0) + (i64.const 0) ) (func (export "hello") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "Holo World" into memory - (i32.store (i32.const 0) (i32.const 72)) - (i32.store (i32.const 1) (i32.const 111)) - (i32.store (i32.const 2) (i32.const 108)) - (i32.store (i32.const 3) (i32.const 111)) - (i32.store (i32.const 4) (i32.const 32)) - (i32.store (i32.const 5) (i32.const 87)) - (i32.store (i32.const 6) (i32.const 111)) - (i32.store (i32.const 7) (i32.const 114)) - (i32.store (i32.const 8) (i32.const 108)) - (i32.store (i32.const 9) (i32.const 100)) - - (i32.const 10) + (i64.store (i32.const 0) (i64.const 72)) + (i64.store (i32.const 1) (i64.const 111)) + (i64.store (i32.const 2) (i64.const 108)) + (i64.store (i32.const 3) (i64.const 111)) + (i64.store (i32.const 4) (i64.const 32)) + (i64.store (i32.const 5) (i64.const 87)) + (i64.store (i32.const 6) (i64.const 111)) + (i64.store (i32.const 7) (i64.const 114)) + (i64.store (i32.const 8) (i64.const 108)) + (i64.store (i32.const 9) (i64.const 100)) + + (i64.const 10) ) ) "# diff --git a/container_api/src/holochain.rs b/container_api/src/holochain.rs index 15c9235768..069e459cc1 100644 --- a/container_api/src/holochain.rs +++ b/container_api/src/holochain.rs @@ -291,8 +291,8 @@ mod tests { r#" (module (memory (;0;) 17) - (func (export "genesis") (param $p0 i32) (result i32) - i32.const 9 + (func (export "genesis") (param $p0 i64) (result i64) + i64.const 9 ) (data (i32.const 0) "fail" @@ -313,33 +313,34 @@ mod tests { } #[test] + #[cfg(feature = "broken-tests")] fn fails_instantiate_if_genesis_times_out() { - // let dna = create_test_dna_with_wat( - // "test_zome", - // Callback::Genesis.capability().as_str(), - // Some( - // r#" - // (module - // (memory (;0;) 17) - // (func (export "genesis") (param $p0 i32) (result i32) - // (loop (br 0)) - // i32.const 0 - // ) - // (export "memory" (memory 0)) - // ) - // "#, - // ), - // ); - // - // let (context, _test_logger, _) = test_context("bob"); - // let result = Holochain::new(dna.clone(), context.clone()); - // assert!(result.is_err()); - // assert_eq!( - // HolochainInstanceError::from(HolochainError::ErrorGeneric( - // "Timeout while initializing".to_string() - // )), - // result.err().unwrap(), - // ); + let dna = create_test_dna_with_wat( + "test_zome", + Callback::Genesis.capability().as_str(), + Some( + r#" + (module + (memory (;0;) 17) + (func (export "genesis") (param $p0 i64) (result i64) + (loop (br 0)) + i64.const 0 + ) + (export "memory" (memory 0)) + ) + "#, + ), + ); + + let (context, _test_logger, _) = test_context("bob"); + let result = Holochain::new(dna.clone(), context.clone()); + assert!(result.is_err()); + assert_eq!( + HolochainInstanceError::from(HolochainError::ErrorGeneric( + "Timeout while initializing".to_string() + )), + result.err().unwrap(), + ); } #[test] @@ -380,8 +381,8 @@ mod tests { (memory 1) (export "memory" (memory 0)) (export "main" (func $func0)) - (func $func0 (param $p0 i32) (result i32) - i32.const 16 + (func $func0 (param $p0 i64) (result i64) + i64.const 16 ) (data (i32.const 0) "{\"holo\":\"world\"}" diff --git a/container_api/wasm-test/src/lib.rs b/container_api/wasm-test/src/lib.rs index 33148e30d0..81d2ec5f09 100644 --- a/container_api/wasm-test/src/lib.rs +++ b/container_api/wasm-test/src/lib.rs @@ -169,7 +169,8 @@ fn hdk_commit_fail(mem_stack: &mut WasmStack) -> Result { .deallocate(allocation_of_input) .expect("deallocate failed"); - let address = JsonString::from(result.value).try_into()?; + // let address = JsonString::from(result.value).try_into()?; + let address = JsonString::from(result).try_into()?; Ok(address) } diff --git a/core/src/instance.rs b/core/src/instance.rs index c2a1572fb5..057547758a 100644 --- a/core/src/instance.rs +++ b/core/src/instance.rs @@ -718,10 +718,10 @@ pub mod tests { r#" (module (memory (;0;) 17) - (func (export "genesis") (param $p0 i32) (result i32) - i32.const 0 + (func (export "genesis") (param $p0 i64) (result i64) + i64.const 0 ) - (data (i32.const 0) + (data (i64.const 0) "" ) (export "memory" (memory 0)) @@ -747,8 +747,8 @@ pub mod tests { r#" (module (memory (;0;) 17) - (func (export "genesis") (param $p0 i32) (result i32) - i32.const 9 + (func (export "genesis") (param $p0 i64) (result i64) + i64.const 9 ) (data (i32.const 0) "1337.0" diff --git a/core/src/network/test_utils.rs b/core/src/network/test_utils.rs index 049f936a5c..4d391029ff 100644 --- a/core/src/network/test_utils.rs +++ b/core/src/network/test_utils.rs @@ -51,61 +51,61 @@ pub fn test_wat_always_valid() -> String { (func (export "__hdk_validate_app_entry") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const 0) + (i64.const 0) ) (func (export "__hdk_validate_link") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const 0) + (i64.const 0) ) (func (export "__hdk_get_validation_package_for_entry_type") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "Entry" into memory - (i32.store (i32.const 0) (i32.const 34)) - (i32.store (i32.const 1) (i32.const 69)) - (i32.store (i32.const 2) (i32.const 110)) - (i32.store (i32.const 3) (i32.const 116)) - (i32.store (i32.const 4) (i32.const 114)) - (i32.store (i32.const 5) (i32.const 121)) - (i32.store (i32.const 6) (i32.const 34)) - - (i32.const 7) + (i64.store (i32.const 0) (i64.const 34)) + (i64.store (i32.const 1) (i64.const 69)) + (i64.store (i32.const 2) (i64.const 110)) + (i64.store (i32.const 3) (i64.const 116)) + (i64.store (i32.const 4) (i64.const 114)) + (i64.store (i32.const 5) (i64.const 121)) + (i64.store (i32.const 6) (i64.const 34)) + + (i64.const 7) ) (func (export "__hdk_get_validation_package_for_link") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "Entry" into memory - (i32.store (i32.const 0) (i32.const 34)) - (i32.store (i32.const 1) (i32.const 69)) - (i32.store (i32.const 2) (i32.const 110)) - (i32.store (i32.const 3) (i32.const 116)) - (i32.store (i32.const 4) (i32.const 114)) - (i32.store (i32.const 5) (i32.const 121)) - (i32.store (i32.const 6) (i32.const 34)) - - (i32.const 7) + (i64.store (i32.const 0) (i64.const 34)) + (i64.store (i32.const 1) (i64.const 69)) + (i64.store (i32.const 2) (i64.const 110)) + (i64.store (i32.const 3) (i64.const 116)) + (i64.store (i32.const 4) (i64.const 114)) + (i64.store (i32.const 5) (i64.const 121)) + (i64.store (i32.const 6) (i64.const 34)) + + (i64.const 7) ) (func (export "__list_capabilities") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const 0) + (i64.const 0) ) ) "# @@ -121,18 +121,18 @@ pub fn test_wat_always_invalid() -> String { (func (export "__hdk_validate_app_entry") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "FAIL wat" into memory - (i32.store (i32.const 0) (i32.const 70)) - (i32.store (i32.const 1) (i32.const 65)) - (i32.store (i32.const 2) (i32.const 73)) - (i32.store (i32.const 3) (i32.const 76)) - (i32.store (i32.const 4) (i32.const 32)) - (i32.store (i32.const 5) (i32.const 119)) - (i32.store (i32.const 6) (i32.const 97)) - (i32.store (i32.const 7) (i32.const 116)) + (i64.store (i32.const 0) (i64.const 70)) + (i64.store (i32.const 1) (i64.const 65)) + (i64.store (i32.const 2) (i64.const 73)) + (i64.store (i32.const 3) (i64.const 76)) + (i64.store (i32.const 4) (i64.const 32)) + (i64.store (i32.const 5) (i64.const 119)) + (i64.store (i32.const 6) (i64.const 97)) + (i64.store (i32.const 7) (i64.const 116)) (i32.const 8) ) @@ -143,59 +143,59 @@ pub fn test_wat_always_invalid() -> String { (result i32) ;; This writes "FAIL wat" into memory - (i32.store (i32.const 0) (i32.const 70)) - (i32.store (i32.const 1) (i32.const 65)) - (i32.store (i32.const 2) (i32.const 73)) - (i32.store (i32.const 3) (i32.const 76)) - (i32.store (i32.const 4) (i32.const 32)) - (i32.store (i32.const 5) (i32.const 119)) - (i32.store (i32.const 6) (i32.const 97)) - (i32.store (i32.const 7) (i32.const 116)) - - (i32.const 8) + (i64.store (i32.const 0) (i64.const 70)) + (i64.store (i32.const 1) (i64.const 65)) + (i64.store (i32.const 2) (i64.const 73)) + (i64.store (i32.const 3) (i64.const 76)) + (i64.store (i32.const 4) (i64.const 32)) + (i64.store (i32.const 5) (i64.const 119)) + (i64.store (i32.const 6) (i64.const 97)) + (i64.store (i32.const 7) (i64.const 116)) + + (i64.const 8) ) (func (export "__hdk_get_validation_package_for_entry_type") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "Entry" into memory - (i32.store (i32.const 0) (i32.const 34)) - (i32.store (i32.const 1) (i32.const 69)) - (i32.store (i32.const 2) (i32.const 110)) - (i32.store (i32.const 3) (i32.const 116)) - (i32.store (i32.const 4) (i32.const 114)) - (i32.store (i32.const 5) (i32.const 121)) - (i32.store (i32.const 6) (i32.const 34)) - - (i32.const 7) + (i64.store (i32.const 0) (i64.const 34)) + (i64.store (i32.const 1) (i64.const 69)) + (i64.store (i32.const 2) (i64.const 110)) + (i64.store (i32.const 3) (i64.const 116)) + (i64.store (i32.const 4) (i64.const 114)) + (i64.store (i32.const 5) (i64.const 121)) + (i64.store (i32.const 6) (i64.const 34)) + + (i64.const 7) ) (func (export "__hdk_get_validation_package_for_link") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "Entry" into memory - (i32.store (i32.const 0) (i32.const 34)) - (i32.store (i32.const 1) (i32.const 69)) - (i32.store (i32.const 2) (i32.const 110)) - (i32.store (i32.const 3) (i32.const 116)) - (i32.store (i32.const 4) (i32.const 114)) - (i32.store (i32.const 5) (i32.const 121)) - (i32.store (i32.const 6) (i32.const 34)) - - (i32.const 7) + (i64.store (i32.const 0) (i64.const 34)) + (i64.store (i32.const 1) (i64.const 69)) + (i64.store (i32.const 2) (i64.const 110)) + (i64.store (i32.const 3) (i64.const 116)) + (i64.store (i32.const 4) (i64.const 114)) + (i64.store (i32.const 5) (i64.const 121)) + (i64.store (i32.const 6) (i64.const 34)) + + (i64.const 7) ) (func (export "__list_capabilities") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const 0) + (i64.const 0) ) ) "# diff --git a/core/src/nucleus/ribosome/api/entry_address.rs b/core/src/nucleus/ribosome/api/entry_address.rs index d220b921af..15d8897627 100644 --- a/core/src/nucleus/ribosome/api/entry_address.rs +++ b/core/src/nucleus/ribosome/api/entry_address.rs @@ -7,11 +7,12 @@ use holochain_core_types::{ }; use std::{convert::TryFrom, str::FromStr}; use wasmi::{RuntimeArgs, RuntimeValue}; +use holochain_core_types::error::RibosomeRuntimeBits; pub fn get_entry_type(dna: &Dna, entry_type_name: &str) -> Result> { let entry_type = EntryType::from_str(&entry_type_name).map_err(|_| { - Some(RuntimeValue::I32( - holochain_core_types::error::RibosomeErrorCode::UnknownEntryType as i32, + Some(RuntimeValue::I64( + holochain_core_types::error::RibosomeErrorCode::UnknownEntryType as RibosomeRuntimeBits, )) })?; @@ -19,8 +20,8 @@ pub fn get_entry_type(dna: &Dna, entry_type_name: &str) -> Result Vec { self.wasm_memory .get( - MemoryBits::from(allocation.offset()), + MemoryInt::from(allocation.offset()), MemoryInt::from(allocation.length()) as usize, ) .expect("Successfully retrieve the result") diff --git a/core/src/nucleus/ribosome/run_dna.rs b/core/src/nucleus/ribosome/run_dna.rs index 494d0be5f7..39ff4c5f77 100644 --- a/core/src/nucleus/ribosome/run_dna.rs +++ b/core/src/nucleus/ribosome/run_dna.rs @@ -61,10 +61,10 @@ pub fn run_dna( ZomeApiFunction::Abort => Ok(FuncInstance::alloc_host( Signature::new( &[ - ValueType::I32, - ValueType::I32, - ValueType::I32, - ValueType::I32, + ValueType::I64, + ValueType::I64, + ValueType::I64, + ValueType::I64, ][..], None, ), @@ -72,7 +72,7 @@ pub fn run_dna( )), // All of our Zome API Functions have the same signature _ => Ok(FuncInstance::alloc_host( - Signature::new(&[ValueType::I32][..], Some(ValueType::I32)), + Signature::new(&[ValueType::I64][..], Some(ValueType::I64)), api_fn as usize, )), } @@ -129,7 +129,7 @@ pub fn run_dna( returned_encoding = wasm_instance .invoke_export( zome_call.fn_name.clone().as_str(), - &[RuntimeValue::I32( + &[RuntimeValue::I64( RibosomeEncodingBits::from(encoded_allocation_of_input) as RibosomeRuntimeBits, )], mut_runtime, diff --git a/core/src/nucleus/ribosome/runtime.rs b/core/src/nucleus/ribosome/runtime.rs index b35d973f84..2256650d7f 100644 --- a/core/src/nucleus/ribosome/runtime.rs +++ b/core/src/nucleus/ribosome/runtime.rs @@ -78,7 +78,7 @@ impl Runtime { match self.memory_manager.write(&s_bytes) { Err(_) => ribosome_error_code!(Unspecified), - Ok(allocation) => Ok(Some(RuntimeValue::I32(RibosomeEncodingBits::from( + Ok(allocation) => Ok(Some(RuntimeValue::I64(RibosomeEncodingBits::from( RibosomeEncodedValue::Allocation(allocation.into()), ) as RibosomeRuntimeBits))), diff --git a/core_types/src/bits_n_pieces.rs b/core_types/src/bits_n_pieces.rs index caa0640c21..baa4dcf5e1 100644 --- a/core_types/src/bits_n_pieces.rs +++ b/core_types/src/bits_n_pieces.rs @@ -1,4 +1,5 @@ pub const U16_MAX: u32 = u16::max_value() as u32; +pub const U32_MAX: u64 = u32::max_value() as u64; /// returns the u16 high bits from a u32 pub fn u32_high_bits(i: u32) -> u16 { @@ -20,6 +21,22 @@ pub fn u32_merge_bits(high: u16, low: u16) -> u32 { (u32::from(high) << 16) | u32::from(low) } +pub fn u64_high_bits(i: u64) -> u32 { + (i >> 32) as u32 +} + +pub fn u64_low_bits(i: u64) -> u32 { + (i as u32) +} + +pub fn u64_split_bits(i: u64) -> (u32, u32) { + (u64_high_bits(i), u64_low_bits(i)) +} + +pub fn u64_merge_bits(high: u32, low: u32) -> u64 { + (u64::from(high) << 32) | u64::from(low) +} + #[cfg(test)] pub mod tests { diff --git a/core_types/src/error/ribosome_error.rs b/core_types/src/error/ribosome_error.rs index defcbd12f9..850ee3a64e 100644 --- a/core_types/src/error/ribosome_error.rs +++ b/core_types/src/error/ribosome_error.rs @@ -1,15 +1,15 @@ use self::{RibosomeEncodedValue::*, RibosomeErrorCode::*}; use crate::{error::HolochainError, json::JsonString}; -use bits_n_pieces::u32_split_bits; +use bits_n_pieces::u64_split_bits; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{convert::TryFrom, str::FromStr}; /// size of the integer that encodes ribosome codes -pub type RibosomeEncodingBits = u32; +pub type RibosomeEncodingBits = u64; /// size of the integer that wasm sees -pub type RibosomeRuntimeBits = i32; +pub type RibosomeRuntimeBits = i64; /// size of the integer that represents a ribosome code -pub type RibosomeCodeBits = u16; +pub type RibosomeCodeBits = u32; #[derive(Clone, Debug, PartialEq)] pub struct RibosomeEncodedAllocation(RibosomeEncodingBits); @@ -34,7 +34,7 @@ impl ToString for RibosomeEncodedAllocation { /// Represents all possible values passed to/from wasmi functions /// All wasmi functions are I32 values -#[repr(u32)] +#[repr(u64)] #[derive(Clone, Debug, PartialEq)] pub enum RibosomeEncodedValue { /// @TODO make this unambiguous or remove @@ -75,7 +75,7 @@ impl From for RibosomeEncodedValue { if i == 0 { RibosomeEncodedValue::Success } else { - let (code_int, maybe_allocation_length) = u32_split_bits(i); + let (code_int, maybe_allocation_length) = u64_split_bits(i); if maybe_allocation_length == 0 { RibosomeEncodedValue::Failure(RibosomeErrorCode::from_code_int(code_int)) } else { @@ -139,7 +139,7 @@ impl RibosomeEncodedValue { } /// Enum of all possible ERROR codes that a Zome API Function could return. -#[repr(u32)] +#[repr(u64)] #[derive(Clone, Debug, PartialEq, Eq, Hash, DefaultJson)] #[rustfmt::skip] pub enum RibosomeErrorCode { @@ -286,7 +286,7 @@ pub mod tests { #[test] fn ribosome_error_code_round_trip() { let oom = RibosomeErrorCode::from_code_int( - ((RibosomeErrorCode::OutOfMemory as u32) >> 16) as u16, + ((RibosomeErrorCode::OutOfMemory as u64) >> 32) as u32, ); assert_eq!(RibosomeErrorCode::OutOfMemory, oom); assert_eq!(RibosomeErrorCode::OutOfMemory.to_string(), oom.to_string()); @@ -303,8 +303,8 @@ pub mod tests { let inner_code = RibosomeEncodedValue::from_error(err); - let _one_int: i32 = inner_code.clone().into(); - let _another_int: u32 = inner_code.clone().into(); + let _one_int: i64 = inner_code.clone().into(); + let _another_int: u64 = inner_code.clone().into(); } } diff --git a/doc/holochain_101/src/zome/intro_to_webassembly.md b/doc/holochain_101/src/zome/intro_to_webassembly.md index 867dfb4a60..1d23933a7e 100644 --- a/doc/holochain_101/src/zome/intro_to_webassembly.md +++ b/doc/holochain_101/src/zome/intro_to_webassembly.md @@ -17,8 +17,8 @@ Not because it needs to be understood, but so that you can get a glimpse of what ``` (module (memory (;0;) 17) - (func (export "main") (param $p0 i32) (result i32) - i32.const 6 + (func (export "main") (param $p0 i64) (result i64) + i64.const 6 ) (data (i32.const 0) "1337.0" @@ -32,5 +32,3 @@ Once the above code is converted from WAT to binary WASM it is in the format tha Often times, for a language that compiles to WASM, you will have a configuration option to generate the (more) human readable WAT version of the code as well, while compiling it to WASM. While the compilation to WASM mostly happens in the background for you as an app developer, having a basic understanding of the role of WebAssembly in this technology stack will no doubt help you along the way. - - diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index fc9fb151e2..4c0c982565 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -55,10 +55,10 @@ pub fn create_test_dna_with_wat(zome_name: &str, cap_name: &str, wat: Option<&st let default_wat = r#" (module (memory (;0;) 17) - (func (export "main") (param $p0 i32) (result i32) - i32.const 6 + (func (export "main") (param $p0 i64) (result i64) + i64.const 6 ) - (data (i32.const 0) + (data (i64.const 0) "1337.0" ) (export "memory" (memory 0)) @@ -66,6 +66,8 @@ pub fn create_test_dna_with_wat(zome_name: &str, cap_name: &str, wat: Option<&st "#; let wat_str = wat.unwrap_or_else(|| &default_wat); + println!("xxx {:?}", wat_str); + // Test WASM code that returns 1337 as integer let wasm_binary = Wat2Wasm::new() .canonicalize_lebs(false) diff --git a/wasm_utils/src/macros.rs b/wasm_utils/src/macros.rs index 3ecf3baa92..e11704e4b6 100644 --- a/wasm_utils/src/macros.rs +++ b/wasm_utils/src/macros.rs @@ -14,7 +14,7 @@ macro_rules! zome_assert { #[macro_export] macro_rules! ribosome_success { () => { - Ok(Some(RuntimeValue::I32( + Ok(Some(RuntimeValue::I64( $crate::holochain_core_types::error::RibosomeRuntimeBits::from( $crate::holochain_core_types::error::RibosomeEncodedValue::Success, ), @@ -26,7 +26,7 @@ macro_rules! ribosome_success { #[macro_export] macro_rules! ribosome_error_code { ($s:ident) => { - Ok(Some(RuntimeValue::I32( + Ok(Some(RuntimeValue::I64( $crate::holochain_core_types::error::RibosomeErrorCode::$s as $crate::holochain_core_types::error::RibosomeRuntimeBits, ))) diff --git a/wasm_utils/src/memory/allocation.rs b/wasm_utils/src/memory/allocation.rs index 4d47f450b0..08adcfaecc 100644 --- a/wasm_utils/src/memory/allocation.rs +++ b/wasm_utils/src/memory/allocation.rs @@ -213,7 +213,7 @@ pub mod tests { pub fn allocation_new_test() { assert_eq!( Err(AllocationError::OutOfBounds), - WasmAllocation::new(Offset::from(std::u16::MAX), Length::from(1)), + WasmAllocation::new(Offset::from(std::u32::MAX), Length::from(1)), ); assert_eq!( diff --git a/wasm_utils/src/memory/mod.rs b/wasm_utils/src/memory/mod.rs index 563f78ed91..0610defc2d 100644 --- a/wasm_utils/src/memory/mod.rs +++ b/wasm_utils/src/memory/mod.rs @@ -1,4 +1,4 @@ -use holochain_core_types::bits_n_pieces::U16_MAX; +use holochain_core_types::bits_n_pieces::U32_MAX; pub mod allocation; pub mod read; @@ -7,11 +7,11 @@ pub mod stack; pub mod write; /// offsets, lengths, etc. -pub type MemoryInt = u16; +pub type MemoryInt = u32; /// encodes allocations as 2x MemoryInt in high/low bits etc. /// must be 2x larger than MemoryInt -pub type MemoryBits = u32; +pub type MemoryBits = u64; /// represents the max MemoryInt in MemoryBits to facilitate gt comparisons -const MEMORY_INT_MAX: MemoryBits = U16_MAX; +const MEMORY_INT_MAX: MemoryBits = U32_MAX; diff --git a/wasm_utils/src/memory/ribosome.rs b/wasm_utils/src/memory/ribosome.rs index 789c15166a..2fb28069b5 100644 --- a/wasm_utils/src/memory/ribosome.rs +++ b/wasm_utils/src/memory/ribosome.rs @@ -1,7 +1,7 @@ // extends memory allocation to work with ribosome encodings use holochain_core_types::{ - bits_n_pieces::{u32_merge_bits, u32_split_bits}, + bits_n_pieces::{u64_merge_bits, u64_split_bits}, error::{ HolochainError, RibosomeEncodedAllocation, RibosomeEncodedValue, RibosomeEncodingBits, RibosomeErrorCode, @@ -20,14 +20,14 @@ impl TryFrom for WasmAllocation { fn try_from( ribosome_memory_allocation: RibosomeEncodedAllocation, ) -> Result { - let (offset, length) = u32_split_bits(MemoryBits::from(ribosome_memory_allocation)); + let (offset, length) = u64_split_bits(MemoryBits::from(ribosome_memory_allocation)); WasmAllocation::new(offset.into(), length.into()) } } impl From for RibosomeEncodedAllocation { fn from(wasm_allocation: WasmAllocation) -> Self { - u32_merge_bits( + u64_merge_bits( wasm_allocation.offset().into(), wasm_allocation.length().into(), ) @@ -143,7 +143,7 @@ where pub mod tests { use holochain_core_types::{ - bits_n_pieces::u32_merge_bits, + bits_n_pieces::u64_merge_bits, error::{ RibosomeEncodedAllocation, RibosomeEncodedValue, RibosomeEncodingBits, RibosomeErrorCode, @@ -165,9 +165,9 @@ pub mod tests { assert_eq!( Err(AllocationError::OutOfBounds), - WasmAllocation::try_from(RibosomeEncodedAllocation::from(u32_merge_bits( - std::u16::MAX, - std::u16::MAX + WasmAllocation::try_from(RibosomeEncodedAllocation::from(u64_merge_bits( + std::u32::MAX, + std::u32::MAX ))), ); @@ -284,7 +284,7 @@ pub mod tests { fn stack_from_encoding_test() { assert_eq!( Err(RibosomeEncodedValue::from(AllocationError::OutOfBounds)), - WasmStack::try_from_ribosome_encoding(u32_merge_bits(std::u16::MAX, std::u16::MAX)), + WasmStack::try_from_ribosome_encoding(u64_merge_bits(std::u32::MAX, std::u32::MAX)), ); assert_eq!( diff --git a/wasm_utils/src/memory/stack.rs b/wasm_utils/src/memory/stack.rs index dd840fb01d..43d38d1ebe 100644 --- a/wasm_utils/src/memory/stack.rs +++ b/wasm_utils/src/memory/stack.rs @@ -16,7 +16,7 @@ impl From for MemoryInt { impl From for usize { fn from(top: Top) -> Self { - Self::from(MemoryInt::from(top)) + MemoryInt::from(top) as usize } } @@ -182,7 +182,7 @@ pub mod tests { let out_of_bounds_allocation = WasmAllocation { offset: Offset::from(13), - length: Length::from(std::u16::MAX), + length: Length::from(std::u32::MAX), }; assert_eq!( Err(AllocationError::OutOfBounds), @@ -220,7 +220,7 @@ pub mod tests { assert_eq!( Err(AllocationError::OutOfBounds), WasmStack::try_from(WasmAllocation { - offset: Offset::from(std::u16::MAX), + offset: Offset::from(std::u32::MAX), length: Length::from(1) }), ); From f04e85b3292bab20ce9b73a5797d642fe276ba97 Mon Sep 17 00:00:00 2001 From: David Meister Date: Fri, 25 Jan 2019 04:28:34 +1100 Subject: [PATCH 02/10] WIP on memory bits update --- core/src/instance.rs | 2 +- core/src/network/test_utils.rs | 6 +- core/src/nucleus/ribosome/api/call.rs | 4 +- core/src/nucleus/ribosome/api/commit.rs | 4 +- core/src/nucleus/ribosome/api/debug.rs | 4 +- .../src/nucleus/ribosome/api/entry_address.rs | 4 +- core/src/nucleus/ribosome/api/get_entry.rs | 4 +- core/src/nucleus/ribosome/api/get_links.rs | 4 +- core/src/nucleus/ribosome/api/init_globals.rs | 4 +- core/src/nucleus/ribosome/api/link_entries.rs | 2 +- core/src/nucleus/ribosome/api/mod.rs | 82 +++++++++---------- core/src/nucleus/ribosome/api/query.rs | 6 +- core/src/nucleus/ribosome/api/send.rs | 4 +- core/src/nucleus/ribosome/api/update_entry.rs | 4 +- core/src/nucleus/ribosome/callback/mod.rs | 22 ++--- core/src/nucleus/ribosome/memory.rs | 4 +- core_types/src/error/error.rs | 5 -- core_types/src/error/ribosome_error.rs | 27 +++--- doc/holochain_101/src/zome/implementation.md | 4 +- test_utils/src/lib.rs | 2 +- wasm_utils/src/memory/ribosome.rs | 8 +- .../wasm-test/integration-test/src/lib.rs | 2 +- 22 files changed, 103 insertions(+), 105 deletions(-) diff --git a/core/src/instance.rs b/core/src/instance.rs index 057547758a..3fac470daf 100644 --- a/core/src/instance.rs +++ b/core/src/instance.rs @@ -721,7 +721,7 @@ pub mod tests { (func (export "genesis") (param $p0 i64) (result i64) i64.const 0 ) - (data (i64.const 0) + (data (i32.const 0) "" ) (export "memory" (memory 0)) diff --git a/core/src/network/test_utils.rs b/core/src/network/test_utils.rs index 4d391029ff..fb35c8887a 100644 --- a/core/src/network/test_utils.rs +++ b/core/src/network/test_utils.rs @@ -134,13 +134,13 @@ pub fn test_wat_always_invalid() -> String { (i64.store (i32.const 6) (i64.const 97)) (i64.store (i32.const 7) (i64.const 116)) - (i32.const 8) + (i64.const 8) ) (func (export "__hdk_validate_link") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "FAIL wat" into memory (i64.store (i32.const 0) (i64.const 70)) diff --git a/core/src/nucleus/ribosome/api/call.rs b/core/src/nucleus/ribosome/api/call.rs index 83c53aeb4a..0ce8589c78 100644 --- a/core/src/nucleus/ribosome/api/call.rs +++ b/core/src/nucleus/ribosome/api/call.rs @@ -32,12 +32,12 @@ impl ZomeFnCall { } /// HcApiFuncIndex::CALL function code -/// args: [0] encoded MemoryAllocation as u32 +/// args: [0] encoded MemoryAllocation as u64 /// expected complex argument: {zome_name: String, cap_token: Address, fn_name: String, args: String} /// args from API call are converted into a ZomeFnCall /// Launch an Action::Call with newly formed ZomeFnCall /// Waits for a ZomeFnResult -/// Returns an HcApiReturnCode as I32 +/// Returns an HcApiReturnCode as I64 pub fn invoke_call(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult { // deserialize args let args_str = runtime.load_json_string_from_args(&args); diff --git a/core/src/nucleus/ribosome/api/commit.rs b/core/src/nucleus/ribosome/api/commit.rs index f8ce153114..052bfeab5c 100644 --- a/core/src/nucleus/ribosome/api/commit.rs +++ b/core/src/nucleus/ribosome/api/commit.rs @@ -8,9 +8,9 @@ use std::convert::TryFrom; use wasmi::{RuntimeArgs, RuntimeValue}; /// ZomeApiFunction::CommitAppEntry function code -/// args: [0] encoded MemoryAllocation as u32 +/// args: [0] encoded MemoryAllocation as u64 /// Expected complex argument: CommitArgs -/// Returns an HcApiReturnCode as I32 +/// Returns an HcApiReturnCode as I64 pub fn invoke_commit_app_entry(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult { // deserialize args let args_str = runtime.load_json_string_from_args(&args); diff --git a/core/src/nucleus/ribosome/api/debug.rs b/core/src/nucleus/ribosome/api/debug.rs index cf78a6042c..089b736ea6 100644 --- a/core/src/nucleus/ribosome/api/debug.rs +++ b/core/src/nucleus/ribosome/api/debug.rs @@ -2,9 +2,9 @@ use crate::nucleus::ribosome::{api::ZomeApiResult, Runtime}; use wasmi::{RuntimeArgs, RuntimeValue}; /// ZomeApiFunction::Debug function code -/// args: [0] encoded MemoryAllocation as u32 +/// args: [0] encoded MemoryAllocation as u64 /// Expecting a string as complex input argument -/// Returns an HcApiReturnCode as I32 +/// Returns an HcApiReturnCode as I64 pub fn invoke_debug(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult { let payload = runtime.load_json_string_from_args(args); diff --git a/core/src/nucleus/ribosome/api/entry_address.rs b/core/src/nucleus/ribosome/api/entry_address.rs index 15d8897627..d26a546a64 100644 --- a/core/src/nucleus/ribosome/api/entry_address.rs +++ b/core/src/nucleus/ribosome/api/entry_address.rs @@ -30,9 +30,9 @@ pub fn get_entry_type(dna: &Dna, entry_type_name: &str) -> Result ZomeApiResult { // deserialize args let args_str = runtime.load_json_string_from_args(&args); diff --git a/core/src/nucleus/ribosome/api/get_entry.rs b/core/src/nucleus/ribosome/api/get_entry.rs index a998ec823d..17ec57d500 100644 --- a/core/src/nucleus/ribosome/api/get_entry.rs +++ b/core/src/nucleus/ribosome/api/get_entry.rs @@ -8,9 +8,9 @@ use std::convert::TryFrom; use wasmi::{RuntimeArgs, RuntimeValue}; /// ZomeApiFunction::GetAppEntry function code -/// args: [0] encoded MemoryAllocation as u32 +/// args: [0] encoded MemoryAllocation as u64 /// Expected complex argument: GetEntryArgs -/// Returns an HcApiReturnCode as I32 +/// Returns an HcApiReturnCode as I64 pub fn invoke_get_entry(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult { // deserialize args let args_str = runtime.load_json_string_from_args(&args); diff --git a/core/src/nucleus/ribosome/api/get_links.rs b/core/src/nucleus/ribosome/api/get_links.rs index 4b251e6dce..2262cbfb2f 100644 --- a/core/src/nucleus/ribosome/api/get_links.rs +++ b/core/src/nucleus/ribosome/api/get_links.rs @@ -10,9 +10,9 @@ use std::convert::TryFrom; use wasmi::{RuntimeArgs, RuntimeValue}; /// ZomeApiFunction::GetLinks function code -/// args: [0] encoded MemoryAllocation as u32 +/// args: [0] encoded MemoryAllocation as u64 /// Expected complex argument: GetLinksArgs -/// Returns an HcApiReturnCode as I32 +/// Returns an HcApiReturnCode as I64 pub fn invoke_get_links(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult { // deserialize args let args_str = runtime.load_json_string_from_args(&args); diff --git a/core/src/nucleus/ribosome/api/init_globals.rs b/core/src/nucleus/ribosome/api/init_globals.rs index 9420076d03..ebf26b576b 100644 --- a/core/src/nucleus/ribosome/api/init_globals.rs +++ b/core/src/nucleus/ribosome/api/init_globals.rs @@ -9,9 +9,9 @@ use holochain_wasm_utils::api_serialization::ZomeApiGlobals; use wasmi::RuntimeArgs; /// ZomeApiFunction::InitGlobals secret function code -/// args: [0] encoded MemoryAllocation as u32 +/// args: [0] encoded MemoryAllocation as u64 /// Not expecting any complex input -/// Returns an HcApiReturnCode as I32 +/// Returns an HcApiReturnCode as I64 pub fn invoke_init_globals(runtime: &mut Runtime, _args: &RuntimeArgs) -> ZomeApiResult { // Create the ZomeApiGlobals struct with some default values let mut globals = ZomeApiGlobals { diff --git a/core/src/nucleus/ribosome/api/link_entries.rs b/core/src/nucleus/ribosome/api/link_entries.rs index 528fd60503..e041ab834e 100644 --- a/core/src/nucleus/ribosome/api/link_entries.rs +++ b/core/src/nucleus/ribosome/api/link_entries.rs @@ -9,7 +9,7 @@ use std::convert::TryFrom; use wasmi::{RuntimeArgs, RuntimeValue}; /// ZomeApiFunction::LinkEntries function code -/// args: [0] encoded MemoryAllocation as u32 +/// args: [0] encoded MemoryAllocation as u64 /// Expected complex argument: LinkEntriesArgs pub fn invoke_link_entries(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult { // deserialize args diff --git a/core/src/nucleus/ribosome/api/mod.rs b/core/src/nucleus/ribosome/api/mod.rs index ee2a6a4815..7661947684 100644 --- a/core/src/nucleus/ribosome/api/mod.rs +++ b/core/src/nucleus/ribosome/api/mod.rs @@ -220,8 +220,8 @@ pub mod tests { // define the signature as 1 input, 1 output // (import "env" "" // (func $zome_api_function - // (param i32) - // (result i32) + // (param i64) + // (result i64) // ) // ) // @@ -238,10 +238,10 @@ pub mod tests { // (func (export "test") ...) // // define the memory allocation for the memory manager that the serialized input - // struct can be found across as an i32 to the exported function, also the function - // return type is i32 - // (param $allocation i32) - // (result i32) + // struct can be found across as an i64 to the exported function, also the function + // return type is i64 + // (param $allocation i64) + // (result i64) // // call the imported function and pass the exported function arguments straight // through, let the return also fall straight through @@ -255,8 +255,8 @@ pub mod tests { (module (import "env" "{}" (func $zome_api_function - (param i32) - (result i32) + (param i64) + (result i64) ) ) @@ -265,8 +265,8 @@ pub mod tests { (func (export "test") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) (call $zome_api_function @@ -276,61 +276,61 @@ pub mod tests { (func (export "__hdk_validate_app_entry") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const 0) + (i64.const 0) ) (func (export "__hdk_validate_link") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const 0) + (i64.const 0) ) (func (export "__hdk_get_validation_package_for_entry_type") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "Entry" into memory - (i32.store (i32.const 0) (i32.const 34)) - (i32.store (i32.const 1) (i32.const 69)) - (i32.store (i32.const 2) (i32.const 110)) - (i32.store (i32.const 3) (i32.const 116)) - (i32.store (i32.const 4) (i32.const 114)) - (i32.store (i32.const 5) (i32.const 121)) - (i32.store (i32.const 6) (i32.const 34)) - - (i32.const 7) + (i64.store (i32.const 0) (i64.const 34)) + (i64.store (i32.const 1) (i64.const 69)) + (i64.store (i32.const 2) (i64.const 110)) + (i64.store (i32.const 3) (i64.const 116)) + (i64.store (i32.const 4) (i64.const 114)) + (i64.store (i32.const 5) (i64.const 121)) + (i64.store (i32.const 6) (i64.const 34)) + + (i64.const 7) ) (func (export "__hdk_get_validation_package_for_link") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) ;; This writes "Entry" into memory - (i32.store (i32.const 0) (i32.const 34)) - (i32.store (i32.const 1) (i32.const 69)) - (i32.store (i32.const 2) (i32.const 110)) - (i32.store (i32.const 3) (i32.const 116)) - (i32.store (i32.const 4) (i32.const 114)) - (i32.store (i32.const 5) (i32.const 121)) - (i32.store (i32.const 6) (i32.const 34)) - - (i32.const 7) + (i64.store (i32.const 0) (i64.const 34)) + (i64.store (i32.const 1) (i64.const 69)) + (i64.store (i32.const 2) (i64.const 110)) + (i64.store (i32.const 3) (i64.const 116)) + (i64.store (i32.const 4) (i64.const 114)) + (i64.store (i32.const 5) (i64.const 121)) + (i64.store (i32.const 6) (i64.const 34)) + + (i64.const 7) ) (func (export "__list_capabilities") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const 0) + (i64.const 0) ) ) "#, diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index 7f22770550..f0e99667f4 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -14,15 +14,15 @@ use std::{convert::TryFrom, sync::Arc}; use wasmi::{RuntimeArgs, RuntimeValue}; /// ZomeApiFunction::query function code -/// args: [0] encoded MemoryAllocation as u32 +/// args: [0] encoded MemoryAllocation as u64 /// Expected complex argument: ? -/// Returns an HcApiReturnCode as I32 +/// Returns an HcApiReturnCode as I64 /// /// Specify 0 or more simple or "glob" patterns matching EntryType names, returning Vec
. /// /// The empty String or an empty Vec matches all. The '*' glob pattern matches all simple EntryType /// names (with no '/'), while the ** pattern matches everything (use "" or [] for efficiency). -/// +/// /// `[]` /// `[""]` /// `["**"]` diff --git a/core/src/nucleus/ribosome/api/send.rs b/core/src/nucleus/ribosome/api/send.rs index 14cf455cfd..c681bea32b 100644 --- a/core/src/nucleus/ribosome/api/send.rs +++ b/core/src/nucleus/ribosome/api/send.rs @@ -8,9 +8,9 @@ use std::convert::TryFrom; use wasmi::{RuntimeArgs, RuntimeValue}; /// ZomeApiFunction::Send function code -/// args: [0] encoded MemoryAllocation as u32 +/// args: [0] encoded MemoryAllocation as u64 /// Expected complex argument: SendArgs -/// Returns an HcApiReturnCode as I32 +/// Returns an HcApiReturnCode as I64 pub fn invoke_send(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult { // deserialize args let args_str = runtime.load_json_string_from_args(&args); diff --git a/core/src/nucleus/ribosome/api/update_entry.rs b/core/src/nucleus/ribosome/api/update_entry.rs index 18147f0d41..50952b0f43 100644 --- a/core/src/nucleus/ribosome/api/update_entry.rs +++ b/core/src/nucleus/ribosome/api/update_entry.rs @@ -22,9 +22,9 @@ use std::convert::TryFrom; use wasmi::{RuntimeArgs, RuntimeValue}; /// ZomeApiFunction::UpdateEntry function code -/// args: [0] encoded MemoryAllocation as u32 +/// args: [0] encoded MemoryAllocation as u64 /// Expected complex argument: UpdateEntryArgs -/// Returns an HcApiReturnCode as I32 +/// Returns an HcApiReturnCode as I64 pub fn invoke_update_entry(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult { // deserialize args let args_str = runtime.load_json_string_from_args(&args); diff --git a/core/src/nucleus/ribosome/callback/mod.rs b/core/src/nucleus/ribosome/callback/mod.rs index df00738378..ae2aa4f410 100644 --- a/core/src/nucleus/ribosome/callback/mod.rs +++ b/core/src/nucleus/ribosome/callback/mod.rs @@ -255,7 +255,7 @@ pub mod tests { /// generates the wasm to dispatch any zome API function with a single memomry managed runtime /// and bytes argument - pub fn test_callback_wasm(canonical_name: &str, result: i32) -> Vec { + pub fn test_callback_wasm(canonical_name: &str, result: u64) -> Vec { Wat2Wasm::new() .canonicalize_lebs(false) .write_debug_names(true) @@ -274,8 +274,8 @@ pub mod tests { // define the signature as 1 input, 1 output // (import "env" "" // (func $zome_api_function - // (param i32) - // (result i32) + // (param i64) + // (result i64) // ) // ) // @@ -292,10 +292,10 @@ pub mod tests { // (func (export "test") ...) // // define the memory allocation for the memory manager that the serialized input - // struct can be found across as an i32 to the exported function, also the function - // return type is i32 - // (param $allocation i32) - // (result i32) + // struct can be found across as an i64 to the exported function, also the function + // return type is i64 + // (param $allocation i64) + // (result i64) // // call the imported function and pass the exported function arguments straight // through, let the return also fall straight through @@ -313,10 +313,10 @@ pub mod tests { (func (export "{}") - (param $allocation i32) - (result i32) + (param $allocation i64) + (result i64) - (i32.const {}) + (i64.const {}) ) ) "#, @@ -331,7 +331,7 @@ pub mod tests { pub fn test_callback_instance( zome: &str, canonical_name: &str, - result: i32, + result: u64, network_name: Option<&str>, ) -> Result { let dna = test_utils::create_test_dna_with_wasm( diff --git a/core/src/nucleus/ribosome/memory.rs b/core/src/nucleus/ribosome/memory.rs index bcc754344a..f3e35172c5 100644 --- a/core/src/nucleus/ribosome/memory.rs +++ b/core/src/nucleus/ribosome/memory.rs @@ -18,8 +18,8 @@ pub struct SinglePageManager { /// A Memory Manager limited to one wasm memory page that works like a stack. /// With this Memory Manager, the WASM host (i.e. the Ribosome) and WASM module (i.e. the Zome) -/// only need to pass around an i32 to communicate any data. -/// That i32 is the last memory allocation on the stack: +/// only need to pass around an i64 to communicate any data. +/// That u64 is the last memory allocation on the stack: /// it is split in an i16 'offset' in the upper bits and an i16 'length' in the lower bits. /// This fits with the 64KiB sized of a memory Page. /// Complex input arguments should be stored on the latest allocation on the stack. diff --git a/core_types/src/error/error.rs b/core_types/src/error/error.rs index b3e03f5481..f9ecded710 100644 --- a/core_types/src/error/error.rs +++ b/core_types/src/error/error.rs @@ -44,11 +44,6 @@ impl CoreError { line: String::new(), } } - - // TODO - get the u32 error code from a CoreError - // pub fn code(&self) -> u32 { - // u32::from(self.kind.code()) << 16 as u32 - // } } impl ::std::convert::TryFrom for CoreError { diff --git a/core_types/src/error/ribosome_error.rs b/core_types/src/error/ribosome_error.rs index 850ee3a64e..b648544c82 100644 --- a/core_types/src/error/ribosome_error.rs +++ b/core_types/src/error/ribosome_error.rs @@ -33,7 +33,7 @@ impl ToString for RibosomeEncodedAllocation { } /// Represents all possible values passed to/from wasmi functions -/// All wasmi functions are I32 values +/// All wasmi functions are I64 values #[repr(u64)] #[derive(Clone, Debug, PartialEq)] pub enum RibosomeEncodedValue { @@ -143,16 +143,16 @@ impl RibosomeEncodedValue { #[derive(Clone, Debug, PartialEq, Eq, Hash, DefaultJson)] #[rustfmt::skip] pub enum RibosomeErrorCode { - Unspecified = 1 << 16, - ArgumentDeserializationFailed = 2 << 16, - OutOfMemory = 3 << 16, - ReceivedWrongActionResult = 4 << 16, - CallbackFailed = 5 << 16, - RecursiveCallForbidden = 6 << 16, - ResponseSerializationFailed = 7 << 16, - NotAnAllocation = 8 << 16, - ZeroSizedAllocation = 9 << 16, - UnknownEntryType = 10 << 16, + Unspecified = 1 << 32, + ArgumentDeserializationFailed = 2 << 32, + OutOfMemory = 3 << 32, + ReceivedWrongActionResult = 4 << 32, + CallbackFailed = 5 << 32, + RecursiveCallForbidden = 6 << 32, + ResponseSerializationFailed = 7 << 32, + NotAnAllocation = 8 << 32, + ZeroSizedAllocation = 9 << 32, + UnknownEntryType = 10 << 32, } #[rustfmt::skip] @@ -211,6 +211,7 @@ impl From for String { impl RibosomeErrorCode { pub fn from_code_int(code: RibosomeCodeBits) -> Self { + println!("{:?}", code); match code { 0 => unreachable!(), 2 => ArgumentDeserializationFailed, @@ -285,8 +286,10 @@ pub mod tests { #[test] fn ribosome_error_code_round_trip() { + let code_int = RibosomeErrorCode::OutOfMemory as u64; + println!("zz {:?}", code_int); let oom = RibosomeErrorCode::from_code_int( - ((RibosomeErrorCode::OutOfMemory as u64) >> 32) as u32, + ((RibosomeErrorCode::OutOfMemory as u64) >> 32) as RibosomeCodeBits, ); assert_eq!(RibosomeErrorCode::OutOfMemory, oom); assert_eq!(RibosomeErrorCode::OutOfMemory.to_string(), oom.to_string()); diff --git a/doc/holochain_101/src/zome/implementation.md b/doc/holochain_101/src/zome/implementation.md index 8089004ea5..4b1dc4aac9 100644 --- a/doc/holochain_101/src/zome/implementation.md +++ b/doc/holochain_101/src/zome/implementation.md @@ -51,7 +51,7 @@ This function will be called by the invocation dispatch (see above). #### Zome API function arguments The `wasmi::RuntimeArgs` passed to the Zome API function contains only a single -`u32` value. This is an encoded representation of a single page of memory +`u64` value. This is an encoded representation of a single page of memory supported by the memory manager. The 16 high bits are the memory offset and the 16 low bits are the memory length. See the `wasm_utils` crate for more implementation details. @@ -93,7 +93,7 @@ In summary, if you want to send an action and wait for a return value: The zome API function returns a value to wasm representing success or a wasm trap. -The success value can only be a single `i32`. +The success value can only be a single `u64`. Traps are a low level wasm concern and are unlikely to be directly useful to a zome API function implementation. diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index 4c0c982565..4a3dfb2787 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -58,7 +58,7 @@ pub fn create_test_dna_with_wat(zome_name: &str, cap_name: &str, wat: Option<&st (func (export "main") (param $p0 i64) (result i64) i64.const 6 ) - (data (i64.const 0) + (data (i32.const 0) "1337.0" ) (export "memory" (memory 0)) diff --git a/wasm_utils/src/memory/ribosome.rs b/wasm_utils/src/memory/ribosome.rs index 2fb28069b5..7b9eb69c1c 100644 --- a/wasm_utils/src/memory/ribosome.rs +++ b/wasm_utils/src/memory/ribosome.rs @@ -177,7 +177,7 @@ pub mod tests { length: Length::from(8) }), WasmAllocation::try_from(RibosomeEncodedAllocation::from( - 0b00000000000000100_0000000000001000 + 0b000000000000000000000000000000100_00000000000000000000000000001000 )), ); } @@ -185,7 +185,7 @@ pub mod tests { #[test] fn ribosome_allocation_from_allocation_test() { assert_eq!( - RibosomeEncodedAllocation::from(0b0000000000000100_0000000000001000), + RibosomeEncodedAllocation::from(0b00000000000000000000000000000100_00000000000000000000000000001000), RibosomeEncodedAllocation::from(WasmAllocation { offset: Offset::from(4), length: Length::from(8) @@ -197,7 +197,7 @@ pub mod tests { fn ribosome_encoded_value_from_allocation_test() { assert_eq!( RibosomeEncodedValue::Allocation(RibosomeEncodedAllocation::from( - 0b0000000000000100_0000000000001000 + 0b00000000000000000000000000000100_00000000000000000000000000001000 )), RibosomeEncodedValue::from(WasmAllocation { offset: Offset::from(4), @@ -295,7 +295,7 @@ pub mod tests { assert_eq!( Ok(WasmStack { top: Top(4) }), // 2 + 2 = 4 - WasmStack::try_from_ribosome_encoding(0b0000000000000010_0000000000000010), + WasmStack::try_from_ribosome_encoding(0b00000000000000000000000000000010_00000000000000000000000000000010), ); } diff --git a/wasm_utils/wasm-test/integration-test/src/lib.rs b/wasm_utils/wasm-test/integration-test/src/lib.rs index 261592f731..a857804d97 100644 --- a/wasm_utils/wasm-test/integration-test/src/lib.rs +++ b/wasm_utils/wasm-test/integration-test/src/lib.rs @@ -364,7 +364,7 @@ pub extern "C" fn store_json_err(_: RibosomeEncodingBits) -> RibosomeEncodingBit pub extern "C" fn load_json_err(_: RibosomeEncodingBits) -> RibosomeEncodingBits { let mut stack = WasmStack::default(); - let encoded = 1 << 16; + let encoded = 1 << 32; let maybe_test_struct: Result = load_ribosome_encoded_json(encoded); let test_struct = match maybe_test_struct { Ok(test_struct) => test_struct, From 9b00ae0e4a8bf3f5a9c389b4b43079cd0c93e125 Mon Sep 17 00:00:00 2001 From: David Meister Date: Fri, 25 Jan 2019 04:42:31 +1100 Subject: [PATCH 03/10] lint --- container_api/wasm-test/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/container_api/wasm-test/src/lib.rs b/container_api/wasm-test/src/lib.rs index 81d2ec5f09..33148e30d0 100644 --- a/container_api/wasm-test/src/lib.rs +++ b/container_api/wasm-test/src/lib.rs @@ -169,8 +169,7 @@ fn hdk_commit_fail(mem_stack: &mut WasmStack) -> Result { .deallocate(allocation_of_input) .expect("deallocate failed"); - // let address = JsonString::from(result.value).try_into()?; - let address = JsonString::from(result).try_into()?; + let address = JsonString::from(result.value).try_into()?; Ok(address) } From 6c75856ba2c494331c57e5d77219b4393156f368 Mon Sep 17 00:00:00 2001 From: David Meister Date: Fri, 25 Jan 2019 04:51:14 +1100 Subject: [PATCH 04/10] fix u64 tests --- wasm_utils/wasm-test/integration-test/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm_utils/wasm-test/integration-test/src/lib.rs b/wasm_utils/wasm-test/integration-test/src/lib.rs index a857804d97..321a8d699c 100644 --- a/wasm_utils/wasm-test/integration-test/src/lib.rs +++ b/wasm_utils/wasm-test/integration-test/src/lib.rs @@ -73,7 +73,7 @@ pub extern "C" fn store_string(_: RibosomeEncodingBits) -> RibosomeEncodingBits #[no_mangle] pub extern "C" fn store_string_err(_: RibosomeEncodingBits) -> RibosomeEncodingBits { - let allmost_full_alloc = 0b1111111111111101_0000000000000010; + let allmost_full_alloc = 0b11111111111111111111111111111101_00000000000000000000000000000010; let allocation = match WasmAllocation::try_from_ribosome_encoding(allmost_full_alloc) { Ok(allocation) => allocation, @@ -337,7 +337,7 @@ pub extern "C" fn stacked_json_struct(_: RibosomeEncodingBits) -> RibosomeEncodi #[no_mangle] pub extern "C" fn store_json_err(_: RibosomeEncodingBits) -> RibosomeEncodingBits { - let allmost_full_alloc = 0b1111111111111101_0000000000000010; + let allmost_full_alloc = 0b11111111111111111111111111111101_00000000000000000000000000000010; let allocation = match WasmAllocation::try_from_ribosome_encoding(allmost_full_alloc) { Ok(allocation) => allocation, From b40d328a4efffbbf6f8edf915cd1757e890ebb4f Mon Sep 17 00:00:00 2001 From: David Meister Date: Fri, 25 Jan 2019 05:02:13 +1100 Subject: [PATCH 05/10] u64 bits n pieces tests --- core_types/src/bits_n_pieces.rs | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/core_types/src/bits_n_pieces.rs b/core_types/src/bits_n_pieces.rs index baa4dcf5e1..8b8a28f7e6 100644 --- a/core_types/src/bits_n_pieces.rs +++ b/core_types/src/bits_n_pieces.rs @@ -76,4 +76,40 @@ pub mod tests { ); } + #[test] + /// tests that we can extract the high bits from a u64 into the correct u32 + fn u64_high_bits_test() { + assert_eq!( + 0b10101010_10101010_10101010_10101010, + super::u64_high_bits(0b10101010_10101010_10101010_10101010_01010101_01010101_01010101_01010101), + ); + } + + #[test] + /// tests that we can extract the high bits from a u64 into the correct u32 + fn u64_low_bits_test() { + assert_eq!( + 0b01010101_01010101_01010101_01010101, + super::u64_low_bits(0b10101010_10101010_10101010_10101010_01010101_01010101_01010101_01010101), + ); + } + + #[test] + /// tests that we can split a u64 into a tuple of high/low bits + fn u64_split_bits_test() { + assert_eq!( + (0b10101010_10101010_10101010_10101010, 0b01010101_01010101_01010101_01010101), + super::u64_split_bits(0b10101010_10101010_10101010_10101010_01010101_01010101_01010101_01010101), + ); + } + + #[test] + /// tests that we can merge a u32 tuple into a u64 + fn u64_merge_bits_test() { + assert_eq!( + 0b10101010_10101010_10101010_10101010_01010101_01010101_01010101_01010101, + super::u64_merge_bits(0b10101010_10101010_10101010_10101010, 0b01010101_01010101_01010101_01010101), + ); + } + } From 9a57722ad9ade81de21c62346c99fb942386b5ae Mon Sep 17 00:00:00 2001 From: David Meister Date: Fri, 25 Jan 2019 05:04:19 +1100 Subject: [PATCH 06/10] fmt --- .../src/nucleus/ribosome/api/entry_address.rs | 5 +++-- core_types/src/bits_n_pieces.rs | 22 ++++++++++++++----- wasm_utils/src/memory/ribosome.rs | 8 +++++-- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/core/src/nucleus/ribosome/api/entry_address.rs b/core/src/nucleus/ribosome/api/entry_address.rs index d26a546a64..b78dc166a3 100644 --- a/core/src/nucleus/ribosome/api/entry_address.rs +++ b/core/src/nucleus/ribosome/api/entry_address.rs @@ -4,10 +4,10 @@ use holochain_core_types::{ cas::content::AddressableContent, dna::Dna, entry::{entry_type::EntryType, Entry}, + error::RibosomeRuntimeBits, }; use std::{convert::TryFrom, str::FromStr}; use wasmi::{RuntimeArgs, RuntimeValue}; -use holochain_core_types::error::RibosomeRuntimeBits; pub fn get_entry_type(dna: &Dna, entry_type_name: &str) -> Result> { let entry_type = EntryType::from_str(&entry_type_name).map_err(|_| { @@ -21,7 +21,8 @@ pub fn get_entry_type(dna: &Dna, entry_type_name: &str) -> Result Date: Fri, 25 Jan 2019 05:23:40 +1100 Subject: [PATCH 07/10] update changelog for u64 values --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81eaddf24f..0dc33a336a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed ### Added +- Encoded values in ribosome function's input/output are u64 (up from u32) - All structs/values to all HDK functions must implement `Into` and `TryFrom` (derive `DefaultJson` to do this automatically) - HDK globals `AGENT_ADDRESS`, `AGENT_ID_STR`, `DNA_NAME` and `DNA_ADDRESS` are now set to real, correct values. - `hc run` now looks for the --interface flag or `HC_INTERFACE` env var if you want to specify the `http` interface [#846]((https://github.com/holochain/holochain-rust/pull/779) From 24d9c981f7768d3f521d396d90c78b95cbe0b35f Mon Sep 17 00:00:00 2001 From: David Meister Date: Fri, 25 Jan 2019 05:31:30 +1100 Subject: [PATCH 08/10] lint changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc33a336a..95677d1f31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed -### Added - Encoded values in ribosome function's input/output are u64 (up from u32) + +### Added - All structs/values to all HDK functions must implement `Into` and `TryFrom` (derive `DefaultJson` to do this automatically) - HDK globals `AGENT_ADDRESS`, `AGENT_ID_STR`, `DNA_NAME` and `DNA_ADDRESS` are now set to real, correct values. - `hc run` now looks for the --interface flag or `HC_INTERFACE` env var if you want to specify the `http` interface [#846]((https://github.com/holochain/holochain-rust/pull/779) From 509f642e3c9fc230b296bae2c1ed3e92fe0cce4c Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Fri, 25 Jan 2019 06:58:21 +1100 Subject: [PATCH 09/10] Update test_utils/src/lib.rs Co-Authored-By: thedavidmeister --- test_utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index 4a3dfb2787..f2f42c948f 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -66,7 +66,7 @@ pub fn create_test_dna_with_wat(zome_name: &str, cap_name: &str, wat: Option<&st "#; let wat_str = wat.unwrap_or_else(|| &default_wat); - println!("xxx {:?}", wat_str); + // Test WASM code that returns 1337 as integer let wasm_binary = Wat2Wasm::new() From 67a706b90cd8f749504fcfaea3332247a0b755ba Mon Sep 17 00:00:00 2001 From: David Meister Date: Fri, 25 Jan 2019 23:35:24 +1100 Subject: [PATCH 10/10] update mdbook for wasm memory handling --- .../src/writing_development_kit.md | 66 +++++++++++++++++-- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/doc/holochain_101/src/writing_development_kit.md b/doc/holochain_101/src/writing_development_kit.md index e7fadf2e64..a1c9543f0d 100644 --- a/doc/holochain_101/src/writing_development_kit.md +++ b/doc/holochain_101/src/writing_development_kit.md @@ -8,15 +8,71 @@ If you are interested in supporting developers to write Zomes in an unsupported ### Why Development Kits -Development Kits are important because the WASM interface between Zomes and Holochain is really constrained. Because of WASMs design, WASM functions may only be called with 32 bit integers. Holochain implements a solution to this, but if app developers were to always have to interact with this solution directly, it would feel very complex. A Development Kit for each language should ideally be developed so that it gets so much simpler! +Development Kits are important because the WASM interface between Zomes and Holochain is constrained to singular 64 bit integers. -### The Development Kit WASM Solution +The WASM spec allows for multiple function arguments and defines integers as neither signed nor unsigned, but Holochain only supports a single `u64` input and output for all zome functions. -To enable passing arguments more complex than 32 bit integers between Zomes and Holochain, a pattern of utilizing WASM memory is used. When it is running the WASM code for a Zome, Holochain has access to both read and write from the WASM memory. +WASM implements a single linear memory of bytes accessible by offset and length. -The pattern defines that Holochain Zome API functions expect to both give and receive 32 bit integers which actually represent a WASM memory location. So to provide a Holochain Zome API function a complex argument, one must first write it into memory, and then call the function, giving it the memory location. Holochain will pull the argument from memory, execute its behaviour, store the result in memory, and return the memory location of the result. The Zome code then has to *also* lookup the result by its location in memory. +Holochain sends and receives allocated bytes of memory to zomes by treating the 64 bit integer as two 32 bit integers (high bits as offset and low bits as length). -Technically, an app developer can do all of these things if they have a reason to, but most won't want to handle the extra step involving memory. A Development Kit, then, should handle the extra step of writing to memory, and calling the native API function, and reading the result from memory, and returning that instead. Plus a few other sprinkles on top. +If no bytes of memory are allocated (i.e. the 32 bit length is 0) the high bits map to an internal enum. This enum is contextual to the zome but typically represents errors: + +```rust +pub enum RibosomeErrorCode { + Unspecified = 1 << 32, + ArgumentDeserializationFailed = 2 << 32, + OutOfMemory = 3 << 32, + ReceivedWrongActionResult = 4 << 32, + CallbackFailed = 5 << 32, + RecursiveCallForbidden = 6 << 32, + ResponseSerializationFailed = 7 << 32, + NotAnAllocation = 8 << 32, + ZeroSizedAllocation = 9 << 32, + UnknownEntryType = 10 << 32, +} +``` + +Each development kit should abstract memory handling in some contextually idiomatic way. + +### The Rust Development Kit WASM Solution + +The standard development kit implements a simple memory stack. + +The `WasmAllocation` struct represents a pair of offset/length `u32` values. + +The `WasmStack` struct is a single "top" `u32` value that tracks the current end of linear memory that can be written to (either allocation or deallocation). + +Use of these structs is optional inside zome WASM, Holochain core will always write/read according to the input/output position represented by the `u64` arg/return values. + +Reads and write methods are provided for both primitive Rust UTF-8 strings and `JsonString` structs. + +Write new data to `WasmStack` as `stack.write_string(String)` and `stack.write_json(Into)`. + +If the allocation is successful a `WasmAllocation` will be returned else an `AllocationError` will result. + +Allocation to the stack can be handled manually as `stack.allocate(allocation)` and the next allocation can be built with `stack.next_allocation(length)`. + +Allocation on the stack will fail if the offset of the new allocation does not match the current stack top value. + +To read a previous write call `let s = allocation.read_to_string()` and standard `let foo: Result = JsonString::try_from(s)` for JSON deserialization. + +To write a deallocation call `stack.deallocate(allocation)`. + +Deallocation does not clear out WASM memory, it simply moves the top of the stack back to the start of the passed allocation ready to be overwritten by the next allocation. + +Deallocation will fail if the allocation offset + length does not equal the current stack top. + +Holochain compatible encodings of allocations for the return value of zome functions can be generated with `allocation.as_ribosome_encoding()`. + +The development kit: + +- Implements the simple stack/allocation structs and methods +- Manages a static stack for consistent writing +- Exposes convenience functions for the Holochain API to handle relevant allocation/deallocations +- Maps `u64` values to/from encoded error values and `u32` offset/length values for memory allocations + +For more details review the unit/integration test suites in `hdk-rust` and `wasm_utils`. ### Crafting the API