git.delta.rocks / jrsonnet / refs/commits / 9e9c9376494e

difftreelog

perf rc imports

Лач2020-06-26parent: #4a1c9d0.patch.diff
in: master

3 files changed

modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
before · cmds/jrsonnet/src/main.rs
1pub mod location;23use clap::Clap;4use jsonnet_evaluator::{EvaluationSettings, EvaluationState, LocError, StackTrace, Val};5use jsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParserSettings};6use location::{offset_to_location, CodeLocation};7use std::env::current_dir;8use std::{collections::HashMap, path::PathBuf, rc::Rc, str::FromStr};910enum Format {11	None,12	Json,13	Yaml,14}1516impl FromStr for Format {17	type Err = &'static str;18	fn from_str(s: &str) -> Result<Self, Self::Err> {19		Ok(match s {20			"none" => Format::None,21			"json" => Format::Json,22			"yaml" => Format::Yaml,23			_ => return Err("no such format"),24		})25	}26}2728#[derive(PartialEq)]29enum TraceFormat {30	CppJsonnet,31	GoJsonnet,32	Custom,33}34impl FromStr for TraceFormat {35	type Err = &'static str;36	fn from_str(s: &str) -> Result<Self, Self::Err> {37		Ok(match s {38			"cpp" => TraceFormat::CppJsonnet,39			"go" => TraceFormat::GoJsonnet,40			"default" => TraceFormat::Custom,41			_ => return Err("no such format"),42		})43	}44}4546#[derive(Clone)]47struct ExtStr {48	name: String,49	value: String,50}51impl FromStr for ExtStr {52	type Err = &'static str;53	fn from_str(s: &str) -> Result<Self, Self::Err> {54		let out: Vec<_> = s.split('=').collect();55		match out.len() {56			1 => Ok(ExtStr {57				name: out[0].to_owned(),58				value: std::env::var(out[0]).or(Err("missing env var"))?,59			}),60			2 => Ok(ExtStr {61				name: out[0].to_owned(),62				value: out[1].to_owned(),63			}),64			_ => Err("bad ext-str syntax"),65		}66	}67}6869#[derive(Clap)]70#[clap(version = "0.1.0", author = "Lach <iam@lach.pw>")]71struct Opts {72	#[clap(long, about = "Disable global std variable")]73	no_stdlib: bool,74	#[clap(long, about = "Add external string")]75	ext_str: Vec<ExtStr>,76	#[clap(long, about = "Add external string from code")]77	ext_code: Vec<ExtStr>,78	#[clap(long, about = "Add TLA")]79	tla_str: Vec<ExtStr>,80	#[clap(long, about = "Add TLA from code")]81	tla_code: Vec<ExtStr>,82	#[clap(long, short = "f", default_value = "json", possible_values = &["none", "json", "yaml"], about = "Output format, wraps resulting value to corresponding std.manifest call")]83	format: Format,84	#[clap(long, default_value = "default", possible_values = &["cpp", "go", "default"], about = "Emulated needed stacktrace display")]85	trace_format: TraceFormat,8687	#[clap(88		long,89		short = "s",90		default_value = "200",91		about = "Number of allowed stack frames"92	)]93	max_stack: usize,94	#[clap(95		long,96		short = "t",97		default_value = "20",98		about = "Max length of stack trace before cropping"99	)]100	max_trace: usize,101102	#[clap(long, short = "J", about = "Library search dir")]103	jpath: Vec<PathBuf>,104105	#[clap(106		long,107		default_value = "3",108		about = "When using --format, this option specifies string to pad output with"109	)]110	line_padding: usize,111112	#[clap(about = "File to compile", index = 1)]113	input: String,114}115116fn main() {117	let opts: Opts = Opts::parse();118	let evaluator = jsonnet_evaluator::EvaluationState::new(119		EvaluationSettings {120			max_stack_trace_size: opts.max_trace,121			max_stack_frames: opts.max_stack,122		},123		Box::new(jsonnet_evaluator::FileImportResolver {124			library_paths: opts.jpath.clone(),125		}),126	);127	if !opts.no_stdlib {128		evaluator.with_stdlib();129	}130	for ExtStr { name, value } in opts.ext_str.iter().cloned() {131		evaluator.add_ext_var(name.into(), Val::Str(value.into()));132	}133	for ExtStr { name, value } in opts.ext_code.iter().cloned() {134		evaluator.add_ext_var(name.into(), evaluator.parse_evaluate_raw(&value).unwrap());135	}136	let mut input = current_dir().unwrap();137	input.push(opts.input.clone());138	let code_string = String::from_utf8(std::fs::read(opts.input.clone()).unwrap()).unwrap();139	if let Err(e) = evaluator.add_file(input.clone(), code_string.clone()) {140		print_syntax_error(e, &input, &code_string);141		std::process::exit(1);142	}143	let result = evaluator.evaluate_file(&input);144	match result {145		Ok(v) => {146			let v = match v {147				Val::Func(f) => {148					let mut desc_map = HashMap::new();149					for ExtStr { name, value } in opts.tla_str.iter().cloned() {150						desc_map.insert(name, el!(Expr::Str(value.into())));151					}152					for ExtStr { name, value } in opts.tla_code.iter().cloned() {153						desc_map.insert(154							name,155							jsonnet_parser::parse(156								&value,157								&ParserSettings {158									file_name: Rc::new(PathBuf::new()),159									loc_data: false,160								},161							)162							.unwrap(),163						);164					}165					evaluator.add_global("__tmp__tlf__".into(), Val::Func(f));166					evaluator167						.evaluate_raw(el!(Expr::Apply(168							el!(Expr::Var("__tmp__tlf__".into())),169							ArgsDesc(desc_map.into_iter().map(|(k, v)| Arg(Some(k), v)).collect()),170							false,171						)))172						.unwrap()173				}174				v => v,175			};176			let v = evaluator.run_in_state(|| match opts.format {177				Format::Json => Ok(Val::Str(v.into_json(opts.line_padding)?)),178				Format::Yaml => {179					evaluator.add_global("__tmp__to_yaml__".into(), v);180					evaluator.parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \"  \")")181				}182				_ => Ok(v),183			});184			let v = match v {185				Ok(v) => v,186				Err(err) => {187					print_error(&err, evaluator, &opts);188					std::process::exit(1);189				}190			};191			match v {192				Val::Str(s) => println!("{}", s),193				Val::Num(n) => println!("{}", n),194				_v => eprintln!(195					"jsonnet output is not a string.\nDid you forgot to set --format, or wrap your data with std.manifestJson?"196				),197			}198		}199		Err(err) => {200			print_error(&err, evaluator, &opts);201			std::process::exit(1);202		}203	}204}205206fn print_error(err: &LocError, evaluator: EvaluationState, opts: &Opts) {207	println!("Error: {:?}", err.0);208	print_trace(&(err.1), evaluator, &opts);209}210211fn print_syntax_error(error: jsonnet_parser::ParseError, file: &PathBuf, code: &str) {212	use annotate_snippets::{213		display_list::{DisplayList, FormatOptions},214		snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},215	};216	//&("Expected: ".to_owned() + error.expected)217	let origin = file.to_str().unwrap();218	let error_message = format!("Expected: {}", error.expected);219	let snippet = Snippet {220		opt: FormatOptions {221			color: true,222			..Default::default()223		},224		title: Some(Annotation {225			label: Some(&error_message),226			id: None,227			annotation_type: AnnotationType::Error,228		}),229		footer: vec![],230		slices: vec![Slice {231			source: &code,232			line_start: 1,233			origin: Some(origin),234			fold: false,235			annotations: vec![SourceAnnotation {236				label: "At this position",237				annotation_type: AnnotationType::Error,238				range: (error.location.offset, error.location.offset + 1),239			}],240		}],241	};242243	let dl = DisplayList::from(snippet);244	println!("{}", dl);245}246247fn print_trace(trace: &StackTrace, evaluator: EvaluationState, opts: &Opts) {248	use annotate_snippets::{249		display_list::{DisplayList, FormatOptions},250		snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},251	};252	for item in trace.0.iter() {253		let desc = &item.1;254		let source = item.0.clone();255		let code = evaluator.get_source(&source.0);256		if code.is_none() {257			continue;258		}259		let code = code.unwrap();260		let start_end = offset_to_location(&code, &[source.1, source.2]);261		if opts.trace_format == TraceFormat::Custom {262			let source_fragment: String = code263				.chars()264				.skip(start_end[0].line_start_offset)265				.take(start_end[1].line_end_offset - start_end[0].line_start_offset)266				.collect();267			let snippet = Snippet {268				opt: FormatOptions {269					color: true,270					..Default::default()271				},272				title: Some(Annotation {273					label: Some(&item.1),274					id: None,275					annotation_type: AnnotationType::Error,276				}),277				footer: vec![],278				slices: vec![Slice {279					source: &source_fragment,280					line_start: start_end[0].line,281					origin: Some(&source.0.to_str().unwrap()),282					fold: false,283					annotations: vec![SourceAnnotation {284						label: desc,285						annotation_type: AnnotationType::Error,286						range: (287							source.1 - start_end[0].line_start_offset,288							source.2 - start_end[0].line_start_offset,289						),290					}],291				}],292			};293294			let dl = DisplayList::from(snippet);295			println!("{}", dl);296		} else {297			print_jsonnet_pair(298				source.0.to_str().unwrap(),299				&start_end[0],300				&start_end[1],301				opts.trace_format == TraceFormat::GoJsonnet,302			);303		}304	}305}306307fn print_jsonnet_pair(file: &str, start: &CodeLocation, end: &CodeLocation, is_go: bool) {308	if is_go {309		print!("        ");310	} else {311		print!("  ");312	}313	print!("{}:", file);314	if start.line == end.line {315		// IDK why, but this is the behavior original jsonnet cpp impl shows316		if start.column == end.column || !is_go && start.column + 1 == end.column {317			println!("{}:{}", start.line, end.column)318		} else {319			println!("{}:{}-{}", start.line, start.column, end.column);320		}321	} else {322		println!(323			"({}:{})-({}:{})",324			start.line, end.column, start.line, end.column325		);326	}327}
after · cmds/jrsonnet/src/main.rs
1pub mod location;23use clap::Clap;4use jsonnet_evaluator::{EvaluationSettings, EvaluationState, LocError, StackTrace, Val};5use jsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParserSettings};6use location::{offset_to_location, CodeLocation};7use std::env::current_dir;8use std::{collections::HashMap, path::PathBuf, rc::Rc, str::FromStr};910enum Format {11	None,12	Json,13	Yaml,14}1516impl FromStr for Format {17	type Err = &'static str;18	fn from_str(s: &str) -> Result<Self, Self::Err> {19		Ok(match s {20			"none" => Format::None,21			"json" => Format::Json,22			"yaml" => Format::Yaml,23			_ => return Err("no such format"),24		})25	}26}2728#[derive(PartialEq)]29enum TraceFormat {30	CppJsonnet,31	GoJsonnet,32	Custom,33}34impl FromStr for TraceFormat {35	type Err = &'static str;36	fn from_str(s: &str) -> Result<Self, Self::Err> {37		Ok(match s {38			"cpp" => TraceFormat::CppJsonnet,39			"go" => TraceFormat::GoJsonnet,40			"default" => TraceFormat::Custom,41			_ => return Err("no such format"),42		})43	}44}4546#[derive(Clone)]47struct ExtStr {48	name: String,49	value: String,50}51impl FromStr for ExtStr {52	type Err = &'static str;53	fn from_str(s: &str) -> Result<Self, Self::Err> {54		let out: Vec<_> = s.split('=').collect();55		match out.len() {56			1 => Ok(ExtStr {57				name: out[0].to_owned(),58				value: std::env::var(out[0]).or(Err("missing env var"))?,59			}),60			2 => Ok(ExtStr {61				name: out[0].to_owned(),62				value: out[1].to_owned(),63			}),64			_ => Err("bad ext-str syntax"),65		}66	}67}6869#[derive(Clap)]70#[clap(version = "0.1.0", author = "Lach <iam@lach.pw>")]71struct Opts {72	#[clap(long, about = "Disable global std variable")]73	no_stdlib: bool,74	#[clap(long, about = "Add external string")]75	ext_str: Vec<ExtStr>,76	#[clap(long, about = "Add external string from code")]77	ext_code: Vec<ExtStr>,78	#[clap(long, about = "Add TLA")]79	tla_str: Vec<ExtStr>,80	#[clap(long, about = "Add TLA from code")]81	tla_code: Vec<ExtStr>,82	#[clap(long, short = "f", default_value = "json", possible_values = &["none", "json", "yaml"], about = "Output format, wraps resulting value to corresponding std.manifest call")]83	format: Format,84	#[clap(long, default_value = "default", possible_values = &["cpp", "go", "default"], about = "Emulated needed stacktrace display")]85	trace_format: TraceFormat,8687	#[clap(88		long,89		short = "s",90		default_value = "200",91		about = "Number of allowed stack frames"92	)]93	max_stack: usize,94	#[clap(95		long,96		short = "t",97		default_value = "20",98		about = "Max length of stack trace before cropping"99	)]100	max_trace: usize,101102	#[clap(long, short = "J", about = "Library search dir")]103	jpath: Vec<PathBuf>,104105	#[clap(106		long,107		default_value = "3",108		about = "When using --format, this option specifies string to pad output with"109	)]110	line_padding: usize,111112	#[clap(about = "File to compile", index = 1)]113	input: String,114}115116fn main() {117	let opts: Opts = Opts::parse();118	let evaluator = jsonnet_evaluator::EvaluationState::new(119		EvaluationSettings {120			max_stack_trace_size: opts.max_trace,121			max_stack_frames: opts.max_stack,122		},123		Box::new(jsonnet_evaluator::FileImportResolver {124			library_paths: opts.jpath.clone(),125		}),126	);127	if !opts.no_stdlib {128		evaluator.with_stdlib();129	}130	for ExtStr { name, value } in opts.ext_str.iter().cloned() {131		evaluator.add_ext_var(name.into(), Val::Str(value.into()));132	}133	for ExtStr { name, value } in opts.ext_code.iter().cloned() {134		evaluator.add_ext_var(name.into(), evaluator.parse_evaluate_raw(&value).unwrap());135	}136	let mut input = current_dir().unwrap();137	input.push(opts.input.clone());138	let code_string = String::from_utf8(std::fs::read(opts.input.clone()).unwrap()).unwrap();139	if let Err(e) = evaluator.add_file(Rc::new(input.clone()), code_string.clone().into()) {140		print_syntax_error(e, &input, &code_string);141		std::process::exit(1);142	}143	let result = evaluator.evaluate_file(&input);144	match result {145		Ok(v) => {146			let v = match v {147				Val::Func(f) => {148					let mut desc_map = HashMap::new();149					for ExtStr { name, value } in opts.tla_str.iter().cloned() {150						desc_map.insert(name, el!(Expr::Str(value.into())));151					}152					for ExtStr { name, value } in opts.tla_code.iter().cloned() {153						desc_map.insert(154							name,155							jsonnet_parser::parse(156								&value,157								&ParserSettings {158									file_name: Rc::new(PathBuf::new()),159									loc_data: false,160								},161							)162							.unwrap(),163						);164					}165					evaluator.add_global("__tmp__tlf__".into(), Val::Func(f));166					evaluator167						.evaluate_raw(el!(Expr::Apply(168							el!(Expr::Var("__tmp__tlf__".into())),169							ArgsDesc(desc_map.into_iter().map(|(k, v)| Arg(Some(k), v)).collect()),170							false,171						)))172						.unwrap()173				}174				v => v,175			};176			let v = evaluator.run_in_state(|| match opts.format {177				Format::Json => Ok(Val::Str(v.into_json(opts.line_padding)?)),178				Format::Yaml => {179					evaluator.add_global("__tmp__to_yaml__".into(), v);180					evaluator.parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \"  \")")181				}182				_ => Ok(v),183			});184			let v = match v {185				Ok(v) => v,186				Err(err) => {187					print_error(&err, evaluator, &opts);188					std::process::exit(1);189				}190			};191			match v {192				Val::Str(s) => println!("{}", s),193				Val::Num(n) => println!("{}", n),194				_v => eprintln!(195					"jsonnet output is not a string.\nDid you forgot to set --format, or wrap your data with std.manifestJson?"196				),197			}198		}199		Err(err) => {200			print_error(&err, evaluator, &opts);201			std::process::exit(1);202		}203	}204}205206fn print_error(err: &LocError, evaluator: EvaluationState, opts: &Opts) {207	println!("Error: {:?}", err.0);208	print_trace(&(err.1), evaluator, &opts);209}210211fn print_syntax_error(error: jsonnet_parser::ParseError, file: &PathBuf, code: &str) {212	use annotate_snippets::{213		display_list::{DisplayList, FormatOptions},214		snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},215	};216	//&("Expected: ".to_owned() + error.expected)217	let origin = file.to_str().unwrap();218	let error_message = format!("Expected: {}", error.expected);219	let snippet = Snippet {220		opt: FormatOptions {221			color: true,222			..Default::default()223		},224		title: Some(Annotation {225			label: Some(&error_message),226			id: None,227			annotation_type: AnnotationType::Error,228		}),229		footer: vec![],230		slices: vec![Slice {231			source: &code,232			line_start: 1,233			origin: Some(origin),234			fold: false,235			annotations: vec![SourceAnnotation {236				label: "At this position",237				annotation_type: AnnotationType::Error,238				range: (error.location.offset, error.location.offset + 1),239			}],240		}],241	};242243	let dl = DisplayList::from(snippet);244	println!("{}", dl);245}246247fn print_trace(trace: &StackTrace, evaluator: EvaluationState, opts: &Opts) {248	use annotate_snippets::{249		display_list::{DisplayList, FormatOptions},250		snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},251	};252	for item in trace.0.iter() {253		let desc = &item.1;254		let source = item.0.clone();255		let code = evaluator.get_source(&source.0);256		if code.is_none() {257			continue;258		}259		let code = code.unwrap();260		let start_end = offset_to_location(&code, &[source.1, source.2]);261		if opts.trace_format == TraceFormat::Custom {262			let source_fragment: String = code263				.chars()264				.skip(start_end[0].line_start_offset)265				.take(start_end[1].line_end_offset - start_end[0].line_start_offset)266				.collect();267			let snippet = Snippet {268				opt: FormatOptions {269					color: true,270					..Default::default()271				},272				title: Some(Annotation {273					label: Some(&item.1),274					id: None,275					annotation_type: AnnotationType::Error,276				}),277				footer: vec![],278				slices: vec![Slice {279					source: &source_fragment,280					line_start: start_end[0].line,281					origin: Some(&source.0.to_str().unwrap()),282					fold: false,283					annotations: vec![SourceAnnotation {284						label: desc,285						annotation_type: AnnotationType::Error,286						range: (287							source.1 - start_end[0].line_start_offset,288							source.2 - start_end[0].line_start_offset,289						),290					}],291				}],292			};293294			let dl = DisplayList::from(snippet);295			println!("{}", dl);296		} else {297			print_jsonnet_pair(298				source.0.to_str().unwrap(),299				&start_end[0],300				&start_end[1],301				opts.trace_format == TraceFormat::GoJsonnet,302			);303		}304	}305}306307fn print_jsonnet_pair(file: &str, start: &CodeLocation, end: &CodeLocation, is_go: bool) {308	if is_go {309		print!("        ");310	} else {311		print!("  ");312	}313	print!("{}:", file);314	if start.line == end.line {315		// IDK why, but this is the behavior original jsonnet cpp impl shows316		if start.column == end.column || !is_go && start.column + 1 == end.column {317			println!("{}:{}", start.line, end.column)318		} else {319			println!("{}:{}-{}", start.line, start.column, end.column);320		}321	} else {322		println!(323			"({}:{})-({}:{})",324			start.line, end.column, start.line, end.column325		);326	}327}
modifiedcrates/jsonnet-evaluator/src/import.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/import.rs
+++ b/crates/jsonnet-evaluator/src/import.rs
@@ -3,19 +3,19 @@
 use fs::File;
 use std::fs;
 use std::io::Read;
