Introduce module interface cache (#40)

This commit is contained in:
vms
2020-11-05 20:58:13 +03:00
committed by GitHub
parent d09c5b5d73
commit ddd3448af7
12 changed files with 212 additions and 137 deletions

6
Cargo.lock generated
View File

@ -652,6 +652,7 @@ version = "0.1.10"
dependencies = [ dependencies = [
"boolinator", "boolinator",
"bytes", "bytes",
"fce-utils",
"fce-wit-interfaces", "fce-wit-interfaces",
"fce-wit-parser", "fce-wit-parser",
"log", "log",
@ -679,6 +680,10 @@ dependencies = [
"fluence 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "fluence 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "fce-utils"
version = "0.1.0"
[[package]] [[package]]
name = "fce-wit-generator" name = "fce-wit-generator"
version = "0.1.10" version = "0.1.10"
@ -764,6 +769,7 @@ dependencies = [
"cmd_lib", "cmd_lib",
"env_logger 0.7.1", "env_logger 0.7.1",
"fce", "fce",
"fce-utils",
"fluence-sdk-main 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "fluence-sdk-main 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools", "itertools",
"log", "log",

View File

@ -1,6 +1,7 @@
[workspace] [workspace]
members = [ members = [
"aquamarine-vm", "aquamarine-vm",
"crates/utils",
"crates/wit-generator", "crates/wit-generator",
"crates/wit-interfaces", "crates/wit-interfaces",
"crates/wit-parser", "crates/wit-parser",

11
crates/utils/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "fce-utils"
description = "Fluence FCE utils crate"
version = "0.1.0"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
[lib]
name = "fce_utils"
path = "src/lib.rs"

36
crates/utils/src/lib.rs Normal file
View File

@ -0,0 +1,36 @@
/*
* 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.
*/
#![warn(rust_2018_idioms)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
use std::rc::Rc;
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
pub struct SharedString(pub Rc<String>);
impl std::borrow::Borrow<str> for SharedString {
fn borrow(&self) -> &str {
self.0.as_str()
}
}

View File

@ -24,7 +24,7 @@ use fluence_sdk_wit::RustType;
// return error if there is no record with such name // return error if there is no record with such name
pub(crate) fn ptype_to_itype_checked( pub(crate) fn ptype_to_itype_checked(
pty: &ParsedType, pty: &ParsedType,
wit_resolver: &mut WITResolver, wit_resolver: &mut WITResolver<'_>,
) -> Result<IType> { ) -> Result<IType> {
match pty { match pty {
ParsedType::I8 => Ok(IType::S8), ParsedType::I8 => Ok(IType::S8),
@ -50,7 +50,10 @@ pub(crate) fn ptype_to_itype_checked(
} }
} }
pub(crate) fn ptype_to_itype_unchecked(pty: &ParsedType, wit_resolver: &mut WITResolver) -> IType { pub(crate) fn ptype_to_itype_unchecked(
pty: &ParsedType,
wit_resolver: &mut WITResolver<'_>,
) -> IType {
match pty { match pty {
ParsedType::I8 => IType::S8, ParsedType::I8 => IType::S8,
ParsedType::I16 => IType::S16, ParsedType::I16 => IType::S16,

View File

@ -96,7 +96,7 @@ fn generate_interfaces(module_ast: &ModuleAST) -> Result<Interfaces<'_>> {
Ok(wit_resolver.interfaces) Ok(wit_resolver.interfaces)
} }
fn generate_default_export_api(interfaces: &mut Interfaces) { fn generate_default_export_api(interfaces: &mut Interfaces<'_>) {
// TODO: the order is matter // TODO: the order is matter
ALLOCATE_FUNC.update_interfaces(interfaces); ALLOCATE_FUNC.update_interfaces(interfaces);
DEALLOCATE_FUNC.update_interfaces(interfaces); DEALLOCATE_FUNC.update_interfaces(interfaces);
@ -106,13 +106,13 @@ fn generate_default_export_api(interfaces: &mut Interfaces) {
SET_RESULT_PTR_FUNC.update_interfaces(interfaces); SET_RESULT_PTR_FUNC.update_interfaces(interfaces);
} }
fn validate_records(wit_resolver: &WITResolver) -> Result<()> { fn validate_records(wit_resolver: &WITResolver<'_>) -> Result<()> {
const TYPE_RESOLVE_RECURSION_LIMIT: u32 = 1024; const TYPE_RESOLVE_RECURSION_LIMIT: u32 = 1024;
fn validate_record_type( fn validate_record_type(
record_type: &wasmer_wit::types::RecordType, record_type: &wasmer_wit::types::RecordType,
recursion_level: u32, recursion_level: u32,
wit_resolver: &WITResolver, wit_resolver: &WITResolver<'_>,
) -> Result<()> { ) -> Result<()> {
if recursion_level >= TYPE_RESOLVE_RECURSION_LIMIT { if recursion_level >= TYPE_RESOLVE_RECURSION_LIMIT {
return Err(WITGeneratorError::CorruptedRecord(String::from( return Err(WITGeneratorError::CorruptedRecord(String::from(

View File

@ -14,6 +14,17 @@
* limitations under the License. * limitations under the License.
*/ */
#![warn(rust_2018_idioms)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
mod default_export_api_config; mod default_export_api_config;
mod errors; mod errors;
mod instructions_generator; mod instructions_generator;

View File

@ -13,6 +13,7 @@ path = "src/lib.rs"
[dependencies] [dependencies]
fce-wit-interfaces = { path = "../crates/wit-interfaces", version = "0.1.7" } fce-wit-interfaces = { path = "../crates/wit-interfaces", version = "0.1.7" }
fce-wit-parser = { path = "../crates/wit-parser", version = "0.1.9" } fce-wit-parser = { path = "../crates/wit-parser", version = "0.1.9" }
fce-utils = { path = "../crates/utils", version = "0.1.0" }
wasmer-runtime = { package = "wasmer-runtime-fl", version = "0.17.0" } wasmer-runtime = { package = "wasmer-runtime-fl", version = "0.17.0" }
# dynamicfunc-fat-closures allows using state inside DynamicFunc # dynamicfunc-fat-closures allows using state inside DynamicFunc

View File

@ -22,6 +22,7 @@ use crate::FCEModuleConfig;
use fce_wit_interfaces::FCEWITInterfaces; use fce_wit_interfaces::FCEWITInterfaces;
use fce_wit_parser::extract_wit; use fce_wit_parser::extract_wit;
use fce_utils::SharedString;
use wasmer_core::Instance as WasmerInstance; use wasmer_core::Instance as WasmerInstance;
use wasmer_core::import::Namespace; use wasmer_core::import::Namespace;
use wasmer_runtime::compile; use wasmer_runtime::compile;
@ -73,15 +74,6 @@ impl Callable {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
struct SharedString(pub Rc<String>);
impl std::borrow::Borrow<str> for SharedString {
fn borrow(&self) -> &str {
self.0.as_str()
}
}
type ExportFunctions = HashMap<SharedString, Rc<Callable>>; type ExportFunctions = HashMap<SharedString, Rc<Callable>>;
pub(crate) struct FCEModule { pub(crate) struct FCEModule {

View File

@ -8,6 +8,7 @@ edition = "2018"
[dependencies] [dependencies]
fce = { path = "../engine", version = "0.1.10" } fce = { path = "../engine", version = "0.1.10" }
fce-utils = { path = "../crates/utils", version = "0.1.0" }
fluence-sdk-main = "=0.2.8" fluence-sdk-main = "=0.2.8"
wasmer-runtime = { package = "wasmer-runtime-fl", version = "0.17.0" } wasmer-runtime = { package = "wasmer-runtime-fl", version = "0.17.0" }

View File

@ -19,10 +19,14 @@ use crate::faas_interface::FaaSInterface;
use crate::FaaSError; use crate::FaaSError;
use crate::Result; use crate::Result;
use crate::IValue; use crate::IValue;
use crate::IType;
use crate::misc::load_modules_from_fs; use crate::misc::load_modules_from_fs;
use crate::misc::ModulesLoadStrategy; use crate::misc::ModulesLoadStrategy;
use fce::FCE; use fce::FCE;
use fce::IFunctionArg;
use fce_utils::SharedString;
use fce::RecordTypes;
use fluence_sdk_main::CallParameters; use fluence_sdk_main::CallParameters;
use serde_json::Value as JValue; use serde_json::Value as JValue;
@ -33,6 +37,11 @@ use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use std::path::PathBuf; use std::path::PathBuf;
struct ModuleInterface {
function_signatures: HashMap<SharedString, (Rc<Vec<IFunctionArg>>, Rc<Vec<IType>>)>,
record_types: Rc<RecordTypes>,
}
// TODO: remove and use mutex instead // TODO: remove and use mutex instead
unsafe impl Send for FluenceFaaS {} unsafe impl Send for FluenceFaaS {}
@ -42,6 +51,9 @@ pub struct FluenceFaaS {
/// Parameters of call accessible by Wasm modules. /// Parameters of call accessible by Wasm modules.
call_parameters: Rc<RefCell<CallParameters>>, call_parameters: Rc<RefCell<CallParameters>>,
/// Cached module interfaces by names.
module_interfaces_cache: HashMap<String, ModuleInterface>,
} }
impl FluenceFaaS { impl FluenceFaaS {
@ -99,6 +111,7 @@ impl FluenceFaaS {
Ok(Self { Ok(Self {
fce, fce,
call_parameters, call_parameters,
module_interfaces_cache: HashMap::new(),
}) })
} }
@ -148,27 +161,18 @@ impl FluenceFaaS {
let module_name = module_name.as_ref(); let module_name = module_name.as_ref();
let func_name = func_name.as_ref(); let func_name = func_name.as_ref();
// TODO: cache module interface let (func_signature, output_types, record_types) =
let module_interface = self self.lookup_module_interface(module_name, func_name)?;
.fce let iargs = json_to_ivalues(
.module_interface(module_name) json_args,
.ok_or_else(|| FaaSError::NoSuchModule(module_name.to_string()))?; func_signature.iter().map(|arg| (&arg.name, &arg.ty)),
&record_types,
let func_signature = module_interface )?;
.function_signatures
.iter()
.find(|sign| sign.name.as_str() == func_name)
.ok_or_else(|| FaaSError::MissingFunctionError(func_name.to_string()))?;
let record_types = module_interface.record_types.clone();
let iargs = json_to_ivalues(json_args, func_signature, &record_types)?;
let outputs = func_signature.outputs.clone();
self.call_parameters.replace(call_parameters); self.call_parameters.replace(call_parameters);
let result = self.fce.call(module_name, func_name, &iargs)?; let result = self.fce.call(module_name, func_name, &iargs)?;
ivalues_to_json(result, &outputs, &record_types) ivalues_to_json(result, &output_types, &record_types)
} }
/// Return all export functions (name and signatures) of loaded modules. /// Return all export functions (name and signatures) of loaded modules.
@ -177,6 +181,59 @@ impl FluenceFaaS {
FaaSInterface { modules } FaaSInterface { modules }
} }
/// At first, tries to find function signature and record types in module_interface_cache,
/// if there is no them, tries to look
fn lookup_module_interface<'faas>(
&'faas mut self,
module_name: &str,
func_name: &str,
) -> Result<(Rc<Vec<IFunctionArg>>, Rc<Vec<IType>>, Rc<RecordTypes>)> {
use FaaSError::NoSuchModule;
use FaaSError::MissingFunctionError;
if let Some(module_interface) = self.module_interfaces_cache.get(module_name) {
if let Some(function) = module_interface.function_signatures.get(func_name) {
return Ok((
function.0.clone(),
function.1.clone(),
module_interface.record_types.clone(),
));
}
return Err(MissingFunctionError(func_name.to_string()));
}
let module_interface = self
.fce
.module_interface(module_name)
.ok_or_else(|| NoSuchModule(module_name.to_string()))?;
let function_signatures = module_interface
.function_signatures
.iter()
.cloned()
.map(|f| (SharedString(f.name), (f.arguments, f.outputs)))
.collect::<HashMap<_, _>>();
let (arg_types, output_types) = function_signatures
.get(func_name)
.ok_or_else(|| MissingFunctionError(func_name.to_string()))?;
let arg_types = arg_types.clone();
let output_types = output_types.clone();
let record_types = Rc::new(module_interface.record_types.clone());
let module_interface = ModuleInterface {
function_signatures,
record_types: record_types.clone(),
};
self.module_interfaces_cache
.insert(func_name.to_string(), module_interface);
Ok((arg_types, output_types, record_types))
}
} }
// This API is intended for testing purposes (mostly in FCE REPL) // This API is intended for testing purposes (mostly in FCE REPL)

View File

@ -24,47 +24,39 @@ use serde_json::Value as JValue;
use wasmer_wit::vec1::Vec1; use wasmer_wit::vec1::Vec1;
use std::collections::HashMap; use std::collections::HashMap;
use std::iter::ExactSizeIterator;
pub(crate) fn json_to_ivalues( /// Convert json to an array of ivalues according to the supplied argument types.
pub(crate) fn json_to_ivalues<'a, 'b>(
json_args: JValue, json_args: JValue,
func_signature: &crate::FaaSFunctionSignature, arg_types: impl Iterator<Item = (&'a String, &'a IType)> + ExactSizeIterator,
record_types: &RecordTypes, record_types: &'b RecordTypes,
) -> Result<Vec<IValue>> { ) -> Result<Vec<IValue>> {
let ivalues = match json_args { let ivalues = match json_args {
JValue::Object(json_map) => json_map_to_ivalues( JValue::Object(json_map) => json_map_to_ivalues(json_map, arg_types, &record_types)?,
json_map, JValue::Array(json_array) => {
func_signature json_array_to_ivalues(json_array, arg_types.map(|arg| arg.1), &record_types)?
.arguments }
.iter() JValue::Null => json_null_to_ivalue(arg_types)?,
.map(|arg| (&arg.name, &arg.ty)), json_value => json_value_to_ivalue(json_value, arg_types)?,
&record_types,
)?,
JValue::Array(json_array) => json_array_to_ivalues(
json_array,
func_signature.arguments.iter().map(|arg| &arg.ty),
&record_types,
)?,
JValue::String(json_string) => json_string_to_ivalue(json_string, func_signature)?,
json_bool @ JValue::Bool(_) => json_bool_to_ivalue(json_bool, func_signature)?,
json_number @ JValue::Number(_) => json_number_to_ivalue(json_number, func_signature)?,
JValue::Null => json_null_to_ivalue(func_signature)?,
}; };
Ok(ivalues) Ok(ivalues)
} }
/// Convert json map to an array of ivalues according to the supplied argument types.
fn json_map_to_ivalues<'a, 'b>( fn json_map_to_ivalues<'a, 'b>(
mut json_map: serde_json::Map<String, JValue>, mut json_map: serde_json::Map<String, JValue>,
signature: impl Iterator<Item = (&'a String, &'a IType)>, arg_types: impl Iterator<Item = (&'a String, &'a IType)>,
record_types: &'b RecordTypes, record_types: &'b RecordTypes,
) -> Result<Vec<IValue>> { ) -> Result<Vec<IValue>> {
let mut iargs = Vec::new(); let mut iargs = Vec::new();
for (arg_name, arg_type) in signature { for (arg_name, arg_type) in arg_types {
let json_value = json_map let json_value = json_map
.remove(arg_name) .remove(arg_name)
.ok_or_else(|| ArgDeError(format!("missing argument with name {}", arg_name)))?; .ok_or_else(|| ArgDeError(format!("missing argument with name {}", arg_name)))?;
let iarg = json_value_to_ivalue(json_value, arg_type, record_types)?; let iarg = jvalue_to_ivalue(json_value, arg_type, record_types)?;
iargs.push(iarg); iargs.push(iarg);
} }
@ -79,97 +71,61 @@ fn json_map_to_ivalues<'a, 'b>(
Ok(iargs) Ok(iargs)
} }
/// Convert json array to an array of ivalues according to the supplied argument types.
fn json_array_to_ivalues<'a, 'b>( fn json_array_to_ivalues<'a, 'b>(
mut json_array: Vec<JValue>, json_array: Vec<JValue>,
signature: impl Iterator<Item = &'a IType> + std::iter::ExactSizeIterator, arg_types: impl Iterator<Item = &'a IType> + ExactSizeIterator,
record_types: &'b RecordTypes, record_types: &'b RecordTypes,
) -> Result<Vec<IValue>> { ) -> Result<Vec<IValue>> {
if json_array.len() != signature.len() { if json_array.len() != arg_types.len() {
return Err(ArgDeError(format!( return Err(ArgDeError(format!(
"function requires {} arguments, {} provided", "function requires {} arguments, {} provided",
signature.len(), arg_types.len(),
json_array.len() json_array.len()
))); )));
} }
let mut iargs = Vec::with_capacity(signature.len()); let iargs = json_array
.into_iter()
for arg_type in signature { .zip(arg_types)
// remove here is safe because we've already checked sizes .map(|(json_value, arg_type)| jvalue_to_ivalue(json_value, arg_type, record_types))
let json_value = json_array.remove(0); .collect::<Result<Vec<_>>>()?;
let iarg = json_value_to_ivalue(json_value, arg_type, record_types)?;
iargs.push(iarg);
}
Ok(iargs) Ok(iargs)
} }
fn json_string_to_ivalue( /// Convert json value (Number, String or Bool) to an array of ivalues according to the supplied argument types.
json_string: String, fn json_value_to_ivalue<'a>(
func_signature: &fce::FCEFunctionSignature, json_value: JValue,
mut arg_types: impl Iterator<Item = (&'a String, &'a IType)> + ExactSizeIterator,
) -> Result<Vec<IValue>> { ) -> Result<Vec<IValue>> {
if func_signature.arguments.len() != 1 || func_signature.arguments[0].ty != IType::String { if arg_types.len() != 1 {
return Err(ArgDeError(format!( return Err(ArgDeError(format!(
"the called function has the following signature: {:?}, but only one string argument is provided", "the called function has the following signature: {:?}, but only one string argument is provided",
func_signature arg_types.collect::<Vec<_>>()
))); )));
} }
Ok(vec![IValue::String(json_string)]) let ivalue = jvalue_to_ivalue(json_value, arg_types.next().unwrap().1, &HashMap::new())?;
Ok(vec![ivalue])
} }
fn json_bool_to_ivalue( /// Convert json Null to an empty array of ivalues.
json_bool: JValue, fn json_null_to_ivalue<'a>(
func_signature: &fce::FCEFunctionSignature, arg_types: impl Iterator<Item = (&'a String, &'a IType)> + ExactSizeIterator,
) -> Result<Vec<IValue>> { ) -> Result<Vec<IValue>> {
if func_signature.arguments.len() != 1 { if arg_types.len() != 0 {
return Err(ArgDeError(format!(
"the called function has the following signature: {:?}, but only one bool argument is provided",
func_signature
)));
}
Ok(vec![json_value_to_ivalue(
json_bool,
&func_signature.arguments[0].ty,
&HashMap::new(),
)?])
}
fn json_number_to_ivalue(
json_number: JValue,
func_signature: &fce::FCEFunctionSignature,
) -> Result<Vec<IValue>> {
if func_signature.arguments.len() != 1 {
return Err(ArgDeError(format!(
"the called function has the following signature: {:?}, but only one number argument is provided",
func_signature
)));
}
Ok(vec![json_value_to_ivalue(
json_number,
&func_signature.arguments[0].ty,
&HashMap::new(),
)?])
}
fn json_null_to_ivalue(func_signature: &fce::FCEFunctionSignature) -> Result<Vec<IValue>> {
if !func_signature.arguments.is_empty() {
return Err(ArgDeError(format!( return Err(ArgDeError(format!(
"the called function has the following signature: {:?}, but no arguments is provided", "the called function has the following signature: {:?}, but no arguments is provided",
func_signature arg_types.collect::<Vec<_>>()
))); )));
} }
Ok(vec![]) Ok(vec![])
} }
fn json_value_to_ivalue( /// Convert one JValue to an array of ivalues according to the supplied argument type.
json_value: JValue, fn jvalue_to_ivalue(jvalue: JValue, ty: &IType, record_types: &RecordTypes) -> Result<IValue> {
ty: &IType,
record_types: &RecordTypes,
) -> Result<IValue> {
macro_rules! to_ivalue( macro_rules! to_ivalue(
($json_value:expr, $ty:ident) => { ($json_value:expr, $ty:ident) => {
{ {
@ -186,41 +142,39 @@ fn json_value_to_ivalue(
); );
match ty { match ty {
IType::S8 => to_ivalue!(json_value, S8), IType::S8 => to_ivalue!(jvalue, S8),
IType::S16 => to_ivalue!(json_value, S16), IType::S16 => to_ivalue!(jvalue, S16),
IType::S32 => to_ivalue!(json_value, S32), IType::S32 => to_ivalue!(jvalue, S32),
IType::S64 => to_ivalue!(json_value, S64), IType::S64 => to_ivalue!(jvalue, S64),
IType::U8 => to_ivalue!(json_value, U8), IType::U8 => to_ivalue!(jvalue, U8),
IType::U16 => to_ivalue!(json_value, U16), IType::U16 => to_ivalue!(jvalue, U16),
IType::U32 => to_ivalue!(json_value, U32), IType::U32 => to_ivalue!(jvalue, U32),
IType::U64 => to_ivalue!(json_value, U64), IType::U64 => to_ivalue!(jvalue, U64),
IType::F32 => to_ivalue!(json_value, F32), IType::F32 => to_ivalue!(jvalue, F32),
IType::F64 => to_ivalue!(json_value, F64), IType::F64 => to_ivalue!(jvalue, F64),
IType::String => to_ivalue!(json_value, String), IType::String => to_ivalue!(jvalue, String),
IType::Array(value_type) => { IType::Array(value_type) => {
let value = match json_value { let value = match jvalue {
JValue::Array(json_array) => { JValue::Array(json_array) => {
let iargs: Result<Vec<_>> = json_array let iargs = json_array
.into_iter() .into_iter()
.map(|json_value| { .map(|json_value| jvalue_to_ivalue(json_value, value_type, record_types))
json_value_to_ivalue(json_value, value_type, record_types) .collect::<Result<Vec<_>>>()?;
})
.collect();
Ok(iargs?) Ok(iargs)
} }
_ => Err(ArgDeError(format!( _ => Err(ArgDeError(format!(
"expected array of {:?} types, got {:?}", "expected array of {:?} types, got {:?}",
value_type, json_value value_type, jvalue
))), ))),
}?; }?;
Ok(IValue::Array(value)) Ok(IValue::Array(value))
} }
IType::I32 => to_ivalue!(json_value, I32), IType::I32 => to_ivalue!(jvalue, I32),
IType::I64 => to_ivalue!(json_value, I64), IType::I64 => to_ivalue!(jvalue, I64),
IType::Record(record_type_id) => { IType::Record(record_type_id) => {
let value = json_record_type_to_ivalue(json_value, record_type_id, &record_types)?; let value = json_record_type_to_ivalue(jvalue, record_type_id, &record_types)?;
Ok(IValue::Record(value)) Ok(IValue::Record(value))
} }
IType::Anyref => Err(ArgDeError(String::from("anyrefs aren't supported now"))), IType::Anyref => Err(ArgDeError(String::from("anyrefs aren't supported now"))),
@ -228,6 +182,8 @@ fn json_value_to_ivalue(
} }
#[allow(clippy::ptr_arg)] #[allow(clippy::ptr_arg)]
/// Convert JValue of array or object types to an IValue record type.
// TODO: after introducing new Record type wrapper change the result type
fn json_record_type_to_ivalue( fn json_record_type_to_ivalue(
json_value: JValue, json_value: JValue,
record_type_id: &u64, record_type_id: &u64,