moved crates in lib/ to src/, renamed crates, modified some files' text (#660)
moved crates in lib/ to src/, renamed crates, modified some files' text (#660)
This commit is contained in:
26
cranelift/serde/Cargo.toml
Normal file
26
cranelift/serde/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "cranelift-serde"
|
||||
version = "0.28.0"
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
description = "Serializer/Deserializer for Cranelift IR"
|
||||
repository = "https://github.com/CraneStation/cranelift"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
readme = "README.md"
|
||||
keywords = ["webassembly", "serde"]
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
name = "clif-json"
|
||||
path = "src/clif-json.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.32.0"
|
||||
serde = "1.0.8"
|
||||
serde_derive = "1.0.75"
|
||||
serde_json = "1.0.26"
|
||||
cranelift-codegen = { path = "../cranelift-codegen", version = "0.28.0" }
|
||||
cranelift-reader = { path = "../cranelift-reader", version = "0.28.0" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
travis-ci = { repository = "CraneStation/cranelift" }
|
||||
220
cranelift/serde/LICENSE
Normal file
220
cranelift/serde/LICENSE
Normal file
@@ -0,0 +1,220 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
|
||||
|
||||
--- LLVM Exceptions to the Apache 2.0 License ----
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.
|
||||
|
||||
32
cranelift/serde/README.md
Normal file
32
cranelift/serde/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
This crate performs serialization of the [Cranelift](https://crates.io/crates/cranelift) IR.
|
||||
|
||||
This crate is structured as an optional ability to serialize and deserialize cranelift IR into JSON
|
||||
format.
|
||||
|
||||
Status
|
||||
------
|
||||
|
||||
Cranelift IR can be serialized into JSON.
|
||||
|
||||
Deserialize is a work in progress, as it currently deserializes into the serializable data structure
|
||||
that can be utilized by serde instead of the actual Cranelift IR data structure.
|
||||
|
||||
|
||||
Building and Using Cranelift Serde
|
||||
----------------------------------
|
||||
|
||||
clif-json usage:
|
||||
|
||||
clif-json serialize [-p] <file>
|
||||
clif-json deserialize <file>
|
||||
|
||||
Where the -p flag outputs Cranelift IR as pretty JSON.
|
||||
|
||||
For example to build and use clif-json:
|
||||
|
||||
``` {.sourceCode .sh}
|
||||
cd cranelift-serde
|
||||
cargo build
|
||||
clif-json serialize -p test.clif
|
||||
```
|
||||
|
||||
114
cranelift/serde/src/clif-json.rs
Normal file
114
cranelift/serde/src/clif-json.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
//! Utility for `cranelift_serde`.
|
||||
|
||||
#![deny(
|
||||
missing_docs,
|
||||
trivial_numeric_casts,
|
||||
unused_extern_crates,
|
||||
unstable_features
|
||||
)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
warn(
|
||||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
)
|
||||
)]
|
||||
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use cranelift_reader::parse_functions;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::{self, Write};
|
||||
use std::process;
|
||||
|
||||
mod serde_clif_json;
|
||||
|
||||
fn call_ser(file: &str, pretty: bool) -> Result<(), String> {
|
||||
let ret_of_parse = parse_functions(file);
|
||||
match ret_of_parse {
|
||||
Ok(funcs) => {
|
||||
let ser_funcs = serde_clif_json::SerObj::new(&funcs);
|
||||
let ser_str = if pretty {
|
||||
serde_json::to_string_pretty(&ser_funcs).unwrap()
|
||||
} else {
|
||||
serde_json::to_string(&ser_funcs).unwrap()
|
||||
};
|
||||
println!("{}", ser_str);
|
||||
Ok(())
|
||||
}
|
||||
Err(_pe) => Err("There was a parsing error".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn call_de(file: &File) -> Result<(), String> {
|
||||
let de: serde_clif_json::SerObj = match serde_json::from_reader(file) {
|
||||
Result::Ok(val) => val,
|
||||
Result::Err(err) => panic!("{}", err),
|
||||
};
|
||||
println!("{:?}", de);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let matches = App::new("Cranelift JSON serializer/deserializer utility")
|
||||
.subcommand(
|
||||
SubCommand::with_name("serialize")
|
||||
.display_order(1)
|
||||
.about("Serializes Cranelift IR into JSON.")
|
||||
.arg(Arg::with_name("pretty").short("p").help("pretty json"))
|
||||
.arg(
|
||||
Arg::with_name("FILE")
|
||||
.required(true)
|
||||
.value_name("FILE")
|
||||
.help("Input file for serialization"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("deserialize")
|
||||
.about("Deserializes Cranelift IR into JSON.")
|
||||
.arg(
|
||||
Arg::with_name("FILE")
|
||||
.required(true)
|
||||
.value_name("FILE")
|
||||
.help("Input file for deserialization"),
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let res_serde = match matches.subcommand() {
|
||||
("serialize", Some(m)) => {
|
||||
let mut file =
|
||||
File::open(m.value_of("FILE").unwrap()).expect("Unable to open the file");
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)
|
||||
.expect("Unable to read the file");
|
||||
|
||||
match m.occurrences_of("pretty") {
|
||||
0 => call_ser(&contents, false),
|
||||
_ => call_ser(&contents, true),
|
||||
}
|
||||
}
|
||||
("deserialize", Some(m)) => {
|
||||
let file = File::open(m.value_of("FILE").unwrap()).expect("Unable to open the file");
|
||||
call_de(&file)
|
||||
}
|
||||
_ => Err("Invalid subcommand.".to_string()),
|
||||
};
|
||||
|
||||
if let Err(mut msg) = res_serde {
|
||||
if !msg.ends_with('\n') {
|
||||
msg.push('\n');
|
||||
}
|
||||
io::stdout().flush().expect("flushing stdout");
|
||||
io::stderr().write_all(msg.as_bytes()).unwrap();
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
875
cranelift/serde/src/serde_clif_json.rs
Normal file
875
cranelift/serde/src/serde_clif_json.rs
Normal file
@@ -0,0 +1,875 @@
|
||||
use cranelift_codegen::ir::{Ebb, Function, Inst, InstructionData, Signature};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// Serializable version of the original Cranelift IR
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum SerInstData {
|
||||
Unary {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
},
|
||||
UnaryImm {
|
||||
opcode: String,
|
||||
imm: String,
|
||||
},
|
||||
UnaryIeee32 {
|
||||
opcode: String,
|
||||
imm: String,
|
||||
},
|
||||
UnaryIeee64 {
|
||||
opcode: String,
|
||||
imm: String,
|
||||
},
|
||||
UnaryBool {
|
||||
opcode: String,
|
||||
imm: bool,
|
||||
},
|
||||
UnaryGlobalValue {
|
||||
opcode: String,
|
||||
global_value: String,
|
||||
},
|
||||
Binary {
|
||||
opcode: String,
|
||||
args: [String; 2],
|
||||
},
|
||||
BinaryImm {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
imm: String,
|
||||
},
|
||||
Ternary {
|
||||
opcode: String,
|
||||
args: [String; 3],
|
||||
},
|
||||
MultiAry {
|
||||
opcode: String,
|
||||
args: Vec<String>,
|
||||
},
|
||||
NullAry {
|
||||
opcode: String,
|
||||
},
|
||||
InsertLane {
|
||||
opcode: String,
|
||||
args: [String; 2],
|
||||
lane: String,
|
||||
},
|
||||
ExtractLane {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
lane: String,
|
||||
},
|
||||
IntCompare {
|
||||
opcode: String,
|
||||
args: [String; 2],
|
||||
cond: String,
|
||||
},
|
||||
IntCompareImm {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
cond: String,
|
||||
imm: String,
|
||||
},
|
||||
IntCond {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
cond: String,
|
||||
},
|
||||
FloatCompare {
|
||||
opcode: String,
|
||||
args: [String; 2],
|
||||
cond: String,
|
||||
},
|
||||
FloatCond {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
cond: String,
|
||||
},
|
||||
IntSelect {
|
||||
opcode: String,
|
||||
args: [String; 3],
|
||||
cond: String,
|
||||
},
|
||||
Jump {
|
||||
opcode: String,
|
||||
args: Vec<String>,
|
||||
destination: String,
|
||||
},
|
||||
Branch {
|
||||
opcode: String,
|
||||
args: Vec<String>,
|
||||
destination: String,
|
||||
},
|
||||
BranchInt {
|
||||
opcode: String,
|
||||
args: Vec<String>,
|
||||
cond: String,
|
||||
destination: String,
|
||||
},
|
||||
BranchFloat {
|
||||
opcode: String,
|
||||
args: Vec<String>,
|
||||
cond: String,
|
||||
destination: String,
|
||||
},
|
||||
BranchIcmp {
|
||||
opcode: String,
|
||||
args: Vec<String>,
|
||||
cond: String,
|
||||
destination: String,
|
||||
},
|
||||
BranchTable {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
destination: String,
|
||||
table: String,
|
||||
},
|
||||
BranchTableEntry {
|
||||
opcode: String,
|
||||
args: [String; 2],
|
||||
imm: String,
|
||||
table: String,
|
||||
},
|
||||
BranchTableBase {
|
||||
opcode: String,
|
||||
table: String,
|
||||
},
|
||||
IndirectJump {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
table: String,
|
||||
},
|
||||
Call {
|
||||
opcode: String,
|
||||
args: Vec<String>,
|
||||
func_ref: String,
|
||||
},
|
||||
CallIndirect {
|
||||
opcode: String,
|
||||
args: Vec<String>,
|
||||
sig_ref: String,
|
||||
},
|
||||
FuncAddr {
|
||||
opcode: String,
|
||||
func_ref: String,
|
||||
},
|
||||
Load {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
flags: String,
|
||||
offset: String,
|
||||
},
|
||||
LoadComplex {
|
||||
opcode: String,
|
||||
args: Vec<String>,
|
||||
flags: String,
|
||||
offset: String,
|
||||
},
|
||||
Store {
|
||||
opcode: String,
|
||||
args: [String; 2],
|
||||
flags: String,
|
||||
offset: String,
|
||||
},
|
||||
StoreComplex {
|
||||
opcode: String,
|
||||
args: Vec<String>,
|
||||
flags: String,
|
||||
offset: String,
|
||||
},
|
||||
StackLoad {
|
||||
opcode: String,
|
||||
stack_slot: String,
|
||||
offset: String,
|
||||
},
|
||||
StackStore {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
stack_slot: String,
|
||||
offset: String,
|
||||
},
|
||||
HeapAddr {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
heap: String,
|
||||
imm: String,
|
||||
},
|
||||
TableAddr {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
table: String,
|
||||
offset: String,
|
||||
},
|
||||
RegMove {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
src: String,
|
||||
dst: String,
|
||||
},
|
||||
CopySpecial {
|
||||
opcode: String,
|
||||
src: String,
|
||||
dst: String,
|
||||
},
|
||||
RegSpill {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
src: String,
|
||||
dst: String,
|
||||
},
|
||||
RegFill {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
src: String,
|
||||
dst: String,
|
||||
},
|
||||
Trap {
|
||||
opcode: String,
|
||||
code: String,
|
||||
},
|
||||
CondTrap {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
code: String,
|
||||
},
|
||||
IntCondTrap {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
cond: String,
|
||||
code: String,
|
||||
},
|
||||
FloatCondTrap {
|
||||
opcode: String,
|
||||
arg: String,
|
||||
cond: String,
|
||||
code: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// Convert Cranelift IR instructions to JSON format.
|
||||
pub fn get_inst_data(inst_index: Inst, func: &Function) -> SerInstData {
|
||||
let inst = &func.dfg[inst_index];
|
||||
match *inst {
|
||||
InstructionData::Unary { opcode, arg } => SerInstData::Unary {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
},
|
||||
InstructionData::UnaryImm { opcode, imm } => SerInstData::UnaryImm {
|
||||
opcode: opcode.to_string(),
|
||||
imm: imm.to_string(),
|
||||
},
|
||||
InstructionData::UnaryIeee32 { opcode, imm } => SerInstData::UnaryIeee32 {
|
||||
opcode: opcode.to_string(),
|
||||
imm: imm.to_string(),
|
||||
},
|
||||
InstructionData::UnaryIeee64 { opcode, imm } => SerInstData::UnaryIeee64 {
|
||||
opcode: opcode.to_string(),
|
||||
imm: imm.to_string(),
|
||||
},
|
||||
InstructionData::UnaryBool { opcode, imm } => SerInstData::UnaryBool {
|
||||
opcode: opcode.to_string(),
|
||||
imm,
|
||||
},
|
||||
InstructionData::UnaryGlobalValue {
|
||||
opcode,
|
||||
global_value,
|
||||
} => SerInstData::UnaryGlobalValue {
|
||||
opcode: opcode.to_string(),
|
||||
global_value: global_value.to_string(),
|
||||
},
|
||||
InstructionData::Binary { opcode, args } => {
|
||||
let hold_args = [args[0].to_string(), args[1].to_string()];
|
||||
SerInstData::Binary {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
}
|
||||
}
|
||||
InstructionData::BinaryImm { opcode, arg, imm } => SerInstData::BinaryImm {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
imm: imm.to_string(),
|
||||
},
|
||||
InstructionData::Ternary { opcode, args } => {
|
||||
let hold_args = [
|
||||
args[0].to_string(),
|
||||
args[1].to_string(),
|
||||
args[2].to_string(),
|
||||
];
|
||||
SerInstData::Ternary {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
}
|
||||
}
|
||||
InstructionData::MultiAry { opcode, ref args } => {
|
||||
let mut hold_args = Vec::new();
|
||||
let args_iter = args.as_slice(&func.dfg.value_lists);
|
||||
for arg in args_iter {
|
||||
hold_args.push(arg.to_string());
|
||||
}
|
||||
|
||||
SerInstData::MultiAry {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
}
|
||||
}
|
||||
InstructionData::NullAry { opcode } => SerInstData::NullAry {
|
||||
opcode: opcode.to_string(),
|
||||
},
|
||||
InstructionData::InsertLane { opcode, args, lane } => {
|
||||
let hold_args = [args[0].to_string(), args[1].to_string()];
|
||||
SerInstData::InsertLane {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
lane: lane.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::ExtractLane { opcode, arg, lane } => SerInstData::ExtractLane {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
lane: lane.to_string(),
|
||||
},
|
||||
InstructionData::IntCompare { opcode, args, cond } => {
|
||||
let hold_args = [args[0].to_string(), args[1].to_string()];
|
||||
SerInstData::IntCompare {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
cond: cond.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::IntCompareImm {
|
||||
opcode,
|
||||
arg,
|
||||
cond,
|
||||
imm,
|
||||
} => SerInstData::IntCompareImm {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
cond: cond.to_string(),
|
||||
imm: imm.to_string(),
|
||||
},
|
||||
InstructionData::IntCond { opcode, arg, cond } => SerInstData::IntCond {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
cond: cond.to_string(),
|
||||
},
|
||||
InstructionData::FloatCompare { opcode, args, cond } => {
|
||||
let hold_args = [args[0].to_string(), args[1].to_string()];
|
||||
SerInstData::FloatCompare {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
cond: cond.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::FloatCond { opcode, arg, cond } => SerInstData::FloatCond {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
cond: cond.to_string(),
|
||||
},
|
||||
InstructionData::IntSelect { opcode, args, cond } => {
|
||||
let hold_args = [
|
||||
args[0].to_string(),
|
||||
args[1].to_string(),
|
||||
args[2].to_string(),
|
||||
];
|
||||
SerInstData::IntSelect {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
cond: cond.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::Jump {
|
||||
opcode,
|
||||
ref args,
|
||||
destination,
|
||||
} => {
|
||||
let mut hold_args = Vec::new();
|
||||
let args_iter = args.as_slice(&func.dfg.value_lists);
|
||||
for arg in args_iter {
|
||||
hold_args.push(arg.to_string());
|
||||
}
|
||||
SerInstData::Jump {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
destination: destination.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::Branch {
|
||||
opcode,
|
||||
ref args,
|
||||
destination,
|
||||
} => {
|
||||
let mut hold_args = Vec::new();
|
||||
let args_iter = args.as_slice(&func.dfg.value_lists);
|
||||
for arg in args_iter {
|
||||
hold_args.push(arg.to_string());
|
||||
}
|
||||
SerInstData::Branch {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
destination: destination.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::BranchInt {
|
||||
opcode,
|
||||
ref args,
|
||||
cond,
|
||||
destination,
|
||||
} => {
|
||||
let mut hold_args = Vec::new();
|
||||
let args_iter = args.as_slice(&func.dfg.value_lists);
|
||||
for arg in args_iter {
|
||||
hold_args.push(arg.to_string());
|
||||
}
|
||||
SerInstData::BranchInt {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
cond: cond.to_string(),
|
||||
destination: destination.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::BranchFloat {
|
||||
opcode,
|
||||
ref args,
|
||||
cond,
|
||||
destination,
|
||||
} => {
|
||||
let mut hold_args = Vec::new();
|
||||
let args_iter = args.as_slice(&func.dfg.value_lists);
|
||||
for arg in args_iter {
|
||||
hold_args.push(arg.to_string());
|
||||
}
|
||||
SerInstData::BranchFloat {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
cond: cond.to_string(),
|
||||
destination: destination.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::BranchIcmp {
|
||||
opcode,
|
||||
ref args,
|
||||
cond,
|
||||
destination,
|
||||
} => {
|
||||
let mut hold_args = Vec::new();
|
||||
let args_iter = args.as_slice(&func.dfg.value_lists);
|
||||
for arg in args_iter {
|
||||
hold_args.push(arg.to_string());
|
||||
}
|
||||
SerInstData::BranchIcmp {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
cond: cond.to_string(),
|
||||
destination: destination.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::BranchTable {
|
||||
opcode,
|
||||
arg,
|
||||
destination,
|
||||
table,
|
||||
} => SerInstData::BranchTable {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
destination: destination.to_string(),
|
||||
table: table.to_string(),
|
||||
},
|
||||
InstructionData::BranchTableBase { opcode, table } => SerInstData::BranchTableBase {
|
||||
opcode: opcode.to_string(),
|
||||
table: table.to_string(),
|
||||
},
|
||||
InstructionData::BranchTableEntry {
|
||||
opcode,
|
||||
args,
|
||||
imm,
|
||||
table,
|
||||
} => {
|
||||
let hold_args = [args[0].to_string(), args[1].to_string()];
|
||||
SerInstData::BranchTableEntry {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
imm: imm.to_string(),
|
||||
table: table.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::IndirectJump { opcode, arg, table } => SerInstData::IndirectJump {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
table: table.to_string(),
|
||||
},
|
||||
InstructionData::Call {
|
||||
opcode,
|
||||
ref args,
|
||||
func_ref,
|
||||
} => {
|
||||
let mut hold_args = Vec::new();
|
||||
let args_iter = args.as_slice(&func.dfg.value_lists);
|
||||
for arg in args_iter {
|
||||
hold_args.push(arg.to_string());
|
||||
}
|
||||
SerInstData::Call {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
func_ref: func_ref.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::CallIndirect {
|
||||
opcode,
|
||||
ref args,
|
||||
sig_ref,
|
||||
} => {
|
||||
let mut hold_args = Vec::new();
|
||||
let args_iter = args.as_slice(&func.dfg.value_lists);
|
||||
for arg in args_iter {
|
||||
hold_args.push(arg.to_string());
|
||||
}
|
||||
SerInstData::CallIndirect {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
sig_ref: sig_ref.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::FuncAddr { opcode, func_ref } => SerInstData::FuncAddr {
|
||||
opcode: opcode.to_string(),
|
||||
func_ref: func_ref.to_string(),
|
||||
},
|
||||
InstructionData::Load {
|
||||
opcode,
|
||||
arg,
|
||||
flags,
|
||||
offset,
|
||||
} => SerInstData::Load {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
flags: flags.to_string(),
|
||||
offset: offset.to_string(),
|
||||
},
|
||||
InstructionData::LoadComplex {
|
||||
opcode,
|
||||
ref args,
|
||||
flags,
|
||||
offset,
|
||||
} => {
|
||||
let mut hold_args = Vec::new();
|
||||
let args_iter = args.as_slice(&func.dfg.value_lists);
|
||||
for arg in args_iter {
|
||||
hold_args.push(arg.to_string());
|
||||
}
|
||||
SerInstData::LoadComplex {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
flags: flags.to_string(),
|
||||
offset: offset.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::Store {
|
||||
opcode,
|
||||
args,
|
||||
flags,
|
||||
offset,
|
||||
} => {
|
||||
let hold_args = [args[0].to_string(), args[1].to_string()];
|
||||
SerInstData::Store {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
flags: flags.to_string(),
|
||||
offset: offset.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::StoreComplex {
|
||||
opcode,
|
||||
ref args,
|
||||
flags,
|
||||
offset,
|
||||
} => {
|
||||
let mut hold_args = Vec::new();
|
||||
let args_iter = args.as_slice(&func.dfg.value_lists);
|
||||
for arg in args_iter {
|
||||
hold_args.push(arg.to_string());
|
||||
}
|
||||
SerInstData::StoreComplex {
|
||||
opcode: opcode.to_string(),
|
||||
args: hold_args,
|
||||
flags: flags.to_string(),
|
||||
offset: offset.to_string(),
|
||||
}
|
||||
}
|
||||
InstructionData::StackLoad {
|
||||
opcode,
|
||||
stack_slot,
|
||||
offset,
|
||||
} => SerInstData::StackLoad {
|
||||
opcode: opcode.to_string(),
|
||||
stack_slot: stack_slot.to_string(),
|
||||
offset: offset.to_string(),
|
||||
},
|
||||
InstructionData::StackStore {
|
||||
opcode,
|
||||
arg,
|
||||
stack_slot,
|
||||
offset,
|
||||
} => SerInstData::StackStore {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
stack_slot: stack_slot.to_string(),
|
||||
offset: offset.to_string(),
|
||||
},
|
||||
InstructionData::HeapAddr {
|
||||
opcode,
|
||||
arg,
|
||||
heap,
|
||||
imm,
|
||||
} => SerInstData::HeapAddr {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
heap: heap.to_string(),
|
||||
imm: imm.to_string(),
|
||||
},
|
||||
InstructionData::TableAddr {
|
||||
opcode,
|
||||
arg,
|
||||
table,
|
||||
offset,
|
||||
} => SerInstData::TableAddr {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
table: table.to_string(),
|
||||
offset: offset.to_string(),
|
||||
},
|
||||
InstructionData::RegMove {
|
||||
opcode,
|
||||
arg,
|
||||
src,
|
||||
dst,
|
||||
} => SerInstData::RegMove {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
src: src.to_string(),
|
||||
dst: dst.to_string(),
|
||||
},
|
||||
InstructionData::CopySpecial { opcode, src, dst } => SerInstData::CopySpecial {
|
||||
opcode: opcode.to_string(),
|
||||
src: src.to_string(),
|
||||
dst: dst.to_string(),
|
||||
},
|
||||
InstructionData::RegSpill {
|
||||
opcode,
|
||||
arg,
|
||||
src,
|
||||
dst,
|
||||
} => SerInstData::RegSpill {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
src: src.to_string(),
|
||||
dst: dst.to_string(),
|
||||
},
|
||||
InstructionData::RegFill {
|
||||
opcode,
|
||||
arg,
|
||||
src,
|
||||
dst,
|
||||
} => SerInstData::RegFill {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
src: src.to_string(),
|
||||
dst: dst.to_string(),
|
||||
},
|
||||
InstructionData::Trap { opcode, code } => SerInstData::Trap {
|
||||
opcode: opcode.to_string(),
|
||||
code: code.to_string(),
|
||||
},
|
||||
InstructionData::CondTrap { opcode, arg, code } => SerInstData::CondTrap {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
code: code.to_string(),
|
||||
},
|
||||
InstructionData::IntCondTrap {
|
||||
opcode,
|
||||
arg,
|
||||
cond,
|
||||
code,
|
||||
} => SerInstData::IntCondTrap {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
cond: cond.to_string(),
|
||||
code: code.to_string(),
|
||||
},
|
||||
InstructionData::FloatCondTrap {
|
||||
opcode,
|
||||
arg,
|
||||
cond,
|
||||
code,
|
||||
} => SerInstData::FloatCondTrap {
|
||||
opcode: opcode.to_string(),
|
||||
arg: arg.to_string(),
|
||||
cond: cond.to_string(),
|
||||
code: code.to_string(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializable version of Cranelift IR instructions.
|
||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||
pub struct SerInst {
|
||||
pub inst_name: String,
|
||||
pub inst_data: SerInstData,
|
||||
}
|
||||
|
||||
impl SerInst {
|
||||
pub fn new(inst: Inst, func: &Function) -> Self {
|
||||
Self {
|
||||
inst_name: inst.to_string(),
|
||||
inst_data: get_inst_data(inst, func),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializable version of Cranelift IR Ebbs.
|
||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||
pub struct SerEbb {
|
||||
pub ebb: String,
|
||||
pub params: Vec<String>,
|
||||
pub insts: Vec<SerInst>,
|
||||
}
|
||||
|
||||
impl SerEbb {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
ebb: name,
|
||||
params: Vec::new(),
|
||||
insts: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn populate_inst(func: &Function, ebb: Ebb) -> Vec<SerInst> {
|
||||
let mut ser_vec: Vec<SerInst> = Vec::new();
|
||||
let ret_iter = func.layout.ebb_insts(ebb);
|
||||
for inst in ret_iter {
|
||||
let ser_inst: SerInst = SerInst::new(inst, &func);
|
||||
ser_vec.push(ser_inst);
|
||||
}
|
||||
ser_vec
|
||||
}
|
||||
|
||||
/// Translating Ebb parameters into serializable parameters.
|
||||
pub fn populate_params(func: &Function, ebb: Ebb) -> Vec<String> {
|
||||
let mut ser_vec: Vec<String> = Vec::new();
|
||||
let parameters = func.dfg.ebb_params(ebb);
|
||||
for param in parameters {
|
||||
ser_vec.push(param.to_string());
|
||||
}
|
||||
ser_vec
|
||||
}
|
||||
|
||||
/// Serializable Data Flow Graph.
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct SerDataFlowGraph {
|
||||
ebbs: Vec<SerEbb>,
|
||||
}
|
||||
|
||||
/// Serialize all parts of the Cranelift Ebb data structure, this includes name, parameters, and
|
||||
/// instructions.
|
||||
pub fn populate_ebbs(func: &Function) -> Vec<SerEbb> {
|
||||
let mut ebb_vec: Vec<SerEbb> = Vec::new();
|
||||
for ebb in func.layout.ebbs() {
|
||||
let mut ser_ebb: SerEbb = SerEbb::new(ebb.to_string());
|
||||
ser_ebb.params = populate_params(&func, ebb);
|
||||
ser_ebb.insts = populate_inst(&func, ebb);
|
||||
ebb_vec.push(ser_ebb);
|
||||
}
|
||||
ebb_vec
|
||||
}
|
||||
|
||||
/// Serializable Cranelift IR data flow graph, including all ebbs.
|
||||
impl SerDataFlowGraph {
|
||||
pub fn create_new(func: &Function) -> Self {
|
||||
Self {
|
||||
ebbs: populate_ebbs(func),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(func: &Function) -> Self {
|
||||
Self::create_new(func)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializable signature including function parameters and returns.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SerSignature {
|
||||
pub func_params: Vec<String>,
|
||||
pub func_returns: Vec<String>,
|
||||
}
|
||||
|
||||
impl SerSignature {
|
||||
/// Creating serializable signature data structure from all Cranelift IR functions.
|
||||
fn create_new(sig: &Signature) -> Self {
|
||||
let mut params_vec: Vec<String> = Vec::new();
|
||||
let mut returns_vec: Vec<String> = Vec::new();
|
||||
for param in &sig.params {
|
||||
params_vec.push(param.to_string());
|
||||
}
|
||||
for ret in &sig.returns {
|
||||
returns_vec.push(ret.to_string());
|
||||
}
|
||||
Self {
|
||||
func_params: params_vec,
|
||||
func_returns: returns_vec,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(func: &Function) -> Self {
|
||||
Self::create_new(&func.signature)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializable Function type, including name, signature, global values, and data flow graph.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SerFunction {
|
||||
pub name: String,
|
||||
pub signature: SerSignature,
|
||||
pub globals: Vec<String>,
|
||||
pub dfg: SerDataFlowGraph,
|
||||
}
|
||||
|
||||
impl SerFunction {
|
||||
/// Creates serializable global values, as well as the functions signature, name, and data flow
|
||||
/// graph.
|
||||
fn create_new(func: &Function) -> Self {
|
||||
let mut global_vec: Vec<String> = Vec::new();
|
||||
for (glob_name, _) in func.global_values.iter() {
|
||||
global_vec.push(glob_name.to_string());
|
||||
}
|
||||
Self {
|
||||
name: func.name.to_string(),
|
||||
signature: SerSignature::new(&func),
|
||||
globals: global_vec,
|
||||
dfg: SerDataFlowGraph::new(&func),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(func: &Function) -> Self {
|
||||
Self::create_new(func)
|
||||
}
|
||||
}
|
||||
|
||||
/// Must have SerObj for deserialization, contains all of the functions from inside the file to be
|
||||
/// serialized. Files have one SerObj each, with all SerFunctions contained inside that SerObj.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SerObj {
|
||||
pub functions: Vec<SerFunction>,
|
||||
}
|
||||
|
||||
impl SerObj {
|
||||
fn create_new(funcs: Vec<SerFunction>) -> Self {
|
||||
Self { functions: funcs }
|
||||
}
|
||||
|
||||
pub fn new(funcs: &[Function]) -> Self {
|
||||
let mut func_vec: Vec<SerFunction> = Vec::new();
|
||||
for func in funcs {
|
||||
let ser_func: SerFunction = SerFunction::new(&func);
|
||||
func_vec.push(ser_func);
|
||||
}
|
||||
Self::create_new(func_vec)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user