difftreelog
feat importbin
in: master
9 files changed
bindings/jsonnet/src/import.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/import.rs
+++ b/bindings/jsonnet/src/import.rs
@@ -2,7 +2,7 @@
use jrsonnet_evaluator::{
error::{Error::*, Result},
- throw, EvaluationState, IStr, ImportResolver,
+ throw, EvaluationState, ImportResolver,
};
use std::{
any::Any,
@@ -29,8 +29,7 @@
pub struct CallbackImportResolver {
cb: JsonnetImportCallback,
ctx: *mut c_void,
-
- out: RefCell<HashMap<PathBuf, IStr>>,
+ out: RefCell<HashMap<PathBuf, Vec<u8>>>,
}
impl ImportResolver for CallbackImportResolver {
fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {
@@ -75,9 +74,10 @@
Ok(found_here_buf.into())
}
- fn load_file_contents(&self, resolved: &Path) -> Result<IStr> {
+ fn load_file_contents(&self, resolved: &Path) -> Result<Vec<u8>> {
Ok(self.out.borrow().get(resolved).unwrap().clone())
}
+
unsafe fn as_any(&self) -> &dyn Any {
self
}
@@ -124,12 +124,12 @@
throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))
}
}
- fn load_file_contents(&self, id: &Path) -> Result<IStr> {
+ fn load_file_contents(&self, id: &Path) -> Result<Vec<u8>> {
let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;
- let mut out = String::new();
- file.read_to_string(&mut out)
- .map_err(|_e| ImportBadFileUtf8(id.to_owned()))?;
- Ok(out.into())
+ let mut out = Vec::new();
+ file.read_to_end(&mut out)
+ .map_err(|e| ImportIo(e.to_string()))?;
+ Ok(out)
}
unsafe fn as_any(&self) -> &dyn Any {
self
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -82,6 +82,8 @@
ResolvedFileNotFound(PathBuf),
#[error("imported file is not valid utf-8: {0:?}")]
ImportBadFileUtf8(PathBuf),
+ #[error("import io error: {0}")]
+ ImportIo(String),
#[error("tried to import {1} from {0}, but imports is not supported")]
ImportNotSupported(PathBuf, PathBuf),
#[error(
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth1use std::convert::TryFrom;23use crate::{4 builtin::{std_slice, BUILTINS},5 error::Error::*,6 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},7 gc::TraceBox,8 push_frame, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,9 FutureWrapper, GcHashMap, LazyBinding, LazyVal, LazyValValue, ObjValue, ObjValueBuilder,10 ObjectAssertion, Result, Val,11};12use gcmodule::{Cc, Trace};13use jrsonnet_interner::IStr;14use jrsonnet_parser::{15 ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, ForSpecData,16 IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,17};18use jrsonnet_types::ValType;19pub mod operator;2021pub fn evaluate_binding_in_future(22 b: &BindSpec,23 context_creator: FutureWrapper<Context>,24) -> LazyVal {25 let b = b.clone();26 if let Some(params) = &b.params {27 let params = params.clone();2829 #[derive(Trace)]30 struct LazyMethodBinding {31 context_creator: FutureWrapper<Context>,32 name: IStr,33 params: ParamsDesc,34 value: LocExpr,35 }36 impl LazyValValue for LazyMethodBinding {37 fn get(self: Box<Self>) -> Result<Val> {38 Ok(evaluate_method(39 self.context_creator.unwrap(),40 self.name,41 self.params,42 self.value,43 ))44 }45 }4647 LazyVal::new(TraceBox(Box::new(LazyMethodBinding {48 context_creator,49 name: b.name.clone(),50 params,51 value: b.value.clone(),52 })))53 } else {54 #[derive(Trace)]55 struct LazyNamedBinding {56 context_creator: FutureWrapper<Context>,57 name: IStr,58 value: LocExpr,59 }60 impl LazyValValue for LazyNamedBinding {61 fn get(self: Box<Self>) -> Result<Val> {62 evaluate_named(self.context_creator.unwrap(), &self.value, self.name)63 }64 }65 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {66 context_creator,67 name: b.name.clone(),68 value: b.value,69 })))70 }71}7273pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, LazyBinding) {74 let b = b.clone();75 if let Some(params) = &b.params {76 let params = params.clone();7778 #[derive(Trace)]79 struct BindableMethodLazyVal {80 this: Option<ObjValue>,81 super_obj: Option<ObjValue>,8283 context_creator: ContextCreator,84 name: IStr,85 params: ParamsDesc,86 value: LocExpr,87 }88 impl LazyValValue for BindableMethodLazyVal {89 fn get(self: Box<Self>) -> Result<Val> {90 Ok(evaluate_method(91 self.context_creator.create(self.this, self.super_obj)?,92 self.name,93 self.params,94 self.value,95 ))96 }97 }9899 #[derive(Trace)]100 struct BindableMethod {101 context_creator: ContextCreator,102 name: IStr,103 params: ParamsDesc,104 value: LocExpr,105 }106 impl Bindable for BindableMethod {107 fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {108 Ok(LazyVal::new(TraceBox(Box::new(BindableMethodLazyVal {109 this,110 super_obj,111112 context_creator: self.context_creator.clone(),113 name: self.name.clone(),114 params: self.params.clone(),115 value: self.value.clone(),116 }))))117 }118 }119120 (121 b.name.clone(),122 LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableMethod {123 context_creator,124 name: b.name.clone(),125 params,126 value: b.value.clone(),127 })))),128 )129 } else {130 #[derive(Trace)]131 struct BindableNamedLazyVal {132 this: Option<ObjValue>,133 super_obj: Option<ObjValue>,134135 context_creator: ContextCreator,136 name: IStr,137 value: LocExpr,138 }139 impl LazyValValue for BindableNamedLazyVal {140 fn get(self: Box<Self>) -> Result<Val> {141 evaluate_named(142 self.context_creator.create(self.this, self.super_obj)?,143 &self.value,144 self.name,145 )146 }147 }148149 #[derive(Trace)]150 struct BindableNamed {151 context_creator: ContextCreator,152 name: IStr,153 value: LocExpr,154 }155 impl Bindable for BindableNamed {156 fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {157 Ok(LazyVal::new(TraceBox(Box::new(BindableNamedLazyVal {158 this,159 super_obj,160161 context_creator: self.context_creator.clone(),162 name: self.name.clone(),163 value: self.value.clone(),164 }))))165 }166 }167168 (169 b.name.clone(),170 LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableNamed {171 context_creator,172 name: b.name.clone(),173 value: b.value.clone(),174 })))),175 )176 }177}178179pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {180 Val::Func(Cc::new(FuncVal::Normal(FuncDesc {181 name,182 ctx,183 params,184 body,185 })))186}187188pub fn evaluate_field_name(189 context: Context,190 field_name: &jrsonnet_parser::FieldName,191) -> Result<Option<IStr>> {192 Ok(match field_name {193 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),194 jrsonnet_parser::FieldName::Dyn(expr) => push_frame(195 Some(&expr.1),196 || "evaluating field name".to_string(),197 || {198 let value = evaluate(context, expr)?;199 if matches!(value, Val::Null) {200 Ok(None)201 } else {202 Ok(Some(IStr::try_from(value)?))203 }204 },205 )?,206 })207}208209pub fn evaluate_comp(210 context: Context,211 specs: &[CompSpec],212 callback: &mut impl FnMut(Context) -> Result<()>,213) -> Result<()> {214 match specs.get(0) {215 None => callback(context)?,216 Some(CompSpec::IfSpec(IfSpecData(cond))) => {217 if bool::try_from(evaluate(context.clone(), cond)?)? {218 evaluate_comp(context, &specs[1..], callback)?219 }220 }221 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {222 Val::Arr(list) => {223 for item in list.iter() {224 evaluate_comp(225 context.clone().with_var(var.clone(), item?.clone()),226 &specs[1..],227 callback,228 )?229 }230 }231 _ => throw!(InComprehensionCanOnlyIterateOverArray),232 },233 }234 Ok(())235}236237pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {238 let new_bindings = FutureWrapper::new();239 let future_this = FutureWrapper::new();240 let context_creator = ContextCreator(context.clone(), new_bindings.clone());241 {242 let mut bindings: GcHashMap<IStr, LazyBinding> = GcHashMap::with_capacity(members.len());243 for (n, b) in members244 .iter()245 .filter_map(|m| match m {246 Member::BindStmt(b) => Some(b.clone()),247 _ => None,248 })249 .map(|b| evaluate_binding(&b, context_creator.clone()))250 {251 bindings.insert(n, b);252 }253 new_bindings.fill(bindings);254 }255256 let mut builder = ObjValueBuilder::new();257 for member in members.iter() {258 match member {259 Member::Field(FieldMember {260 name,261 plus,262 params: None,263 visibility,264 value,265 }) => {266 let name = evaluate_field_name(context.clone(), name)?;267 if name.is_none() {268 continue;269 }270 let name = name.unwrap();271272 #[derive(Trace)]273 struct ObjMemberBinding {274 context_creator: ContextCreator,275 value: LocExpr,276 name: IStr,277 }278 impl Bindable for ObjMemberBinding {279 fn bind(280 &self,281 this: Option<ObjValue>,282 super_obj: Option<ObjValue>,283 ) -> Result<LazyVal> {284 Ok(LazyVal::new_resolved(evaluate_named(285 self.context_creator.create(this, super_obj)?,286 &self.value,287 self.name.clone(),288 )?))289 }290 }291 builder292 .member(name.clone())293 .with_add(*plus)294 .with_visibility(*visibility)295 .with_location(value.1.clone())296 .bindable(TraceBox(Box::new(ObjMemberBinding {297 context_creator: context_creator.clone(),298 value: value.clone(),299 name,300 })));301 }302 Member::Field(FieldMember {303 name,304 params: Some(params),305 value,306 ..307 }) => {308 let name = evaluate_field_name(context.clone(), name)?;309 if name.is_none() {310 continue;311 }312 let name = name.unwrap();313 #[derive(Trace)]314 struct ObjMemberBinding {315 context_creator: ContextCreator,316 value: LocExpr,317 params: ParamsDesc,318 name: IStr,319 }320 impl Bindable for ObjMemberBinding {321 fn bind(322 &self,323 this: Option<ObjValue>,324 super_obj: Option<ObjValue>,325 ) -> Result<LazyVal> {326 Ok(LazyVal::new_resolved(evaluate_method(327 self.context_creator.create(this, super_obj)?,328 self.name.clone(),329 self.params.clone(),330 self.value.clone(),331 )))332 }333 }334 builder335 .member(name.clone())336 .hide()337 .with_location(value.1.clone())338 .bindable(TraceBox(Box::new(ObjMemberBinding {339 context_creator: context_creator.clone(),340 value: value.clone(),341 params: params.clone(),342 name,343 })));344 }345 Member::BindStmt(_) => {}346 Member::AssertStmt(stmt) => {347 #[derive(Trace)]348 struct ObjectAssert {349 context_creator: ContextCreator,350 assert: AssertStmt,351 }352 impl ObjectAssertion for ObjectAssert {353 fn run(354 &self,355 this: Option<ObjValue>,356 super_obj: Option<ObjValue>,357 ) -> Result<()> {358 let ctx = self.context_creator.create(this, super_obj)?;359 evaluate_assert(ctx, &self.assert)360 }361 }362 builder.assert(TraceBox(Box::new(ObjectAssert {363 context_creator: context_creator.clone(),364 assert: stmt.clone(),365 })));366 }367 }368 }369 let this = builder.build();370 future_this.fill(this.clone());371 Ok(this)372}373374pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {375 Ok(match object {376 ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,377 ObjBody::ObjComp(obj) => {378 let future_this = FutureWrapper::new();379 let mut builder = ObjValueBuilder::new();380 evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {381 let new_bindings = FutureWrapper::new();382 let context_creator = ContextCreator(context.clone(), new_bindings.clone());383 let mut bindings: GcHashMap<IStr, LazyBinding> =384 GcHashMap::with_capacity(obj.pre_locals.len() + obj.post_locals.len());385 for (n, b) in obj386 .pre_locals387 .iter()388 .chain(obj.post_locals.iter())389 .map(|b| evaluate_binding(b, context_creator.clone()))390 {391 bindings.insert(n, b);392 }393 new_bindings.fill(bindings.clone());394 let ctx = ctx.extend_unbound(bindings, None, None, None)?;395 let key = evaluate(ctx.clone(), &obj.key)?;396397 match key {398 Val::Null => {}399 Val::Str(n) => {400 #[derive(Trace)]401 struct ObjCompBinding {402 context: Context,403 value: LocExpr,404 }405 impl Bindable for ObjCompBinding {406 fn bind(407 &self,408 this: Option<ObjValue>,409 _super_obj: Option<ObjValue>,410 ) -> Result<LazyVal> {411 Ok(LazyVal::new_resolved(evaluate(412 self.context413 .clone()414 .extend(GcHashMap::new(), None, this, None),415 &self.value,416 )?))417 }418 }419 builder420 .member(n)421 .with_location(obj.value.1.clone())422 .with_add(obj.plus)423 .bindable(TraceBox(Box::new(ObjCompBinding {424 context: ctx,425 value: obj.value.clone(),426 })));427 }428 v => throw!(FieldMustBeStringGot(v.value_type())),429 }430431 Ok(())432 })?;433434 let this = builder.build();435 future_this.fill(this.clone());436 this437 }438 })439}440441pub fn evaluate_apply(442 context: Context,443 value: &LocExpr,444 args: &ArgsDesc,445 loc: Option<&ExprLocation>,446 tailstrict: bool,447) -> Result<Val> {448 let value = evaluate(context.clone(), value)?;449 Ok(match value {450 Val::Func(f) => {451 let body = || f.evaluate(context, loc, args, tailstrict);452 if tailstrict {453 body()?454 } else {455 push_frame(loc, || format!("function <{}> call", f.name()), body)?456 }457 }458 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),459 })460}461462pub fn evaluate_assert(context: Context, assertion: &AssertStmt) -> Result<()> {463 let value = &assertion.0;464 let msg = &assertion.1;465 let assertion_result = push_frame(466 Some(&value.1),467 || "assertion condition".to_owned(),468 || bool::try_from(evaluate(context.clone(), value)?),469 )?;470 if !assertion_result {471 push_frame(472 Some(&value.1),473 || "assertion failure".to_owned(),474 || {475 if let Some(msg) = msg {476 throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));477 } else {478 throw!(AssertionFailed(Val::Null.to_string()?));479 }480 },481 )?482 }483 Ok(())484}485486pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {487 use Expr::*;488 let LocExpr(expr, _loc) = lexpr;489 Ok(match &**expr {490 Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),491 _ => evaluate(context, lexpr)?,492 })493}494495pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {496 use Expr::*;497 let LocExpr(expr, loc) = expr;498 // let bp = with_state(|s| s.0.stop_at.borrow().clone());499 Ok(match &**expr {500 Literal(LiteralType::This) => {501 Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)502 }503 Literal(LiteralType::Super) => Val::Obj(504 context505 .super_obj()506 .clone()507 .ok_or(NoSuperFound)?508 .with_this(context.this().clone().unwrap()),509 ),510 Literal(LiteralType::Dollar) => {511 Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)512 }513 Literal(LiteralType::True) => Val::Bool(true),514 Literal(LiteralType::False) => Val::Bool(false),515 Literal(LiteralType::Null) => Val::Null,516 Parened(e) => evaluate(context, e)?,517 Str(v) => Val::Str(v.clone()),518 Num(v) => Val::new_checked_num(*v)?,519 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,520 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,521 Var(name) => push_frame(522 Some(loc),523 || format!("variable <{}> access", name),524 || context.binding(name.clone())?.evaluate(),525 )?,526 Index(value, index) => {527 match (evaluate(context.clone(), value)?, evaluate(context, index)?) {528 (Val::Obj(v), Val::Str(s)) => {529 let sn = s.clone();530 push_frame(531 Some(loc),532 || format!("field <{}> access", sn),533 || {534 if let Some(v) = v.get(s.clone())? {535 Ok(v)536 } else {537 throw!(NoSuchField(s))538 }539 },540 )?541 }542 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(543 ValType::Obj,544 ValType::Str,545 n.value_type(),546 )),547548 (Val::Arr(v), Val::Num(n)) => {549 if n.fract() > f64::EPSILON {550 throw!(FractionalIndex)551 }552 v.get(n as usize)?553 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?554 }555 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),556 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(557 ValType::Arr,558 ValType::Num,559 n.value_type(),560 )),561562 (Val::Str(s), Val::Num(n)) => Val::Str(563 s.chars()564 .skip(n as usize)565 .take(1)566 .collect::<String>()567 .into(),568 ),569 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(570 ValType::Str,571 ValType::Num,572 n.value_type(),573 )),574575 (v, _) => throw!(CantIndexInto(v.value_type())),576 }577 }578 LocalExpr(bindings, returned) => {579 let mut new_bindings: GcHashMap<IStr, LazyVal> =580 GcHashMap::with_capacity(bindings.len());581 let future_context = Context::new_future();582 for b in bindings {583 new_bindings.insert(584 b.name.clone(),585 evaluate_binding_in_future(b, future_context.clone()),586 );587 }588 let context = context589 .extend_bound(new_bindings)590 .into_future(future_context);591 evaluate(context, &returned.clone())?592 }593 Arr(items) => {594 let mut out = Vec::with_capacity(items.len());595 for item in items {596 // TODO: Implement ArrValue::Lazy with same context for every element?597 #[derive(Trace)]598 struct ArrayElement {599 context: Context,600 item: LocExpr,601 }602 impl LazyValValue for ArrayElement {603 fn get(self: Box<Self>) -> Result<Val> {604 evaluate(self.context, &self.item)605 }606 }607 out.push(LazyVal::new(TraceBox(Box::new(ArrayElement {608 context: context.clone(),609 item: item.clone(),610 }))));611 }612 Val::Arr(out.into())613 }614 ArrComp(expr, comp_specs) => {615 let mut out = Vec::new();616 evaluate_comp(context, comp_specs, &mut |ctx| {617 out.push(evaluate(ctx, expr)?);618 Ok(())619 })?;620 Val::Arr(ArrValue::Eager(Cc::new(out)))621 }622 Obj(body) => Val::Obj(evaluate_object(context, body)?),623 ObjExtend(s, t) => evaluate_add_op(624 &evaluate(context.clone(), s)?,625 &Val::Obj(evaluate_object(context, t)?),626 )?,627 Apply(value, args, tailstrict) => {628 evaluate_apply(context, value, args, Some(loc), *tailstrict)?629 }630 Function(params, body) => {631 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())632 }633 Intrinsic(name) => Val::Func(Cc::new(FuncVal::StaticBuiltin(634 BUILTINS635 .with(|b| b.get(name).copied())636 .ok_or_else(|| IntrinsicNotFound(name.clone()))?,637 ))),638 AssertExpr(assert, returned) => {639 evaluate_assert(context.clone(), assert)?;640 evaluate(context, returned)?641 }642 ErrorStmt(e) => push_frame(643 Some(loc),644 || "error statement".to_owned(),645 || throw!(RuntimeError(IStr::try_from(evaluate(context, e)?)?,)),646 )?,647 IfElse {648 cond,649 cond_then,650 cond_else,651 } => {652 if push_frame(653 Some(loc),654 || "if condition".to_owned(),655 || bool::try_from(evaluate(context.clone(), &cond.0)?),656 )? {657 evaluate(context, cond_then)?658 } else {659 match cond_else {660 Some(v) => evaluate(context, v)?,661 None => Val::Null,662 }663 }664 }665 Slice(value, desc) => {666 let indexable = evaluate(context.clone(), value)?;667668 fn parse_num(669 context: &Context,670 expr: Option<&LocExpr>,671 desc: &'static str,672 ) -> Result<Option<usize>> {673 Ok(match expr {674 Some(s) => evaluate(context.clone(), s)?675 .try_cast_nullable_num(desc)?676 .map(|v| v as usize),677 None => None,678 })679 }680681 let start = parse_num(&context, desc.start.as_ref(), "start")?;682 let end = parse_num(&context, desc.end.as_ref(), "end")?;683 let step = parse_num(&context, desc.step.as_ref(), "step")?;684685 std_slice(indexable.into_indexable()?, start, end, step)?686 }687 Import(path) => {688 let tmp = loc.clone().0;689 let mut import_location = tmp.to_path_buf();690 import_location.pop();691 push_frame(692 Some(loc),693 || format!("import {:?}", path),694 || with_state(|s| s.import_file(&import_location, path)),695 )?696 }697 ImportStr(path) => {698 let tmp = loc.clone().0;699 let mut import_location = tmp.to_path_buf();700 import_location.pop();701 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)702 }703 })704}1use std::convert::TryFrom;23use crate::{4 builtin::{std_slice, BUILTINS},5 error::Error::*,6 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},7 gc::TraceBox,8 push_frame, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,9 FutureWrapper, GcHashMap, LazyBinding, LazyVal, LazyValValue, ObjValue, ObjValueBuilder,10 ObjectAssertion, Result, Val,11};12use gcmodule::{Cc, Trace};13use jrsonnet_interner::IStr;14use jrsonnet_parser::{15 ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, ForSpecData,16 IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,17};18use jrsonnet_types::ValType;19pub mod operator;2021pub fn evaluate_binding_in_future(22 b: &BindSpec,23 context_creator: FutureWrapper<Context>,24) -> LazyVal {25 let b = b.clone();26 if let Some(params) = &b.params {27 let params = params.clone();2829 #[derive(Trace)]30 struct LazyMethodBinding {31 context_creator: FutureWrapper<Context>,32 name: IStr,33 params: ParamsDesc,34 value: LocExpr,35 }36 impl LazyValValue for LazyMethodBinding {37 fn get(self: Box<Self>) -> Result<Val> {38 Ok(evaluate_method(39 self.context_creator.unwrap(),40 self.name,41 self.params,42 self.value,43 ))44 }45 }4647 LazyVal::new(TraceBox(Box::new(LazyMethodBinding {48 context_creator,49 name: b.name.clone(),50 params,51 value: b.value.clone(),52 })))53 } else {54 #[derive(Trace)]55 struct LazyNamedBinding {56 context_creator: FutureWrapper<Context>,57 name: IStr,58 value: LocExpr,59 }60 impl LazyValValue for LazyNamedBinding {61 fn get(self: Box<Self>) -> Result<Val> {62 evaluate_named(self.context_creator.unwrap(), &self.value, self.name)63 }64 }65 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {66 context_creator,67 name: b.name.clone(),68 value: b.value,69 })))70 }71}7273pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, LazyBinding) {74 let b = b.clone();75 if let Some(params) = &b.params {76 let params = params.clone();7778 #[derive(Trace)]79 struct BindableMethodLazyVal {80 this: Option<ObjValue>,81 super_obj: Option<ObjValue>,8283 context_creator: ContextCreator,84 name: IStr,85 params: ParamsDesc,86 value: LocExpr,87 }88 impl LazyValValue for BindableMethodLazyVal {89 fn get(self: Box<Self>) -> Result<Val> {90 Ok(evaluate_method(91 self.context_creator.create(self.this, self.super_obj)?,92 self.name,93 self.params,94 self.value,95 ))96 }97 }9899 #[derive(Trace)]100 struct BindableMethod {101 context_creator: ContextCreator,102 name: IStr,103 params: ParamsDesc,104 value: LocExpr,105 }106 impl Bindable for BindableMethod {107 fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {108 Ok(LazyVal::new(TraceBox(Box::new(BindableMethodLazyVal {109 this,110 super_obj,111112 context_creator: self.context_creator.clone(),113 name: self.name.clone(),114 params: self.params.clone(),115 value: self.value.clone(),116 }))))117 }118 }119120 (121 b.name.clone(),122 LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableMethod {123 context_creator,124 name: b.name.clone(),125 params,126 value: b.value.clone(),127 })))),128 )129 } else {130 #[derive(Trace)]131 struct BindableNamedLazyVal {132 this: Option<ObjValue>,133 super_obj: Option<ObjValue>,134135 context_creator: ContextCreator,136 name: IStr,137 value: LocExpr,138 }139 impl LazyValValue for BindableNamedLazyVal {140 fn get(self: Box<Self>) -> Result<Val> {141 evaluate_named(142 self.context_creator.create(self.this, self.super_obj)?,143 &self.value,144 self.name,145 )146 }147 }148149 #[derive(Trace)]150 struct BindableNamed {151 context_creator: ContextCreator,152 name: IStr,153 value: LocExpr,154 }155 impl Bindable for BindableNamed {156 fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {157 Ok(LazyVal::new(TraceBox(Box::new(BindableNamedLazyVal {158 this,159 super_obj,160161 context_creator: self.context_creator.clone(),162 name: self.name.clone(),163 value: self.value.clone(),164 }))))165 }166 }167168 (169 b.name.clone(),170 LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableNamed {171 context_creator,172 name: b.name.clone(),173 value: b.value.clone(),174 })))),175 )176 }177}178179pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {180 Val::Func(Cc::new(FuncVal::Normal(FuncDesc {181 name,182 ctx,183 params,184 body,185 })))186}187188pub fn evaluate_field_name(189 context: Context,190 field_name: &jrsonnet_parser::FieldName,191) -> Result<Option<IStr>> {192 Ok(match field_name {193 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),194 jrsonnet_parser::FieldName::Dyn(expr) => push_frame(195 Some(&expr.1),196 || "evaluating field name".to_string(),197 || {198 let value = evaluate(context, expr)?;199 if matches!(value, Val::Null) {200 Ok(None)201 } else {202 Ok(Some(IStr::try_from(value)?))203 }204 },205 )?,206 })207}208209pub fn evaluate_comp(210 context: Context,211 specs: &[CompSpec],212 callback: &mut impl FnMut(Context) -> Result<()>,213) -> Result<()> {214 match specs.get(0) {215 None => callback(context)?,216 Some(CompSpec::IfSpec(IfSpecData(cond))) => {217 if bool::try_from(evaluate(context.clone(), cond)?)? {218 evaluate_comp(context, &specs[1..], callback)?219 }220 }221 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {222 Val::Arr(list) => {223 for item in list.iter() {224 evaluate_comp(225 context.clone().with_var(var.clone(), item?.clone()),226 &specs[1..],227 callback,228 )?229 }230 }231 _ => throw!(InComprehensionCanOnlyIterateOverArray),232 },233 }234 Ok(())235}236237pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {238 let new_bindings = FutureWrapper::new();239 let future_this = FutureWrapper::new();240 let context_creator = ContextCreator(context.clone(), new_bindings.clone());241 {242 let mut bindings: GcHashMap<IStr, LazyBinding> = GcHashMap::with_capacity(members.len());243 for (n, b) in members244 .iter()245 .filter_map(|m| match m {246 Member::BindStmt(b) => Some(b.clone()),247 _ => None,248 })249 .map(|b| evaluate_binding(&b, context_creator.clone()))250 {251 bindings.insert(n, b);252 }253 new_bindings.fill(bindings);254 }255256 let mut builder = ObjValueBuilder::new();257 for member in members.iter() {258 match member {259 Member::Field(FieldMember {260 name,261 plus,262 params: None,263 visibility,264 value,265 }) => {266 let name = evaluate_field_name(context.clone(), name)?;267 if name.is_none() {268 continue;269 }270 let name = name.unwrap();271272 #[derive(Trace)]273 struct ObjMemberBinding {274 context_creator: ContextCreator,275 value: LocExpr,276 name: IStr,277 }278 impl Bindable for ObjMemberBinding {279 fn bind(280 &self,281 this: Option<ObjValue>,282 super_obj: Option<ObjValue>,283 ) -> Result<LazyVal> {284 Ok(LazyVal::new_resolved(evaluate_named(285 self.context_creator.create(this, super_obj)?,286 &self.value,287 self.name.clone(),288 )?))289 }290 }291 builder292 .member(name.clone())293 .with_add(*plus)294 .with_visibility(*visibility)295 .with_location(value.1.clone())296 .bindable(TraceBox(Box::new(ObjMemberBinding {297 context_creator: context_creator.clone(),298 value: value.clone(),299 name,300 })));301 }302 Member::Field(FieldMember {303 name,304 params: Some(params),305 value,306 ..307 }) => {308 let name = evaluate_field_name(context.clone(), name)?;309 if name.is_none() {310 continue;311 }312 let name = name.unwrap();313 #[derive(Trace)]314 struct ObjMemberBinding {315 context_creator: ContextCreator,316 value: LocExpr,317 params: ParamsDesc,318 name: IStr,319 }320 impl Bindable for ObjMemberBinding {321 fn bind(322 &self,323 this: Option<ObjValue>,324 super_obj: Option<ObjValue>,325 ) -> Result<LazyVal> {326 Ok(LazyVal::new_resolved(evaluate_method(327 self.context_creator.create(this, super_obj)?,328 self.name.clone(),329 self.params.clone(),330 self.value.clone(),331 )))332 }333 }334 builder335 .member(name.clone())336 .hide()337 .with_location(value.1.clone())338 .bindable(TraceBox(Box::new(ObjMemberBinding {339 context_creator: context_creator.clone(),340 value: value.clone(),341 params: params.clone(),342 name,343 })));344 }345 Member::BindStmt(_) => {}346 Member::AssertStmt(stmt) => {347 #[derive(Trace)]348 struct ObjectAssert {349 context_creator: ContextCreator,350 assert: AssertStmt,351 }352 impl ObjectAssertion for ObjectAssert {353 fn run(354 &self,355 this: Option<ObjValue>,356 super_obj: Option<ObjValue>,357 ) -> Result<()> {358 let ctx = self.context_creator.create(this, super_obj)?;359 evaluate_assert(ctx, &self.assert)360 }361 }362 builder.assert(TraceBox(Box::new(ObjectAssert {363 context_creator: context_creator.clone(),364 assert: stmt.clone(),365 })));366 }367 }368 }369 let this = builder.build();370 future_this.fill(this.clone());371 Ok(this)372}373374pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {375 Ok(match object {376 ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,377 ObjBody::ObjComp(obj) => {378 let future_this = FutureWrapper::new();379 let mut builder = ObjValueBuilder::new();380 evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {381 let new_bindings = FutureWrapper::new();382 let context_creator = ContextCreator(context.clone(), new_bindings.clone());383 let mut bindings: GcHashMap<IStr, LazyBinding> =384 GcHashMap::with_capacity(obj.pre_locals.len() + obj.post_locals.len());385 for (n, b) in obj386 .pre_locals387 .iter()388 .chain(obj.post_locals.iter())389 .map(|b| evaluate_binding(b, context_creator.clone()))390 {391 bindings.insert(n, b);392 }393 new_bindings.fill(bindings.clone());394 let ctx = ctx.extend_unbound(bindings, None, None, None)?;395 let key = evaluate(ctx.clone(), &obj.key)?;396397 match key {398 Val::Null => {}399 Val::Str(n) => {400 #[derive(Trace)]401 struct ObjCompBinding {402 context: Context,403 value: LocExpr,404 }405 impl Bindable for ObjCompBinding {406 fn bind(407 &self,408 this: Option<ObjValue>,409 _super_obj: Option<ObjValue>,410 ) -> Result<LazyVal> {411 Ok(LazyVal::new_resolved(evaluate(412 self.context413 .clone()414 .extend(GcHashMap::new(), None, this, None),415 &self.value,416 )?))417 }418 }419 builder420 .member(n)421 .with_location(obj.value.1.clone())422 .with_add(obj.plus)423 .bindable(TraceBox(Box::new(ObjCompBinding {424 context: ctx,425 value: obj.value.clone(),426 })));427 }428 v => throw!(FieldMustBeStringGot(v.value_type())),429 }430431 Ok(())432 })?;433434 let this = builder.build();435 future_this.fill(this.clone());436 this437 }438 })439}440441pub fn evaluate_apply(442 context: Context,443 value: &LocExpr,444 args: &ArgsDesc,445 loc: Option<&ExprLocation>,446 tailstrict: bool,447) -> Result<Val> {448 let value = evaluate(context.clone(), value)?;449 Ok(match value {450 Val::Func(f) => {451 let body = || f.evaluate(context, loc, args, tailstrict);452 if tailstrict {453 body()?454 } else {455 push_frame(loc, || format!("function <{}> call", f.name()), body)?456 }457 }458 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),459 })460}461462pub fn evaluate_assert(context: Context, assertion: &AssertStmt) -> Result<()> {463 let value = &assertion.0;464 let msg = &assertion.1;465 let assertion_result = push_frame(466 Some(&value.1),467 || "assertion condition".to_owned(),468 || bool::try_from(evaluate(context.clone(), value)?),469 )?;470 if !assertion_result {471 push_frame(472 Some(&value.1),473 || "assertion failure".to_owned(),474 || {475 if let Some(msg) = msg {476 throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));477 } else {478 throw!(AssertionFailed(Val::Null.to_string()?));479 }480 },481 )?482 }483 Ok(())484}485486pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {487 use Expr::*;488 let LocExpr(expr, _loc) = lexpr;489 Ok(match &**expr {490 Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),491 _ => evaluate(context, lexpr)?,492 })493}494495pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {496 use Expr::*;497 let LocExpr(expr, loc) = expr;498 // let bp = with_state(|s| s.0.stop_at.borrow().clone());499 Ok(match &**expr {500 Literal(LiteralType::This) => {501 Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)502 }503 Literal(LiteralType::Super) => Val::Obj(504 context505 .super_obj()506 .clone()507 .ok_or(NoSuperFound)?508 .with_this(context.this().clone().unwrap()),509 ),510 Literal(LiteralType::Dollar) => {511 Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)512 }513 Literal(LiteralType::True) => Val::Bool(true),514 Literal(LiteralType::False) => Val::Bool(false),515 Literal(LiteralType::Null) => Val::Null,516 Parened(e) => evaluate(context, e)?,517 Str(v) => Val::Str(v.clone()),518 Num(v) => Val::new_checked_num(*v)?,519 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,520 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,521 Var(name) => push_frame(522 Some(loc),523 || format!("variable <{}> access", name),524 || context.binding(name.clone())?.evaluate(),525 )?,526 Index(value, index) => {527 match (evaluate(context.clone(), value)?, evaluate(context, index)?) {528 (Val::Obj(v), Val::Str(s)) => {529 let sn = s.clone();530 push_frame(531 Some(loc),532 || format!("field <{}> access", sn),533 || {534 if let Some(v) = v.get(s.clone())? {535 Ok(v)536 } else {537 throw!(NoSuchField(s))538 }539 },540 )?541 }542 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(543 ValType::Obj,544 ValType::Str,545 n.value_type(),546 )),547548 (Val::Arr(v), Val::Num(n)) => {549 if n.fract() > f64::EPSILON {550 throw!(FractionalIndex)551 }552 v.get(n as usize)?553 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?554 }555 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),556 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(557 ValType::Arr,558 ValType::Num,559 n.value_type(),560 )),561562 (Val::Str(s), Val::Num(n)) => Val::Str(563 s.chars()564 .skip(n as usize)565 .take(1)566 .collect::<String>()567 .into(),568 ),569 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(570 ValType::Str,571 ValType::Num,572 n.value_type(),573 )),574575 (v, _) => throw!(CantIndexInto(v.value_type())),576 }577 }578 LocalExpr(bindings, returned) => {579 let mut new_bindings: GcHashMap<IStr, LazyVal> =580 GcHashMap::with_capacity(bindings.len());581 let future_context = Context::new_future();582 for b in bindings {583 new_bindings.insert(584 b.name.clone(),585 evaluate_binding_in_future(b, future_context.clone()),586 );587 }588 let context = context589 .extend_bound(new_bindings)590 .into_future(future_context);591 evaluate(context, &returned.clone())?592 }593 Arr(items) => {594 let mut out = Vec::with_capacity(items.len());595 for item in items {596 // TODO: Implement ArrValue::Lazy with same context for every element?597 #[derive(Trace)]598 struct ArrayElement {599 context: Context,600 item: LocExpr,601 }602 impl LazyValValue for ArrayElement {603 fn get(self: Box<Self>) -> Result<Val> {604 evaluate(self.context, &self.item)605 }606 }607 out.push(LazyVal::new(TraceBox(Box::new(ArrayElement {608 context: context.clone(),609 item: item.clone(),610 }))));611 }612 Val::Arr(out.into())613 }614 ArrComp(expr, comp_specs) => {615 let mut out = Vec::new();616 evaluate_comp(context, comp_specs, &mut |ctx| {617 out.push(evaluate(ctx, expr)?);618 Ok(())619 })?;620 Val::Arr(ArrValue::Eager(Cc::new(out)))621 }622 Obj(body) => Val::Obj(evaluate_object(context, body)?),623 ObjExtend(s, t) => evaluate_add_op(624 &evaluate(context.clone(), s)?,625 &Val::Obj(evaluate_object(context, t)?),626 )?,627 Apply(value, args, tailstrict) => {628 evaluate_apply(context, value, args, Some(loc), *tailstrict)?629 }630 Function(params, body) => {631 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())632 }633 Intrinsic(name) => Val::Func(Cc::new(FuncVal::StaticBuiltin(634 BUILTINS635 .with(|b| b.get(name).copied())636 .ok_or_else(|| IntrinsicNotFound(name.clone()))?,637 ))),638 AssertExpr(assert, returned) => {639 evaluate_assert(context.clone(), assert)?;640 evaluate(context, returned)?641 }642 ErrorStmt(e) => push_frame(643 Some(loc),644 || "error statement".to_owned(),645 || throw!(RuntimeError(IStr::try_from(evaluate(context, e)?)?,)),646 )?,647 IfElse {648 cond,649 cond_then,650 cond_else,651 } => {652 if push_frame(653 Some(loc),654 || "if condition".to_owned(),655 || bool::try_from(evaluate(context.clone(), &cond.0)?),656 )? {657 evaluate(context, cond_then)?658 } else {659 match cond_else {660 Some(v) => evaluate(context, v)?,661 None => Val::Null,662 }663 }664 }665 Slice(value, desc) => {666 let indexable = evaluate(context.clone(), value)?;667668 fn parse_num(669 context: &Context,670 expr: Option<&LocExpr>,671 desc: &'static str,672 ) -> Result<Option<usize>> {673 Ok(match expr {674 Some(s) => evaluate(context.clone(), s)?675 .try_cast_nullable_num(desc)?676 .map(|v| v as usize),677 None => None,678 })679 }680681 let start = parse_num(&context, desc.start.as_ref(), "start")?;682 let end = parse_num(&context, desc.end.as_ref(), "end")?;683 let step = parse_num(&context, desc.step.as_ref(), "step")?;684685 std_slice(indexable.into_indexable()?, start, end, step)?686 }687 Import(path) => {688 let tmp = loc.clone().0;689 let mut import_location = tmp.to_path_buf();690 import_location.pop();691 push_frame(692 Some(loc),693 || format!("import {:?}", path),694 || with_state(|s| s.import_file(&import_location, path)),695 )?696 }697 ImportStr(path) => {698 let tmp = loc.clone().0;699 let mut import_location = tmp.to_path_buf();700 import_location.pop();701 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)702 }703 ImportBin(path) => {704 let tmp = loc.clone().0;705 let mut import_location = tmp.to_path_buf();706 import_location.pop();707 let bytes = with_state(|s| s.import_file_bin(&import_location, path))?;708 Val::Arr(ArrValue::Bytes(bytes))709 }710 })711}crates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/import.rs
+++ b/crates/jrsonnet-evaluator/src/import.rs
@@ -5,14 +5,12 @@
use fs::File;
use jrsonnet_interner::IStr;
use std::fs;
-use std::io::Read;
use std::{
any::Any,
- cell::RefCell,
- collections::HashMap,
path::{Path, PathBuf},
rc::Rc,
};
+use std::{convert::TryFrom, io::Read};
/// Implements file resolution logic for `import` and `importStr`
pub trait ImportResolver {
@@ -21,9 +19,19 @@
/// where `${vendor}` is a library path.
fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>>;
+ fn load_file_contents(&self, resolved: &Path) -> Result<Vec<u8>>;
+
/// Reads file from filesystem, should be used only with path received from `resolve_file`
- fn load_file_contents(&self, resolved: &Path) -> Result<IStr>;
+ fn load_file_str(&self, resolved: &Path) -> Result<IStr> {
+ Ok(IStr::try_from(&self.load_file_contents(resolved)? as &[u8])
+ .map_err(|_| ImportBadFileUtf8(resolved.to_path_buf()))?)
+ }
+ /// Reads file from filesystem, should be used only with path received from `resolve_file`
+ fn load_file_bin(&self, resolved: &Path) -> Result<Rc<[u8]>> {
+ Ok(self.load_file_contents(resolved)?.into())
+ }
+
/// # Safety
///
/// For use only in bindings, should not be used elsewhere.
@@ -39,8 +47,7 @@
throw!(ImportNotSupported(from.into(), path.into()))
}
- fn load_file_contents(&self, _resolved: &Path) -> Result<IStr> {
- // Can be only caused by library direct consumer, not by supplied jsonnet
+ fn load_file_contents(&self, _resolved: &Path) -> Result<Vec<u8>> {
panic!("dummy resolver can't load any file")
}
@@ -79,41 +86,12 @@
throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))
}
}
- fn load_file_contents(&self, id: &Path) -> Result<IStr> {
+ fn load_file_contents(&self, id: &Path) -> Result<Vec<u8>> {
let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;
- let mut out = String::new();
- file.read_to_string(&mut out)
- .map_err(|_e| ImportBadFileUtf8(id.to_owned()))?;
- Ok(out.into())
- }
- unsafe fn as_any(&self) -> &dyn Any {
- panic!("this resolver can't be used as any")
- }
-}
-
-type ResolutionData = (PathBuf, PathBuf);
-
-/// Caches results of the underlying resolver
-pub struct CachingImportResolver {
- resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<Path>>>>,
- loading_cache: RefCell<HashMap<PathBuf, Result<IStr>>>,
- inner: Box<dyn ImportResolver>,
-}
-impl ImportResolver for CachingImportResolver {
- fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {
- self.resolution_cache
- .borrow_mut()
- .entry((from.to_owned(), path.to_owned()))
- .or_insert_with(|| self.inner.resolve_file(from, path))
- .clone()
- }
-
- fn load_file_contents(&self, resolved: &Path) -> Result<IStr> {
- self.loading_cache
- .borrow_mut()
- .entry(resolved.to_owned())
- .or_insert_with(|| self.inner.load_file_contents(resolved))
- .clone()
+ let mut out = Vec::new();
+ file.read_to_end(&mut out)
+ .map_err(|e| ImportIo(e.to_string()))?;
+ Ok(out)
}
unsafe fn as_any(&self) -> &dyn Any {
panic!("this resolver can't be used as any")
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -119,8 +119,9 @@
breakpoints: Breakpoints,
/// Contains file source codes and evaluation results for imports and pretty-printed stacktraces
- files: HashMap<Rc<Path>, FileData>,
- str_files: HashMap<Rc<Path>, IStr>,
+ files: GcHashMap<Rc<Path>, FileData>,
+ str_files: GcHashMap<Rc<Path>, IStr>,
+ bin_files: GcHashMap<Rc<Path>, Rc<[u8]>>,
}
pub struct FileData {
@@ -276,18 +277,26 @@
return self.evaluate_loaded_file_raw(&file_path);
}
}
- let contents = self.load_file_contents(&file_path)?;
+ let contents = self.load_file_str(&file_path)?;
self.add_file(file_path.clone(), contents)?;
self.evaluate_loaded_file_raw(&file_path)
}
pub(crate) fn import_file_str(&self, from: &Path, path: &Path) -> Result<IStr> {
let path = self.resolve_file(from, path)?;
if !self.data().str_files.contains_key(&path) {
- let file_str = self.load_file_contents(&path)?;
+ let file_str = self.load_file_str(&path)?;
self.data_mut().str_files.insert(path.clone(), file_str);
}
Ok(self.data().str_files.get(&path).cloned().unwrap())
}
+ pub(crate) fn import_file_bin(&self, from: &Path, path: &Path) -> Result<Rc<[u8]>> {
+ let path = self.resolve_file(from, path)?;
+ if !self.data().bin_files.contains_key(&path) {
+ let file_bin = self.load_file_bin(&path)?;
+ self.data_mut().bin_files.insert(path.clone(), file_bin);
+ }
+ Ok(self.data().bin_files.get(&path).cloned().unwrap())
+ }
fn evaluate_loaded_file_raw(&self, name: &Path) -> Result<Val> {
let expr: LocExpr = {
@@ -603,8 +612,11 @@
pub fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {
self.settings().import_resolver.resolve_file(from, path)
}
- pub fn load_file_contents(&self, path: &Path) -> Result<IStr> {
- self.settings().import_resolver.load_file_contents(path)
+ pub fn load_file_str(&self, path: &Path) -> Result<IStr> {
+ self.settings().import_resolver.load_file_str(path)
+ }
+ pub fn load_file_bin(&self, path: &Path) -> Result<Rc<[u8]>> {
+ self.settings().import_resolver.load_file_bin(path)
}
pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {
@@ -661,7 +673,6 @@
EvaluationState,
};
use gcmodule::{Cc, Trace};
- use jrsonnet_interner::IStr;
use jrsonnet_parser::*;
use std::{
path::{Path, PathBuf},
@@ -1165,19 +1176,23 @@
Ok(())
}
- struct TestImportResolver(IStr);
+ struct TestImportResolver(Vec<u8>);
impl crate::import::ImportResolver for TestImportResolver {
fn resolve_file(&self, _: &Path, _: &Path) -> crate::error::Result<Rc<Path>> {
Ok(PathBuf::from("/test").into())
}
- fn load_file_contents(&self, _: &Path) -> crate::error::Result<IStr> {
+ fn load_file_contents(&self, _: &Path) -> crate::error::Result<Vec<u8>> {
Ok(self.0.clone())
}
unsafe fn as_any(&self) -> &dyn std::any::Any {
panic!()
}
+
+ fn load_file_bin(&self, _resolved: &Path) -> crate::error::Result<Rc<[u8]>> {
+ panic!()
+ }
}
#[test]
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -186,6 +186,7 @@
#[derive(Debug, Clone, Trace)]
#[force_tracking]
pub enum ArrValue {
+ Bytes(#[skip_trace] Rc<[u8]>),
Lazy(Cc<Vec<LazyVal>>),
Eager(Cc<Vec<Val>>),
Extended(Box<(Self, Self)>),
@@ -197,6 +198,7 @@
pub fn len(&self) -> usize {
match self {
+ Self::Bytes(i) => i.len(),
Self::Lazy(l) => l.len(),
Self::Eager(e) => e.len(),
Self::Extended(v) => v.0.len() + v.1.len(),
@@ -209,6 +211,9 @@
pub fn get(&self, index: usize) -> Result<Option<Val>> {
match self {
+ Self::Bytes(i) => i
+ .get(index)
+ .map_or(Ok(None), |v| Ok(Some(Val::Num(*v as f64)))),
Self::Lazy(vec) => {
if let Some(v) = vec.get(index) {
Ok(Some(v.evaluate()?))
@@ -230,6 +235,9 @@
pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {
match self {
+ Self::Bytes(i) => i
+ .get(index)
+ .map(|b| LazyVal::new_resolved(Val::Num(*b as f64))),
Self::Lazy(vec) => vec.get(index).cloned(),
Self::Eager(vec) => vec.get(index).cloned().map(LazyVal::new_resolved),
Self::Extended(v) => {
@@ -245,6 +253,13 @@
pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {
Ok(match self {
+ Self::Bytes(i) => {
+ let mut out = Vec::with_capacity(i.len());
+ for v in i.iter() {
+ out.push(Val::Num(*v as f64));
+ }
+ Cc::new(out)
+ }
Self::Lazy(vec) => {
let mut out = Vec::with_capacity(vec.len());
for item in vec.iter() {
@@ -265,6 +280,7 @@
pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {
(0..self.len()).map(move |idx| match self {
+ Self::Bytes(b) => Ok(Val::Num(b[idx] as f64)),
Self::Lazy(l) => l[idx].evaluate(),
Self::Eager(e) => Ok(e[idx].clone()),
Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),
@@ -273,6 +289,7 @@
pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {
(0..self.len()).map(move |idx| match self {
+ Self::Bytes(b) => LazyVal::new_resolved(Val::Num(b[idx] as f64)),
Self::Lazy(l) => l[idx].clone(),
Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),
Self::Extended(_) => self.get_lazy(idx).unwrap(),
@@ -281,6 +298,11 @@
pub fn reversed(self) -> Self {
match self {
+ Self::Bytes(b) => {
+ let mut out = b.to_vec();
+ out.reverse();
+ Self::Bytes(out.into())
+ }
Self::Lazy(vec) => {
let mut out = (&vec as &Vec<_>).clone();
out.reverse();
crates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-interner/src/lib.rs
+++ b/crates/jrsonnet-interner/src/lib.rs
@@ -4,10 +4,12 @@
use std::{
borrow::Cow,
cell::RefCell,
+ convert::TryFrom,
fmt::{self, Display},
hash::{BuildHasherDefault, Hash, Hasher},
ops::Deref,
rc::Rc,
+ str::Utf8Error,
};
#[derive(Clone, PartialOrd, Ord, Eq)]
@@ -85,6 +87,15 @@
}
}
+impl TryFrom<&[u8]> for IStr {
+ type Error = Utf8Error;
+
+ fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
+ let str = std::str::from_utf8(value)?;
+ Ok(str.into())
+ }
+}
+
impl From<String> for IStr {
fn from(str: String) -> Self {
(&str as &str).into()
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -306,6 +306,8 @@
Import(PathBuf),
/// importStr "file.txt"
ImportStr(PathBuf),
+ /// importBin "file.txt"
+ ImportBin(PathBuf),
/// error "I'm broken"
ErrorStmt(LocExpr),
/// a(b, c)
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -53,7 +53,7 @@
rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.parse().map_err(|_| "<number>") }} / expected!("<number>")
/// Reserved word followed by any non-alphanumberic
- rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()
+ rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "importbin" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()
rule id() = quiet!{ !reserved() alpha() (alpha() / digit())*} / expected!("<identifier>")
rule keyword(id: &'static str) -> ()
@@ -218,6 +218,7 @@
/ array_comp_expr(s)
/ keyword("importstr") _ path:string() {Expr::ImportStr(PathBuf::from(path))}
+ / keyword("importbin") _ path:string() {Expr::ImportBin(PathBuf::from(path))}
/ keyword("import") _ path:string() {Expr::Import(PathBuf::from(path))}
/ var_expr(s)