Skip to content

Commit

Permalink
Merge up
Browse files Browse the repository at this point in the history
  • Loading branch information
prozacchiwawa committed Oct 12, 2023
1 parent 3163164 commit b2f1673
Show file tree
Hide file tree
Showing 11 changed files with 704 additions and 31 deletions.
331 changes: 328 additions & 3 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ linked-hash-map = "0.5.6"
serde = { version = "1.0", features = ["derive", "rc"] }
regex = "1.8.4"

[dependencies.pprof]
version = "0.13"
features = ["flamegraph"]
optional = true

[dependencies.pyo3]
version = "0.14.2"
features = ["abi3-py38", "extension-module"]
Expand All @@ -57,6 +62,7 @@ crate-type = ["cdylib", "rlib"]
[features]
extension-module = ["dep:pyo3"]
debug-print = []
profiling = ["dep:pprof"]
default = ["debug-print"]

[target.'cfg(target_family="wasm")'.dependencies]
Expand Down
6 changes: 5 additions & 1 deletion src/classic/clvm_tools/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use crate::classic::clvm_tools::debug::{
trace_to_text,
};
use crate::classic::clvm_tools::ir::reader::read_ir;
use crate::classic::clvm_tools::profiling::Profiler;
use crate::classic::clvm_tools::sha256tree::sha256tree;
use crate::classic::clvm_tools::stages;
use crate::classic::clvm_tools::stages::stage_0::{
Expand Down Expand Up @@ -320,6 +321,7 @@ impl ArgumentValueConv for OperatorsVersion {

pub fn run(args: &[String]) {
env_logger::init();
let _profiler = Profiler::new("run-profile.svg");

let mut s = Stream::new(None);
launch_tool(&mut s, args, "run", 2);
Expand Down Expand Up @@ -504,6 +506,7 @@ pub fn cldb_hierarchy(

pub fn cldb(args: &[String]) {
env_logger::init();
let _profiler = Profiler::new("cldb-profile.svg");

let tool_name = "cldb".to_string();
let props = TArgumentParserProps {
Expand Down Expand Up @@ -614,7 +617,7 @@ pub fn cldb(args: &[String]) {
_ => None,
});

let only_print = parsed_args.get("only_print").map(|_| true).unwrap_or(false);
let only_print = parsed_args.get("debug_print").map(|_| true).unwrap_or(false);

let do_optimize = parsed_args
.get("optimize")
Expand Down Expand Up @@ -756,6 +759,7 @@ pub fn cldb(args: &[String]) {
loop {
if cldbrun.is_ended() {
println!("{}", yamlette_string(&output));

return;
}

Expand Down
1 change: 1 addition & 0 deletions src/classic/clvm_tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ pub mod debug;
pub mod ir;
pub mod node_path;
pub mod pattern_match;
mod profiling;
pub mod sha256tree;
pub mod stages;
44 changes: 44 additions & 0 deletions src/classic/clvm_tools/profiling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#[cfg(feature = "profiling")]
use std::fs;
#[cfg(feature = "profiling")]
use pprof::ProfilerGuard;

#[cfg(feature = "profiling")]
pub struct Profiler<'a> {
filename: String,
guard: ProfilerGuard<'a>
}

#[cfg(not(feature = "profiling"))]
pub struct Profiler {
}

#[cfg(feature = "profiling")]
impl<'a> Profiler<'a> {
pub fn new(filename: &str) -> Self {
Profiler {
filename: filename.to_string(),
guard: pprof::ProfilerGuardBuilder::default().frequency(100).blocklist(&["libc", "libgcc", "pthread", "vdso"]).build().unwrap()
}
}
}

#[cfg(not(feature = "profiling"))]
impl Profiler {
pub fn new(_filename: &str) -> Self { Profiler { } }
}

#[cfg(feature = "profiling")]
impl<'a> Drop for Profiler<'a> {
fn drop(self: &mut Profiler<'a>) {
if let Ok(report) = self.guard.report().build() {
let file = fs::File::create(&self.filename).unwrap();
report.flamegraph(file).unwrap();
};
}
}

#[cfg(not(feature = "profiling"))]
impl Drop for Profiler {
fn drop(self: &mut Profiler) { }
}
1 change: 1 addition & 0 deletions src/py/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,5 +405,6 @@ fn clvm_tools_rs(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(check_dependencies, m)?)?;
m.add_function(wrap_pyfunction!(compose_run_function, m)?)?;
m.add_class::<PythonRunStep>()?;
m.add_function(wrap_pyfunction!(brun_with_operators, m)?)?;
Ok(())
}
174 changes: 174 additions & 0 deletions wasm/src/dialect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
use std::collections::HashMap;
use std::rc::Rc;

use js_sys::{Function, Reflect};
use clvmr::{Allocator, ChiaDialect, NO_UNKNOWN_OPS, ENABLE_BLS_OPS, ENABLE_SECP_OPS, run_program_with_pre_eval};
use clvmr::allocator::{NodePtr, SExp};
use clvmr::cost::Cost;
use clvmr::dialect::{Dialect, OperatorSet};
use clvmr::reduction::{EvalErr, Reduction, Response};

use wasm_bindgen::JsValue;

use clvm_tools_rs::classic::clvm_tools::stages::stage_0::{RunProgramOption, TRunProgram};
use clvm_tools_rs::compiler::clvm::{convert_from_clvm_rs, convert_to_clvm_rs};
use clvm_tools_rs::compiler::srcloc::Srcloc;

use crate::jsval::sexp_from_js_object;
use crate::objects::convert_to_program;

/// A dialect that allows javascript code to add operators to a clvm runner.
/// This can be used for a variety of purposes, but allows the calpoker driver
/// to run code to an exception by starting an isolated VM.
pub struct JsEnrichedCLVM {
loc: Srcloc,
base_dialect: Rc<dyn Dialect>,
extra_ops: HashMap<Vec<u8>, Function>,
}

impl JsEnrichedCLVM {
pub fn new(loc: Srcloc, extra_ops: HashMap<Vec<u8>, Function>) -> Self {
let base_dialect = Rc::new(ChiaDialect::new(
NO_UNKNOWN_OPS | ENABLE_BLS_OPS | ENABLE_SECP_OPS,
));
JsEnrichedCLVM {
loc,
base_dialect,
extra_ops,
}
}

// Return the extension operator system to use while compiling based on user
// preference.
fn get_operators_extension(&self) -> OperatorSet {
OperatorSet::BLS
}
}

impl Dialect for JsEnrichedCLVM {
fn quote_kw(&self) -> &[u8] {
&[1]
}

fn apply_kw(&self) -> &[u8] {
&[2]
}

fn softfork_kw(&self) -> &[u8] {
&[36]
}

// The softfork operator comes with an extension argument.
fn softfork_extension(&self, _ext: u32) -> OperatorSet { OperatorSet::BLS }

fn op(
&self,
allocator: &mut Allocator,
op: NodePtr,
sexp: NodePtr,
max_cost: Cost,
_extension: OperatorSet,
) -> Response {
let make_eval_obj = |allocator: &mut Allocator| -> Result<NodePtr, EvalErr> {
allocator.new_pair(op, sexp)
};
let run_failure_to_response_err = |allocator: &mut Allocator, _e| -> EvalErr {
let eval_obj =
match make_eval_obj(allocator) {
Ok(eval_obj) => eval_obj,
Err(e) => { return e; }
};
EvalErr(eval_obj, format!("Failure in data conversion"))
};

let js_err_to_response_err = |allocator: &mut Allocator, e: JsValue| {
let eval_obj =
match make_eval_obj(allocator) {
Ok(eval_obj) => eval_obj,
Err(e) => { return e; }
};

if let Some(string_res) = e.as_string() {
EvalErr(eval_obj, format!("Failure returned from injected operator: {string_res}"));
}

EvalErr(eval_obj, format!("Failure returned from injected operator"))
};

// Check for an operator in the operators given to us that matches the
// hex representation of the operator name.
let extensions_to_clvmr_during_compile = self.get_operators_extension();

match allocator.sexp(op) {
SExp::Atom => {
// use of op obvious.
let opbuf = allocator.atom(op);
if let Some(extra_op) = self.extra_ops.get(opbuf) {
// Setup arguments.
let converted_sexp = convert_from_clvm_rs(allocator, self.loc.clone(), sexp).map_err(|e| run_failure_to_response_err(allocator, e))?;
let call_args = js_sys::Array::new();
let arg = convert_to_program(converted_sexp).map_err(|e| js_err_to_response_err(allocator, e))?;
call_args.set(0, arg);

let operator_result = Reflect::apply(
extra_op,
&JsValue::null(),
&call_args,
).map_err(|e| js_err_to_response_err(allocator, e))?;

let from_javascript_program = sexp_from_js_object(self.loc.clone(), &operator_result);
if let Some(sexp) = from_javascript_program {
// Success path.
let as_clvm_rs = convert_to_clvm_rs(allocator, sexp).map_err(|e| run_failure_to_response_err(allocator, e))?;
return Ok(Reduction(1,as_clvm_rs));
}

// Error path: no possible conversion.
let eval_obj = make_eval_obj(allocator)?;
return Err(EvalErr(eval_obj, "Injected operator returned unconvertible value".to_string()));
}

let extensions_to_clvmr_during_compile = self.get_operators_extension();

self.base_dialect.op(
allocator,
op,
sexp,
max_cost,
extensions_to_clvmr_during_compile,
)
}
_ => self.base_dialect.op(
allocator,
op,
sexp,
max_cost,
extensions_to_clvmr_during_compile,
),
}
}

fn allow_unknown_ops(&self) -> bool {
false
}
}

impl TRunProgram for JsEnrichedCLVM {
fn run_program(
&self,
allocator: &mut Allocator,
program: NodePtr,
args: NodePtr,
option: Option<RunProgramOption>,
) -> Response {
let max_cost = option.as_ref().and_then(|o| o.max_cost).unwrap_or(0);
run_program_with_pre_eval(
allocator,
self,
program,
args,
max_cost,
option.and_then(|o| o.pre_eval_f),
)
}
}
22 changes: 11 additions & 11 deletions wasm/src/jsval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,19 @@ pub fn get_property(o: &Object, name: &str) -> Option<JsValue> {
o,
&JsValue::from_str(name),
))
.and_then(|desc_obj| match Object::try_from(desc_obj) {
Some(o) => {
for pj in Object::entries(o).values() {
let pair = Array::from(&pj.unwrap());
let propname = pair.get(0).as_string();
if propname == Some("value".to_string()) {
return Some(pair.get(1));
.and_then(|desc_obj| match Object::try_from(desc_obj) {
Some(o) => {
for pj in Object::entries(o).values() {
let pair = Array::from(&pj.unwrap());
let propname = pair.get(0).as_string();
if propname == Some("value".to_string()) {
return Some(pair.get(1));
}
}
None
}
None
}
_ => None,
})
_ => None,
})
}

fn location_lc_pair(o: &Object) -> Option<(usize, usize)> {
Expand Down
1 change: 1 addition & 0 deletions wasm/src/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod api;
mod dialect;
mod jsval;
pub mod objects;
Loading

0 comments on commit b2f1673

Please sign in to comment.