-use std::{cell::RefCell, collections::HashMap, path::PathBuf};
+use std::{cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc};
 
 pub trait ImportResolver {
-	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf>;
-	fn load_file_contents(&self, resolved: &PathBuf) -> Result<String>;
+	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>>;
+	fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>>;
 }
 
 pub struct DummyImportResolver;
 impl ImportResolver for DummyImportResolver {
-	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf> {
+	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
 		create_error(Error::ImportNotSupported(from.clone(), path.clone()))
 	}
-	fn load_file_contents(&self, _resolved: &PathBuf) -> Result<String> {
+	fn load_file_contents(&self, _resolved: &PathBuf) -> Result<Rc<str>> {
 		// Can be only caused by library direct consumer, not by supplied jsonnet
 		panic!("dummy resolver can't load any file")
 	}
@@ -30,23 +30,23 @@
 	pub library_paths: Vec<PathBuf>,
 }
 impl ImportResolver for FileImportResolver {
-	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf> {
+	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
 		let mut new_path = from.clone();
 		new_path.push(path);
 		if new_path.exists() {
-			Ok(new_path)
+			Ok(Rc::new(new_path))
 		} else {
 			for library_path in self.library_paths.iter() {
 				let mut cloned = library_path.clone();
 				cloned.push(path);
 				if cloned.exists() {
-					return Ok(cloned);
+					return Ok(Rc::new(cloned));
 				}
 			}
 			create_error(Error::ImportFileNotFound(from.clone(), path.clone()))
 		}
 	}
-	fn load_file_contents(&self, id: &PathBuf) -> Result<String> {
+	fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {
 		let mut file = File::open(id).map_err(|_e| {
 			create_error::<()>(Error::ResolvedFileNotFound(id.clone()))
 				.err()
@@ -58,24 +58,24 @@
 				.err()
 				.unwrap()
 		})?;
-		Ok(out)
+		Ok(out.into())
 	}
 }
 
 pub struct CachingImportResolver {
-	resolution_cache: RefCell<HashMap<(PathBuf, PathBuf), Result<PathBuf>>>,
-	loading_cache: RefCell<HashMap<PathBuf, Result<String>>>,
+	resolution_cache: RefCell<HashMap<(PathBuf, PathBuf), Result<Rc<PathBuf>>>>,
+	loading_cache: RefCell<HashMap<PathBuf, Result<Rc<str>>>>,
 	inner: Box<dyn ImportResolver>,
 }
 impl ImportResolver for CachingImportResolver {
-	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf> {
+	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
 		self.resolution_cache
 			.borrow_mut()
 			.entry((from.clone(), path.clone()))
 			.or_insert_with(|| self.inner.resolve_file(from, path))
 			.clone()
 	}
-	fn load_file_contents(&self, resolved: &PathBuf) -> Result<String> {
+	fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {
 		self.loading_cache
 			.borrow_mut()
 			.entry(resolved.clone())
modifiedcrates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -57,15 +57,15 @@
 	}
 }
 
-pub struct FileData(String, LocExpr, Option<Val>);
+pub struct FileData(Rc<str>, LocExpr, Option<Val>);
 #[derive(Default)]
 pub struct EvaluationStateInternals {
 	/// Used for stack-overflows and stacktraces
 	stack: RefCell<Vec<StackTraceElement>>,
 	/// Contains file source codes and evaluated results for imports and pretty
 	/// printing stacktraces
-	files: RefCell<HashMap<PathBuf, FileData>>,
-	str_files: RefCell<HashMap<PathBuf, Rc<str>>>,
+	files: RefCell<HashMap<Rc<PathBuf>, FileData>>,
+	str_files: RefCell<HashMap<Rc<PathBuf>, Rc<str>>>,
 	globals: RefCell<HashMap<Rc<str>, Val>>,
 
 	/// Values to use with std.extVar
@@ -109,7 +109,11 @@
 			..Default::default()
 		}))
 	}
