fn canonical_key_convert_target(key_type: &ValueType) -> Option<ir::ConvertTarget> {
match key_type {
ValueType::Integer { .. } => Some(ir::ConvertTarget::Integer),
ValueType::Boolean => Some(ir::ConvertTarget::Boolean),
ValueType::Address | ValueType::String | ValueType::ByteArray { .. } => {
Some(ir::ConvertTarget::ByteArray)
}
ValueType::Array(_)
| ValueType::Mapping { .. }
| ValueType::Struct { .. }
| ValueType::Any => None,
}
}
fn emit_serialize_key(
bytecode: &mut Vec<u8>,
key_type: &ValueType,
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
if let Some(target) = canonical_key_convert_target(key_type) {
emit_convert(bytecode, target);
}
emit_native_contract_call(
bytecode,
ir::NativeContract::StdLib,
"serialize",
1,
use_callt,
token_patches,
);
}
fn emit_mapping_slot(
bytecode: &mut Vec<u8>,
module: &ir::Module,
state_index: usize,
key_types: &[ValueType],
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
let base_slot = module
.state_variables
.get(state_index)
.map(|state| state.storage_key.clone())
.unwrap_or_else(|| vec![0u8; 32]);
push_data(bytecode, &base_slot);
for key_type in key_types {
bytecode.push(0x50); emit_serialize_key(bytecode, key_type, use_callt, token_patches);
bytecode.push(0x50); bytecode.push(0x8B); emit_native_contract_call(
bytecode,
ir::NativeContract::CryptoLib,
"keccak256",
1,
use_callt,
token_patches,
);
}
}
fn emit_struct_field_slot(
bytecode: &mut Vec<u8>,
module: &ir::Module,
state_index: usize,
key_types: &[ValueType],
field_keys: &[[u8; 32]],
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
emit_mapping_slot(
bytecode,
module,
state_index,
key_types,
use_callt,
token_patches,
);
for field_key in field_keys {
push_data(bytecode, field_key);
bytecode.push(0x50); bytecode.push(0x8B); emit_native_contract_call(
bytecode,
ir::NativeContract::CryptoLib,
"keccak256",
1,
use_callt,
token_patches,
);
}
}
fn emit_load_mapping(
bytecode: &mut Vec<u8>,
module: &ir::Module,
state_index: usize,
key_types: &[ValueType],
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
emit_mapping_slot(
bytecode,
module,
state_index,
key_types,
use_callt,
token_patches,
);
emit_syscall(bytecode, "System.Storage.GetContext");
emit_syscall(bytecode, "System.Storage.Get");
if let Some(value_type) =
resolve_loaded_mapping_value_type(module, state_index, key_types.len())
{
emit_coerce_storage_value(bytecode, value_type);
}
}
fn resolve_loaded_mapping_value_type(
module: &ir::Module,
state_index: usize,
key_depth: usize,
) -> Option<&ValueType> {
let mut current = &module.state_variables.get(state_index)?.ty;
for _ in 0..key_depth {
match current {
ValueType::Mapping { value, .. } => {
current = value.as_ref();
}
ValueType::Array(element) => {
current = element.as_ref();
}
_ => return Some(current),
}
}
Some(current)
}
fn emit_store_mapping(
bytecode: &mut Vec<u8>,
module: &ir::Module,
state_index: usize,
key_types: &[ValueType],
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
emit_mapping_slot(
bytecode,
module,
state_index,
key_types,
use_callt,
token_patches,
);
emit_syscall(bytecode, "System.Storage.GetContext");
emit_syscall(bytecode, "System.Storage.Put");
}
fn emit_store_mapping_array_deep_copy(
bytecode: &mut Vec<u8>,
module: &ir::Module,
state_index: usize,
key_types: &[ValueType],
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
emit_mapping_slot(
bytecode,
module,
state_index,
key_types,
use_callt,
token_patches,
);
bytecode.push(0x50); emit_store_array_value_deep_copy(bytecode, use_callt, token_patches);
}
fn emit_store_array_value_deep_copy(
bytecode: &mut Vec<u8>,
use_callt: bool,
token_patches: &mut Vec<MethodTokenPatch>,
) {
bytecode.push(0x4A); bytecode.push(0xCA);
let loop_start_pos = bytecode.len();
bytecode.push(0x4A); bytecode.push(0x10); bytecode.push(0x97); let jmp_exit_pos = bytecode.len();
bytecode.push(0x25); let jmp_exit_operand = bytecode.len();
bytecode.extend_from_slice(&[0, 0, 0, 0]);
bytecode.push(0x9D);
bytecode.push(0x4A); emit_serialize_key(
bytecode,
&ValueType::Integer {
signed: false,
bits: 256,
},
use_callt,
token_patches,
);
bytecode.push(0x13); bytecode.push(0x4D); bytecode.push(0x8B); emit_native_contract_call(
bytecode,
ir::NativeContract::CryptoLib,
"keccak256",
1,
use_callt,
token_patches,
);
bytecode.push(0x12); bytecode.push(0x4D); bytecode.push(0x12); bytecode.push(0x4D); bytecode.push(0xCE);
bytecode.push(0x50); emit_syscall(bytecode, "System.Storage.GetContext");
emit_syscall(bytecode, "System.Storage.Put");
let jmp_back_pos = bytecode.len();
bytecode.push(0x23); let jmp_back_operand = bytecode.len();
bytecode.extend_from_slice(&[0, 0, 0, 0]);
let rel_back = (loop_start_pos as i32)
.checked_sub(jmp_back_pos as i32)
.unwrap_or(0);
bytecode[jmp_back_operand..jmp_back_operand + 4].copy_from_slice(&rel_back.to_le_bytes());
let exit_pos = bytecode.len();
let rel_exit = (exit_pos as i32)
.checked_sub(jmp_exit_pos as i32)
.unwrap_or(0);
bytecode[jmp_exit_operand..jmp_exit_operand + 4].copy_from_slice(&rel_exit.to_le_bytes());
bytecode.push(0x45); bytecode.push(0xCA); bytecode.push(0x50); emit_syscall(bytecode, "System.Storage.GetContext");
emit_syscall(bytecode, "System.Storage.Put");
}
struct StructFieldMappingSlot<'a> {
module: &'a ir::Module,
state_index: usize,
key_types: &'a [ValueType],
field_keys: &'a [[u8; 32]],
trailing_key_types: &'a [ValueType],
use_callt: bool,
}
fn emit_struct_field_mapping_slot(
bytecode: &mut Vec<u8>,
slot: &StructFieldMappingSlot<'_>,
token_patches: &mut Vec<MethodTokenPatch>,
) {
emit_struct_field_slot(
bytecode,
slot.module,
slot.state_index,
slot.key_types,
slot.field_keys,
slot.use_callt,
token_patches,
);
for key_type in slot.trailing_key_types {
bytecode.push(0x50); emit_serialize_key(bytecode, key_type, slot.use_callt, token_patches);
bytecode.push(0x50); bytecode.push(0x8B); emit_native_contract_call(
bytecode,
ir::NativeContract::CryptoLib,
"keccak256",
1,
slot.use_callt,
token_patches,
);
}
}
fn emit_load_struct_field_mapping_element(
bytecode: &mut Vec<u8>,
slot: &StructFieldMappingSlot<'_>,
value_type: &ValueType,
token_patches: &mut Vec<MethodTokenPatch>,
) {
emit_struct_field_mapping_slot(bytecode, slot, token_patches);
emit_syscall(bytecode, "System.Storage.GetContext");
emit_syscall(bytecode, "System.Storage.Get");
emit_coerce_storage_value(bytecode, value_type);
}
fn emit_store_struct_field_mapping_element(
bytecode: &mut Vec<u8>,
slot: &StructFieldMappingSlot<'_>,
token_patches: &mut Vec<MethodTokenPatch>,
) {
emit_struct_field_mapping_slot(bytecode, slot, token_patches);
emit_syscall(bytecode, "System.Storage.GetContext");
emit_syscall(bytecode, "System.Storage.Put");
}