difftreelog
perf specialize super.field gets
in: master
4 files changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth1use std::{2 fs::{create_dir_all, File},3 io::{Read, Write},4};56use clap::{CommandFactory, Parser};7use clap_complete::Shell;8use jrsonnet_cli::{ConfigureState, GeneralOpts, ManifestOpts, OutputOpts, TraceOpts};9use jrsonnet_evaluator::{10 apply_tla,11 error::{Error as JrError, ErrorKind},12 throw, ResultExt, State, Val,13};1415#[cfg(feature = "mimalloc")]16#[global_allocator]17static GLOBAL: mimallocator::Mimalloc = mimallocator::Mimalloc;1819#[derive(Parser)]20enum SubOpts {21 /// Generate completions for specified shell22 Generate {23 /// Target shell name24 shell: Shell,25 },26}2728#[derive(Parser)]29#[clap(next_help_heading = "DEBUG")]30struct DebugOpts {31 /// Required OS stack size.32 /// This shouldn't be changed unless jrsonnet is failing with stack overflow error.33 #[clap(long, name = "size")]34 pub os_stack: Option<usize>,35}3637#[derive(Parser)]38#[clap(next_help_heading = "INPUT")]39struct InputOpts {40 /// Treat input as code, evaluate them instead of reading file41 #[clap(long, short = 'e')]42 pub exec: bool,4344 /// Path to the file to be compiled if `--evaluate` is unset, otherwise code itself45 pub input: Option<String>,46}4748#[derive(Parser)]49#[clap(args_conflicts_with_subcommands = true, disable_version_flag = true)]50struct Opts {51 #[clap(subcommand)]52 sub: Option<SubOpts>,5354 #[clap(flatten)]55 input: InputOpts,56 #[clap(flatten)]57 general: GeneralOpts,5859 #[clap(flatten)]60 trace: TraceOpts,61 #[clap(flatten)]62 manifest: ManifestOpts,63 #[clap(flatten)]64 output: OutputOpts,65 #[clap(flatten)]66 debug: DebugOpts,67}6869fn main() {70 let opts: Opts = Opts::parse();7172 if let Some(sub) = opts.sub {73 match sub {74 SubOpts::Generate { shell } => {75 use clap_complete::generate;76 let app = &mut Opts::command();77 let buf = &mut std::io::stdout();78 generate(shell, app, "jrsonnet", buf);79 std::process::exit(0)80 }81 }82 }8384 let success = if let Some(size) = opts.debug.os_stack {85 std::thread::Builder::new()86 .stack_size(size * 1024 * 1024)87 .spawn(|| main_catch(opts))88 .expect("new thread spawned")89 .join()90 .expect("thread finished successfully")91 } else {92 main_catch(opts)93 };94 if !success {95 std::process::exit(1);96 }97}9899#[derive(thiserror::Error, Debug)]100enum Error {101 // Handled differently102 #[error("evaluation error")]103 Evaluation(JrError),104 #[error("io error")]105 Io(#[from] std::io::Error),106 #[error("input is not utf8 encoded")]107 Utf8(#[from] std::str::Utf8Error),108 #[error("missing input argument")]109 MissingInputArgument,110}111impl From<JrError> for Error {112 fn from(e: JrError) -> Self {113 Self::Evaluation(e)114 }115}116impl From<ErrorKind> for Error {117 fn from(e: ErrorKind) -> Self {118 Self::from(JrError::from(e))119 }120}121122fn main_catch(opts: Opts) -> bool {123 let s = State::default();124 let trace = opts125 .trace126 .configure(&s)127 .expect("this configurator doesn't fail");128 if let Err(e) = main_real(&s, opts) {129 if let Error::Evaluation(e) = e {130 let mut out = String::new();131 trace.write_trace(&mut out, &e).expect("format error");132 eprintln!("{out}")133 } else {134 eprintln!("{}", e);135 }136 return false;137 }138 true139}140141fn main_real(s: &State, opts: Opts) -> Result<(), Error> {142 let (_stack_guard, tla, _gc_guard) = opts.general.configure(s)?;143 let manifest_format = opts.manifest.configure(s)?;144145 let input = opts.input.input.ok_or(Error::MissingInputArgument)?;146 let val = if opts.input.exec {147 s.evaluate_snippet("<cmdline>".to_owned(), &input as &str)?148 } else if input == "-" {149 let mut input = Vec::new();150 std::io::stdin().read_to_end(&mut input)?;151 let input_str = std::str::from_utf8(&input)?;152 s.evaluate_snippet("<stdin>".to_owned(), input_str)?153 } else {154 s.import(&input)?155 };156157 let val = apply_tla(s.clone(), &tla, val)?;158159 if let Some(multi) = opts.output.multi {160 if opts.output.create_output_dirs {161 let mut dir = multi.clone();162 dir.pop();163 create_dir_all(dir)?;164 }165 let Val::Obj(obj) = val else {166 throw!("value should be object for --multi manifest, got {}", val.value_type())167 };168 for (field, data) in obj.iter(169 #[cfg(feature = "exp-preserve-order")]170 opts.manifest.preserve_order,171 ) {172 let data = data.with_description(|| format!("getting field {field} for manifest"))?;173174 let mut path = multi.clone();175 path.push(&field as &str);176 if opts.output.create_output_dirs {177 let mut dir = path.clone();178 dir.pop();179 create_dir_all(dir)?;180 }181 println!("{}", path.to_str().expect("path"));182 let mut file = File::create(path)?;183 writeln!(184 file,185 "{}",186 data.manifest(&manifest_format)187 .with_description(|| format!("manifesting {field}"))?188 )?;189 }190 } else if let Some(path) = opts.output.output_file {191 if opts.output.create_output_dirs {192 let mut dir = path.clone();193 dir.pop();194 create_dir_all(dir)?;195 }196 let mut file = File::create(path)?;197 writeln!(file, "{}", val.manifest(manifest_format)?)?;198 } else {199 let output = val.manifest(manifest_format)?;200 if !output.is_empty() {201 println!("{}", output);202 }203 }204205 Ok(())206}1use std::{2 fs::{create_dir_all, File},3 io::{Read, Write},4};56use clap::{CommandFactory, Parser};7use clap_complete::Shell;8use jrsonnet_cli::{ConfigureState, GeneralOpts, ManifestOpts, OutputOpts, TraceOpts};9use jrsonnet_evaluator::{10 apply_tla,11 error::{Error as JrError, ErrorKind},12 throw, ResultExt, State, Val,13};1415#[cfg(feature = "mimalloc")]16#[global_allocator]17static GLOBAL: mimallocator::Mimalloc = mimallocator::Mimalloc;1819#[derive(Parser)]20enum SubOpts {21 /// Generate completions for specified shell22 Generate {23 /// Target shell name24 shell: Shell,25 },26}2728#[derive(Parser)]29#[clap(next_help_heading = "DEBUG")]30struct DebugOpts {31 /// Required OS stack size.32 /// This shouldn't be changed unless jrsonnet is failing with stack overflow error.33 #[clap(long, name = "size")]34 pub os_stack: Option<usize>,35}3637#[derive(Parser)]38#[clap(next_help_heading = "INPUT")]39struct InputOpts {40 /// Treat input as code, evaluate them instead of reading file41 #[clap(long, short = 'e')]42 pub exec: bool,4344 /// Path to the file to be compiled if `--evaluate` is unset, otherwise code itself45 pub input: Option<String>,46}4748#[derive(Parser)]49#[clap(args_conflicts_with_subcommands = true, disable_version_flag = true)]50struct Opts {51 #[clap(subcommand)]52 sub: Option<SubOpts>,5354 #[clap(flatten)]55 input: InputOpts,56 #[clap(flatten)]57 general: GeneralOpts,5859 #[clap(flatten)]60 trace: TraceOpts,61 #[clap(flatten)]62 manifest: ManifestOpts,63 #[clap(flatten)]64 output: OutputOpts,65 #[clap(flatten)]66 debug: DebugOpts,67}6869fn main() {70 let opts: Opts = Opts::parse();7172 if let Some(sub) = opts.sub {73 match sub {74 SubOpts::Generate { shell } => {75 use clap_complete::generate;76 let app = &mut Opts::command();77 let buf = &mut std::io::stdout();78 generate(shell, app, "jrsonnet", buf);79 std::process::exit(0)80 }81 }82 }8384 let success = if let Some(size) = opts.debug.os_stack {85 std::thread::Builder::new()86 .stack_size(size * 1024 * 1024)87 .spawn(|| main_catch(opts))88 .expect("new thread spawned")89 .join()90 .expect("thread finished successfully")91 } else {92 main_catch(opts)93 };94 if !success {95 std::process::exit(1);96 }97}9899#[derive(thiserror::Error, Debug)]100enum Error {101 // Handled differently102 #[error("evaluation error")]103 Evaluation(JrError),104 #[error("io error")]105 Io(#[from] std::io::Error),106 #[error("input is not utf8 encoded")]107 Utf8(#[from] std::str::Utf8Error),108 #[error("missing input argument")]109 MissingInputArgument,110}111impl From<JrError> for Error {112 fn from(e: JrError) -> Self {113 Self::Evaluation(e)114 }115}116impl From<ErrorKind> for Error {117 fn from(e: ErrorKind) -> Self {118 Self::from(JrError::from(e))119 }120}121122fn main_catch(opts: Opts) -> bool {123 let s = State::default();124 let trace = opts125 .trace126 .configure(&s)127 .expect("this configurator doesn't fail");128 if let Err(e) = main_real(&s, opts) {129 if let Error::Evaluation(e) = e {130 let mut out = String::new();131 trace.write_trace(&mut out, &e).expect("format error");132 eprintln!("{out}")133 } else {134 eprintln!("{}", e);135 }136 return false;137 }138 true139}140141fn main_real(s: &State, opts: Opts) -> Result<(), Error> {142 let (tla, _gc_guard) = opts.general.configure(s)?;143 let manifest_format = opts.manifest.configure(s)?;144145 let input = opts.input.input.ok_or(Error::MissingInputArgument)?;146 let val = if opts.input.exec {147 s.evaluate_snippet("<cmdline>".to_owned(), &input as &str)?148 } else if input == "-" {149 let mut input = Vec::new();150 std::io::stdin().read_to_end(&mut input)?;151 let input_str = std::str::from_utf8(&input)?;152 s.evaluate_snippet("<stdin>".to_owned(), input_str)?153 } else {154 s.import(&input)?155 };156157 let val = apply_tla(s.clone(), &tla, val)?;158159 if let Some(multi) = opts.output.multi {160 if opts.output.create_output_dirs {161 let mut dir = multi.clone();162 dir.pop();163 create_dir_all(dir)?;164 }165 let Val::Obj(obj) = val else {166 throw!("value should be object for --multi manifest, got {}", val.value_type())167 };168 for (field, data) in obj.iter(169 #[cfg(feature = "exp-preserve-order")]170 opts.manifest.preserve_order,171 ) {172 let data = data.with_description(|| format!("getting field {field} for manifest"))?;173174 let mut path = multi.clone();175 path.push(&field as &str);176 if opts.output.create_output_dirs {177 let mut dir = path.clone();178 dir.pop();179 create_dir_all(dir)?;180 }181 println!("{}", path.to_str().expect("path"));182 let mut file = File::create(path)?;183 writeln!(184 file,185 "{}",186 data.manifest(&manifest_format)187 .with_description(|| format!("manifesting {field}"))?188 )?;189 }190 } else if let Some(path) = opts.output.output_file {191 if opts.output.create_output_dirs {192 let mut dir = path.clone();193 dir.pop();194 create_dir_all(dir)?;195 }196 let mut file = File::create(path)?;197 writeln!(file, "{}", val.manifest(manifest_format)?)?;198 } else {199 let output = val.manifest(manifest_format)?;200 if !output.is_empty() {201 println!("{}", output);202 }203 }204205 Ok(())206}crates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/lib.rs
+++ b/crates/jrsonnet-cli/src/lib.rs
@@ -6,9 +6,7 @@
use std::{env, marker::PhantomData, path::PathBuf};
use clap::Parser;
-use jrsonnet_evaluator::{
- error::Result, stack::StackDepthLimitOverrideGuard, FileImportResolver, State,
-};
+use jrsonnet_evaluator::{error::Result, stack::set_stack_depth_limit, FileImportResolver, State};
use jrsonnet_gcmodule::with_thread_object_space;
pub use manifest::*;
pub use stdlib::*;
@@ -48,7 +46,7 @@
jpath: Vec<PathBuf>,
}
impl ConfigureState for MiscOpts {
- type Guards = StackDepthLimitOverrideGuard;
+ type Guards = ();
fn configure(&self, s: &State) -> Result<Self::Guards> {
let mut library_paths = self.jpath.clone();
library_paths.reverse();
@@ -58,8 +56,8 @@
s.set_import_resolver(Box::new(FileImportResolver::new(library_paths)));
- let _depth_limit = jrsonnet_evaluator::stack::limit_stack_depth(self.max_stack);
- Ok(_depth_limit)
+ set_stack_depth_limit(self.max_stack);
+ Ok(())
}
}
@@ -81,17 +79,16 @@
impl ConfigureState for GeneralOpts {
type Guards = (
- <MiscOpts as ConfigureState>::Guards,
<TlaOpts as ConfigureState>::Guards,
<GcOpts as ConfigureState>::Guards,
);
fn configure(&self, s: &State) -> Result<Self::Guards> {
// Configure trace first, because tla-code/ext-code can throw
- let misc_guards = self.misc.configure(s)?;
+ self.misc.configure(s)?;
let tla_guards = self.tla.configure(s)?;
self.std.configure(s)?;
let gc_guards = self.gc.configure(s)?;
- Ok((misc_guards, tla_guards, gc_guards))
+ Ok((tla_guards, gc_guards))
}
}
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -389,7 +389,7 @@
ctx.super_obj().clone().ok_or(NoSuperFound)?.with_this(
ctx.this()
.clone()
- .expect("if super exists - then this should to"),
+ .expect("if super exists - then this should too"),
),
),
Literal(LiteralType::Dollar) => {
@@ -408,6 +408,21 @@
|| format!("variable <{name}> access"),
|| ctx.binding(name.clone())?.evaluate(),
)?,
+ Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {
+ let name = evaluate(ctx.clone(), index)?;
+ let Val::Str(name) = name else {
+ throw!(ValueIndexMustBeTypeGot(
+ ValType::Obj,
+ ValType::Str,
+ name.value_type(),
+ ))
+ };
+ ctx.super_obj()
+ .clone()
+ .expect("no super found")
+ .get_for(name, ctx.this().clone().expect("no this found"))?
+ .expect("value not found")
+ }
Index(value, index) => match (evaluate(ctx.clone(), value)?, evaluate(ctx, index)?) {
(Val::Obj(v), Val::Str(key)) => State::push(
CallLocation::new(loc),
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -128,7 +128,7 @@
assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,
assertions_ran: RefCell<GcHashSet<ObjValue>>,
this_entries: Cc<GcHashMap<IStr, ObjMember>>,
- value_cache: RefCell<GcHashMap<IStr, CacheValue>>,
+ value_cache: RefCell<GcHashMap<(IStr, Option<WeakObjValue>), CacheValue>>,
}
#[derive(Clone, Trace)]
@@ -387,7 +387,8 @@
pub fn get(&self, key: IStr) -> Result<Option<Val>> {
self.run_assertions()?;
- if let Some(v) = self.0.value_cache.borrow().get(&key) {
+ let cache_key = (key.clone(), None);
+ if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {
return Ok(match v {
CacheValue::Cached(v) => Some(v.clone()),
CacheValue::NotFound => None,
@@ -398,21 +399,48 @@
self.0
.value_cache
.borrow_mut()
- .insert(key.clone(), CacheValue::Pending);
+ .insert(cache_key.clone(), CacheValue::Pending);
let value = self
- .get_raw(
- key.clone(),
- self.0.this.clone().unwrap_or_else(|| self.clone()),
- )
+ .get_raw(key, self.0.this.clone().unwrap_or_else(|| self.clone()))
.map_err(|e| {
self.0
.value_cache
.borrow_mut()
- .insert(key.clone(), CacheValue::Errored(e.clone()));
+ .insert(cache_key.clone(), CacheValue::Errored(e.clone()));
e
})?;
self.0.value_cache.borrow_mut().insert(
- key,
+ cache_key,
+ value
+ .as_ref()
+ .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),
+ );
+ Ok(value)
+ }
+ pub fn get_for(&self, key: IStr, this: Self) -> Result<Option<Val>> {
+ self.run_assertions()?;
+ let cache_key = (key.clone(), Some(this.clone().downgrade()));
+ if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {
+ return Ok(match v {
+ CacheValue::Cached(v) => Some(v.clone()),
+ CacheValue::NotFound => None,
+ CacheValue::Pending => throw!(InfiniteRecursionDetected),
+ CacheValue::Errored(e) => return Err(e.clone()),
+ });
+ }
+ self.0
+ .value_cache
+ .borrow_mut()
+ .insert(cache_key.clone(), CacheValue::Pending);
+ let value = self.get_raw(key, this).map_err(|e| {
+ self.0
+ .value_cache
+ .borrow_mut()
+ .insert(cache_key.clone(), CacheValue::Errored(e.clone()));
+ e
+ })?;
+ self.0.value_cache.borrow_mut().insert(
+ cache_key,
value
.as_ref()
.map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),