-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(zink): introduce contract constructor (#171)
* feat(zink): introduce proc-macro constructor * chore(compiler): clean the parameters of compiler * feat(compiler): switch from runtime bytecode * feat(codegen): compile constructor * feat(examples): introduce tests for example storage * feat(codegen): relocate return instructions in constructor
- Loading branch information
Showing
18 changed files
with
407 additions
and
175 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
//! Contract constructor. | ||
use crate::{Buffer, CodeGen, Function, JumpTable, MacroAssembler, Result, ToLSBytes}; | ||
|
||
/// Contract constructor. | ||
/// | ||
/// # Bytecode | ||
/// - `CREATE` instruction | ||
/// - `INIT_CODE` | ||
/// - `INIT_LOGIC` | ||
/// - `RETURN RUNTIME_BYTECODE` | ||
/// - `RUNTIME_BYTECODE` | ||
pub struct Constructor { | ||
/// Code buffer. | ||
pub masm: MacroAssembler, | ||
|
||
/// Code generator. | ||
pub init_code: Buffer, | ||
|
||
/// Runtime bytecode. | ||
pub runtime_bytecode: Buffer, | ||
} | ||
|
||
impl Constructor { | ||
/// Create a new constructor. | ||
pub fn new(constructor: Option<Function<'_>>, runtime_bytecode: Buffer) -> Result<Self> { | ||
let mut init_code = Buffer::new(); | ||
if let Some(constructor) = constructor { | ||
let codegen = CodeGen::new( | ||
constructor.sig()?, | ||
Default::default(), | ||
Default::default(), | ||
// No `return` instruction in the generated code. | ||
false, | ||
)?; | ||
|
||
let mut jump_table = JumpTable::default(); | ||
init_code = codegen.finish(&mut jump_table, 0)?; | ||
jump_table.relocate(&mut init_code)?; | ||
}; | ||
|
||
Ok(Self { | ||
masm: MacroAssembler::default(), | ||
init_code, | ||
runtime_bytecode, | ||
}) | ||
} | ||
|
||
fn return_instr_length(init_code_length: usize, runtime_bytecode_length: usize) -> usize { | ||
let mut expected_length = | ||
runtime_bytecode_length.to_ls_bytes().len() + init_code_length.to_ls_bytes().len() + 3; | ||
|
||
if init_code_length < 0xff && init_code_length + expected_length > 0xff { | ||
expected_length += 1; | ||
} | ||
|
||
expected_length | ||
} | ||
|
||
/// Concat the constructor code. | ||
/// | ||
/// Here we override the memory totally with | ||
/// the runtime bytecode. | ||
pub fn finish(&mut self) -> Result<Buffer> { | ||
let init_code_length = self.init_code.len(); | ||
let runtime_bytecode_length = self.runtime_bytecode.len(); | ||
let return_instr_length = | ||
Self::return_instr_length(init_code_length, runtime_bytecode_length); | ||
|
||
// Copy init code and runtime bytecode to memory from offset 0. | ||
// | ||
// 1. code size ( init_code + instr_return + runtime_bytecode ) | ||
// 2. byte offset of code which is fixed to N. | ||
// 3. destination offset which is fixed to 0. | ||
{ | ||
self.masm.push( | ||
&(init_code_length + return_instr_length + runtime_bytecode_length).to_ls_bytes(), | ||
)?; | ||
// # SAFETY | ||
// | ||
// The length of the most significiant bytes of | ||
// the bytecode offset is fixed to 1. | ||
self.masm | ||
.push(&((self.masm.pc_offset() as usize + 9).to_ls_bytes()))?; | ||
self.masm._push0()?; | ||
self.masm._codecopy()?; | ||
} | ||
|
||
// Process instruction `CREATE` | ||
{ | ||
self.masm._push0()?; | ||
self.masm._push0()?; | ||
self.masm._push0()?; | ||
self.masm._calldataload()?; | ||
self.masm._create()?; | ||
} | ||
|
||
self.masm.buffer_mut().extend_from_slice(&self.init_code); | ||
|
||
// Process `RETURN`. | ||
// | ||
// 1. size of the runtime bytecode | ||
// 2. offset of the runtime bytecode in memory | ||
{ | ||
self.masm.push(&runtime_bytecode_length.to_ls_bytes())?; | ||
self.masm | ||
.push(&(init_code_length + return_instr_length).to_ls_bytes())?; | ||
self.masm.asm._return()?; | ||
} | ||
|
||
self.masm | ||
.buffer_mut() | ||
.extend_from_slice(&self.runtime_bytecode); | ||
|
||
Ok(self.masm.buffer().into()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
//! Function handler | ||
use crate::{Error, Exports, Result}; | ||
use std::{ | ||
collections::BTreeMap, | ||
ops::{Deref, DerefMut}, | ||
}; | ||
use wasmparser::{FuncType, FuncValidator, FunctionBody, ValidatorResources, WasmModuleResources}; | ||
|
||
/// Function with validator. | ||
pub struct Function<'f> { | ||
/// Function validator. | ||
pub validator: FuncValidator<ValidatorResources>, | ||
/// Function body. | ||
pub body: FunctionBody<'f>, | ||
} | ||
|
||
impl Function<'_> { | ||
/// Get function index. | ||
pub fn index(&self) -> u32 { | ||
self.validator.index() | ||
} | ||
|
||
/// Get the function signature. | ||
pub fn sig(&self) -> Result<FuncType> { | ||
let func_index = self.validator.index(); | ||
let sig = self | ||
.validator | ||
.resources() | ||
.type_of_function(func_index) | ||
.ok_or(Error::InvalidFunctionSignature)? | ||
.clone(); | ||
|
||
Ok(sig) | ||
} | ||
} | ||
|
||
/// Functions with indexes. | ||
#[derive(Default)] | ||
pub struct Functions<'f>(BTreeMap<u32, Function<'f>>); | ||
|
||
impl<'f> Deref for Functions<'f> { | ||
type Target = BTreeMap<u32, Function<'f>>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl<'f> DerefMut for Functions<'f> { | ||
fn deref_mut(&mut self) -> &mut Self::Target { | ||
&mut self.0 | ||
} | ||
} | ||
|
||
impl<'f> Functions<'f> { | ||
/// Add function to the list. | ||
pub fn add( | ||
&mut self, | ||
validator: FuncValidator<ValidatorResources>, | ||
function: FunctionBody<'f>, | ||
) { | ||
self.0.insert( | ||
validator.index(), | ||
Function { | ||
validator, | ||
body: function, | ||
}, | ||
); | ||
} | ||
|
||
/// Remove constructor function | ||
pub fn remove_constructor(&mut self, exports: &Exports) -> Option<Function<'f>> { | ||
for (index, export) in exports.iter() { | ||
if &export.name == "constructor" { | ||
return self.remove(index); | ||
} | ||
} | ||
|
||
None | ||
} | ||
|
||
/// Remove all selector functions | ||
pub fn drain_selectors(&mut self, exports: &Exports) -> Self { | ||
let mut functions = Self::default(); | ||
|
||
for index in exports.selectors() { | ||
if let Some(function) = self.0.remove(&index) { | ||
functions.0.insert(index, function); | ||
} | ||
} | ||
|
||
functions | ||
} | ||
|
||
/// Get all functions | ||
pub fn into_funcs(self) -> Vec<Function<'f>> { | ||
self.0.into_values().collect() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.