-	pub fn add_file(&self, name: PathBuf, code: String) -> std::result::Result<(), ParseError> {
+	pub fn add_file(
+		&self,
+		name: Rc<PathBuf>,
+		code: Rc<str>,
+	) -> std::result::Result<(), ParseError> {
 		self.0.files.borrow_mut().insert(
 			name.clone(),
 			FileData(
@@ -117,7 +121,7 @@
 				parse(
 					&code,
 					&ParserSettings {
-						file_name: Rc::new(name),
+						file_name: name,
 						loc_data: true,
 					},
 				)?,
@@ -129,8 +133,8 @@
 	}
 	pub fn add_parsed_file(
 		&self,
-		name: PathBuf,
-		code: String,
+		name: Rc<PathBuf>,
+		code: Rc<str>,
 		parsed: LocExpr,
 	) -> std::result::Result<(), ()> {
 		self.0
@@ -140,7 +144,7 @@
 
 		Ok(())
 	}
-	pub fn get_source(&self, name: &PathBuf) -> Option<String> {
+	pub fn get_source(&self, name: &PathBuf) -> Option<Rc<str>> {
 		let ro_map = self.0.files.borrow();
 		ro_map.get(name).map(|value| value.0.clone())
 	}
@@ -221,12 +225,13 @@
 	}
 
 	pub fn with_stdlib(&self) -> &Self {
+		let std_path = Rc::new(PathBuf::from("std.jsonnet"));
 		self.run_in_state(|| {
 			use jsonnet_stdlib::STDLIB_STR;
 			if cfg!(feature = "serialized-stdlib") {
 				self.add_parsed_file(
-					PathBuf::from("std.jsonnet"),
-					STDLIB_STR.to_owned(),
+					std_path,
+					STDLIB_STR.to_owned().into(),
 					bincode::deserialize(include_bytes!(concat!(
 						env!("OUT_DIR"),
 						"/stdlib.bincode"
@@ -235,7 +240,7 @@
 				)
 				.unwrap();
 			} else {
-				self.add_file(PathBuf::from("std.jsonnet"), STDLIB_STR.to_owned())
+				self.add_file(std_path, STDLIB_STR.to_owned().into())
 					.unwrap();
 			}
 			let val = self.evaluate_file(&PathBuf::from("std.jsonnet")).unwrap();