difftreelog
fix disallow field duplicates
in: master
5 files changed
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -63,6 +63,8 @@
#[error("field name should be string, got {0}")]
FieldMustBeStringGot(ValType),
+ #[error("duplicate field name: {0}")]
+ DuplicateFieldName(IStr),
#[error("attempted to index array with string {0}")]
AttemptedIndexAnArrayWithString(IStr),
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -298,7 +298,7 @@
context_creator: context_creator.clone(),
value: value.clone(),
name,
- })));
+ })))?;
}
Member::Field(FieldMember {
name,
@@ -341,7 +341,7 @@
value: value.clone(),
params: params.clone(),
name,
- })));
+ })))?;
}
Member::BindStmt(_) => {}
Member::AssertStmt(stmt) => {
@@ -424,7 +424,7 @@
.bindable(TraceBox(Box::new(ObjCompBinding {
context: ctx,
value: obj.value.clone(),
- })));
+ })))?;
}
v => throw!(FieldMustBeStringGot(v.value_type())),
}
crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth1use crate::{2 error::{Error::*, LocError, Result},3 throw, ObjValueBuilder, Val,4};5use serde_json::{Map, Number, Value};6use std::convert::{TryFrom, TryInto};78impl TryFrom<&Val> for Value {9 type Error = LocError;10 fn try_from(v: &Val) -> Result<Self> {11 Ok(match v {12 Val::Bool(b) => Self::Bool(*b),13 Val::Null => Self::Null,14 Val::Str(s) => Self::String((s as &str).into()),15 Val::Num(n) => Self::Number(if n.fract() <= f64::EPSILON {16 (*n as i64).into()17 } else {18 Number::from_f64(*n).expect("jsonnet numbers can't be infinite or NaN")19 }),20 Val::Arr(a) => {21 let mut out = Vec::with_capacity(a.len());22 for item in a.iter() {23 out.push(item?.try_into()?);24 }25 Self::Array(out)26 }27 Val::Obj(o) => {28 let mut out = Map::new();29 for key in o.fields() {30 out.insert(31 (&key as &str).into(),32 o.get(key)?33 .expect("key is present in fields, so value should exist")34 .try_into()?,35 );36 }37 Self::Object(out)38 }39 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),40 })41 }42}43impl TryFrom<Val> for Value {44 type Error = LocError;4546 fn try_from(value: Val) -> Result<Self, Self::Error> {47 <Self as TryFrom<&Val>>::try_from(&value)48 }49}5051impl TryFrom<&Value> for Val {52 type Error = LocError;53 fn try_from(v: &Value) -> Result<Self> {54 Ok(match v {55 Value::Null => Self::Null,56 Value::Bool(v) => Self::Bool(*v),57 Value::Number(n) => Self::Num(n.as_f64().ok_or_else(|| {58 RuntimeError(format!("json number can't be represented as jsonnet: {}", n).into())59 })?),60 Value::String(s) => Self::Str((s as &str).into()),61 Value::Array(a) => {62 let mut out: Vec<Self> = Vec::with_capacity(a.len());63 for v in a {64 out.push(v.try_into()?);65 }66 Self::Arr(out.into())67 }68 Value::Object(o) => {69 let mut builder = ObjValueBuilder::with_capacity(o.len());70 for (k, v) in o {71 builder.member((k as &str).into()).value(v.try_into()?);72 }73 Self::Obj(builder.build())74 }75 })76 }77}78impl TryFrom<Value> for Val {79 type Error = LocError;8081 fn try_from(value: Value) -> Result<Self, Self::Error> {82 <Self as TryFrom<&Value>>::try_from(&value)83 }84}crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -1,6 +1,11 @@
+use crate::function::CallLocation;
use crate::gc::{GcHashMap, GcHashSet, TraceBox};
use crate::operator::evaluate_add_op;
-use crate::{cc_ptr_eq, weak_ptr_eq, weak_raw, Bindable, LazyBinding, LazyVal, Result, Val};
+use crate::push_frame;
+use crate::{
+ cc_ptr_eq, error::Error::*, throw, weak_ptr_eq, weak_raw, Bindable, LazyBinding, LazyVal,
+ Result, Val,
+};
use gcmodule::{Cc, Trace, Weak};
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ExprLocation, Visibility};
@@ -373,22 +378,29 @@
self.location = Some(location);
self
}
- pub fn value(self, value: Val) -> &'v mut ObjValueBuilder {
+ pub fn value(self, value: Val) -> Result<()> {
self.binding(LazyBinding::Bound(LazyVal::new_resolved(value)))
}
- pub fn bindable(self, bindable: TraceBox<dyn Bindable>) -> &'v mut ObjValueBuilder {
+ pub fn bindable(self, bindable: TraceBox<dyn Bindable>) -> Result<()> {
self.binding(LazyBinding::Bindable(Cc::new(bindable)))
}
- pub fn binding(self, binding: LazyBinding) -> &'v mut ObjValueBuilder {
- self.value.map.insert(
- self.name,
+ pub fn binding(self, binding: LazyBinding) -> Result<()> {
+ let old = self.value.map.insert(
+ self.name.clone(),
ObjMember {
add: self.add,
visibility: self.visibility,
invoke: binding,
- location: self.location,
+ location: self.location.clone(),
},
);
- self.value
+ if old.is_some() {
+ push_frame(
+ CallLocation(self.location.as_ref()),
+ || format!("field <{}> initializtion", self.name.clone()),
+ || throw!(DuplicateFieldName(self.name.clone())),
+ )?
+ }
+ Ok(())
}
}
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -459,12 +459,12 @@
if self.is_option() {
quote! {
if let Some(value) = self.#ident {
- out.member(#name.into()).value(value.try_into()?);
+ out.member(#name.into()).value(value.try_into()?)?;
}
}
} else {
quote! {
- out.member(#name.into()).value(self.#ident.try_into()?);
+ out.member(#name.into()).value(self.#ident.try_into()?)?;
}
}
} else if self.is_option() {