Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CodeBlock,ProgramAst: implement Serialize/Deserialize #1173

Merged
merged 2 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions assembly/src/ast/code_body.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Node, Serializable,
SourceLocation, Vec,
SourceLocation, Vec, MAX_BODY_LEN,
};
use core::{iter, slice};

Expand All @@ -22,12 +22,17 @@ impl CodeBody {
// --------------------------------------------------------------------------------------------

/// Creates a new instance of [CodeBody] populated with the provided `nodes`.
///
/// # Panics
/// Assumes that the number of nodes is smaller than 2^16 and panics otherwise.
pub fn new<N>(nodes: N) -> Self
where
N: IntoIterator<Item = Node>,
{
let nodes: Vec<_> = nodes.into_iter().collect();
assert!(nodes.len() <= MAX_BODY_LEN, "too many nodes");
Self {
nodes: nodes.into_iter().collect(),
nodes,
locations: Vec::new(),
}
}
Expand Down
6 changes: 4 additions & 2 deletions assembly/src/ast/module.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use vm_core::utils::Serializable;

use super::{
format::*,
imports::ModuleImports,
Expand All @@ -13,6 +11,10 @@ use super::{
},
};
use core::{fmt, str::from_utf8};
use vm_core::utils::Serializable;

// MODULE AST
// ================================================================================================

/// An abstract syntax tree of a Miden module.
///
Expand Down
6 changes: 6 additions & 0 deletions assembly/src/ast/procedure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use super::{
};
use core::{iter, str::from_utf8};

// PROCEDURE AST
// ================================================================================================

/// An abstract syntax tree of a Miden procedure.
///
/// A procedure AST consists of a list of body nodes and additional metadata about the procedure
Expand Down Expand Up @@ -158,6 +161,9 @@ impl Deserializable for ProcedureAst {
}
}

// PROCEDURE RE-EXPORT
// ================================================================================================

/// Represents a re-exported procedure.
///
/// A re-exported procedure is a procedure that is defined in a different module in the same
Expand Down
58 changes: 42 additions & 16 deletions assembly/src/ast/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ use core::{fmt, iter};
#[cfg(feature = "std")]
use std::{fs, io, path::Path};

// PROGRAM AST
// ================================================================================================

/// An abstract syntax tree of an executable Miden program.
///
/// A program AST consists of a body of the program, a list of internal procedure ASTs, a list of
Expand All @@ -39,7 +42,17 @@ impl ProgramAst {
/// Returns a new [ProgramAst].
///
/// A program consist of a body and a set of internal (i.e., not exported) procedures.
///
/// # Errors
/// Returns an error if:
/// - The number of body nodes is greater than or equal to 2^16.
/// - The number of local procedures is greater than or equal to 2^16.
pub fn new(body: Vec<Node>, local_procs: Vec<ProcedureAst>) -> Result<Self, ParsingError> {
// TODO: instead of ParsingError, this should probably return a different error type:
// e.g., AstError.
if body.len() > MAX_BODY_LEN {
return Err(ParsingError::too_many_body_nodes(body.len(), MAX_BODY_LEN));
}
if local_procs.len() > MAX_LOCAL_PROCS {
return Err(ParsingError::too_many_module_procs(local_procs.len(), MAX_LOCAL_PROCS));
}
Expand Down Expand Up @@ -175,67 +188,80 @@ impl ProgramAst {
// SERIALIZATION / DESERIALIZATION
// --------------------------------------------------------------------------------------------

/// Returns byte representation of this [ProgramAst].
/// Writes byte representation of this [ProgramAst] into the specified target according with
/// the specified serde options.
///
/// The serde options are serialized as header information for the purposes of deserialization.
pub fn to_bytes(&self, options: AstSerdeOptions) -> Vec<u8> {
let mut target = Vec::<u8>::default();

pub fn write_into<W: ByteWriter>(&self, target: &mut W, options: AstSerdeOptions) {
// serialize the options, so that deserialization knows what to do
options.write_into(&mut target);
options.write_into(target);

// asserts below are OK because we enforce limits on the number of procedure and the
// number of body instructions in relevant parsers

// serialize imports if required
if options.serialize_imports {
self.import_info.write_into(&mut target);
self.import_info.write_into(target);
}

// serialize procedures
assert!(self.local_procs.len() <= MAX_LOCAL_PROCS, "too many local procs");
target.write_u16(self.local_procs.len() as u16);
self.local_procs.write_into(&mut target);
self.local_procs.write_into(target);

// serialize program body
assert!(self.body.nodes().len() <= MAX_BODY_LEN, "too many body instructions");
target.write_u16(self.body.nodes().len() as u16);
self.body.nodes().write_into(&mut target);
self.body.nodes().write_into(target);
}

/// Returns byte representation of this [ProgramAst].
///
/// The serde options are serialized as header information for the purposes of deserialization.
pub fn to_bytes(&self, options: AstSerdeOptions) -> Vec<u8> {
let mut target = Vec::<u8>::default();
self.write_into(&mut target, options);
target
}

/// Returns a [ProgramAst] struct deserialized from the provided bytes.
/// Returns a [ProgramAst] struct deserialized from the specified reader.
///
/// This function assumes that the byte array contains a serialized [AstSerdeOptions] struct as
/// a header.
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
let mut source = SliceReader::new(bytes);

pub fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
// Deserialize the serialization options used when serializing
let options = AstSerdeOptions::read_from(&mut source)?;
let options = AstSerdeOptions::read_from(source)?;

// deserialize imports if required
let import_info = if options.serialize_imports {
ModuleImports::read_from(&mut source)?
ModuleImports::read_from(source)?
} else {
ModuleImports::default()
};

// deserialize local procs
let num_local_procs = source.read_u16()?;
let local_procs = Deserializable::read_batch_from(&mut source, num_local_procs as usize)?;
let local_procs = Deserializable::read_batch_from(source, num_local_procs as usize)?;

// deserialize program body
let body_len = source.read_u16()? as usize;
let nodes = Deserializable::read_batch_from(&mut source, body_len)?;
let nodes = Deserializable::read_batch_from(source, body_len)?;

match Self::new(nodes, local_procs) {
Err(err) => Err(DeserializationError::UnknownError(err.message().clone())),
Ok(res) => Ok(res.with_import_info(import_info)),
}
}

/// Returns a [ProgramAst] struct deserialized from the provided bytes.
///
/// This function assumes that the byte array contains a serialized [AstSerdeOptions] struct as
/// a header.
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
let mut source = SliceReader::new(bytes);
Self::read_from(&mut source)
}

/// Loads the [SourceLocation] from the `source`.
///
/// It expects the `start` location at the first position, and will subsequently load the
Expand Down
10 changes: 10 additions & 0 deletions assembly/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,16 @@ impl ParsingError {
}
}

pub fn too_many_body_nodes(num_nodes: usize, max_nodes: usize) -> Self {
ParsingError {
message: format!(
"a code body cannot contain more than {num_nodes} nodes, but had {max_nodes}"
),
location: SourceLocation::default(),
op: "".to_string(),
}
}

pub fn module_docs_too_long(doc_len: usize, max_len: usize) -> Self {
ParsingError {
message: format!(
Expand Down
Loading