// Copyright 2022 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use crate::values::{DictValueRef, ListValueRef, ValueSlotRef}; use serde::de::{DeserializeSeed, Deserializer, Error, MapAccess, SeqAccess, Visitor}; use std::convert::TryFrom; use std::fmt; /// Struct to check we're not recursing too deeply. #[derive(Clone)] struct RecursionDepthCheck(usize); impl RecursionDepthCheck { /// Recurse a level and return an error if we've recursed too far. fn recurse(&self) -> Result { match self.0.checked_sub(1) { Some(recursion_limit) => Ok(RecursionDepthCheck(recursion_limit)), None => Err(Error::custom("recursion limit exceeded")), } } } /// What type of `base::Value` container we're deserializing into. enum DeserializationTarget<'elem, 'container> { /// Deserialize into a brand new root `base::Value`. NewValue { slot: ValueSlotRef<'container> }, /// Deserialize by appending to a list. List { list: &'elem mut ListValueRef<'container> }, /// Deserialize by setting a dictionary key. Dict { dict: &'elem mut DictValueRef<'container>, key: String }, } /// serde deserializer for for `base::Value`. /// /// serde is the "standard" framework for serializing and deserializing /// data formats in Rust. https://serde.rs/ /// /// This implements both the Visitor and Deserialize roles described /// here: https://serde.rs/impl-deserialize.html /// /// One note, though. Normally serde instantiates a new object. The design /// of `base::Value` is that each sub-value (e.g. a list like this) /// needs to be deserialized into the parent value, which is pre-existing. /// To achieve this we use a feature of serde called 'stateful deserialization' /// (see https://docs.serde.rs/serde/de/trait.DeserializeSeed.html) /// /// This struct stores that state. /// /// Handily, this also enables us to store the desired recursion limit. /// /// We use runtime dispatch (matching on an enum) to deserialize into a /// dictionary, list, etc. This may be slower than having three different /// `Visitor` implementations, where everything would be monomorphized /// and would probably disappear with inlining, but for now this is much /// less code. pub struct ValueVisitor<'elem, 'container> { container: DeserializationTarget<'elem, 'container>, recursion_depth_check: RecursionDepthCheck, } impl<'elem, 'container> ValueVisitor<'elem, 'container> { /// Constructor - pass in an existing slot that can store a new /// `base::Value`, then this visitor can be passed to serde deserialization /// libraries to populate it with a tree of contents. /// Any existing `base::Value` in the slot will be replaced. pub fn new(slot: ValueSlotRef<'container>, mut max_depth: usize) -> Self { max_depth += 1; // we will increment this counter when deserializing // the initial `base::Value`. To match C++ behavior, we should // only start counting for subsequent layers, hence decrement // now. Self { container: DeserializationTarget::NewValue { slot }, recursion_depth_check: RecursionDepthCheck(max_depth), } } } impl<'de, 'elem, 'container> Visitor<'de> for ValueVisitor<'elem, 'container> { // Because we are deserializing into a pre-existing object. type Value = (); fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("any valid JSON") } fn visit_i32(self, value: i32) -> Result { match self.container { DeserializationTarget::NewValue { mut slot } => slot.construct_integer(value), DeserializationTarget::List { list } => list.append_integer(value), DeserializationTarget::Dict { dict, key } => dict.set_integer_key(&key, value), }; Ok(()) } fn visit_i8(self, value: i8) -> Result { self.visit_i32(value as i32) } fn visit_bool(self, value: bool) -> Result { match self.container { DeserializationTarget::NewValue { mut slot } => slot.construct_bool(value), DeserializationTarget::List { list } => list.append_bool(value), DeserializationTarget::Dict { dict, key } => dict.set_bool_key(&key, value), }; Ok(()) } fn visit_i64(self, value: i64) -> Result { // Here we match the behavior of the Chromium C++ JSON parser, // which will attempt to create an integer base::Value but will // overflow into a double if needs be. // (See JSONParser::ConsumeNumber in json_parser.cc for equivalent, // and json_reader_unittest.cc LargerIntIsLossy for a related test). match i32::try_from(value) { Ok(value) => self.visit_i32(value), Err(_) => self.visit_f64(value as f64), } } fn visit_u64(self, value: u64) -> Result { // See visit_i64 comment. match i32::try_from(value) { Ok(value) => self.visit_i32(value), Err(_) => self.visit_f64(value as f64), } } fn visit_f64(self, value: f64) -> Result { match self.container { DeserializationTarget::NewValue { mut slot } => slot.construct_double(value), DeserializationTarget::List { list } => list.append_double(value), DeserializationTarget::Dict { dict, key } => dict.set_double_key(&key, value), }; Ok(()) } fn visit_str(self, value: &str) -> Result { match self.container { DeserializationTarget::NewValue { mut slot } => slot.construct_string(value), DeserializationTarget::List { list } => list.append_string(value), DeserializationTarget::Dict { dict, key } => dict.set_string_key(&key, value), }; Ok(()) } fn visit_borrowed_str(self, value: &'de str) -> Result { match self.container { DeserializationTarget::NewValue { mut slot } => slot.construct_string(value), DeserializationTarget::List { list } => list.append_string(value), DeserializationTarget::Dict { dict, key } => dict.set_string_key(&key, value), }; Ok(()) } fn visit_string(self, value: String) -> Result { self.visit_str(&value) } fn visit_none(self) -> Result { match self.container { DeserializationTarget::NewValue { mut slot } => slot.construct_none(), DeserializationTarget::List { list } => list.append_none(), DeserializationTarget::Dict { dict, key } => dict.set_none_key(&key), }; Ok(()) } fn visit_unit(self) -> Result { self.visit_none() } fn visit_map(mut self, mut access: M) -> Result where M: MapAccess<'de>, { let mut value = match self.container { DeserializationTarget::NewValue { ref mut slot } => slot.construct_dict(), DeserializationTarget::List { list } => list.append_dict(), DeserializationTarget::Dict { dict, key } => dict.set_dict_key(&key), }; // If it were exposed by values.h, we could use access.size_hint() // to give a clue to the C++ about the size of the desired map. while let Some(key) = access.next_key::()? { access.next_value_seed(ValueVisitor { container: DeserializationTarget::Dict { dict: &mut value, key }, recursion_depth_check: self.recursion_depth_check.recurse()?, })?; } Ok(()) } fn visit_seq(mut self, mut access: S) -> Result where S: SeqAccess<'de>, { let mut value = match self.container { DeserializationTarget::NewValue { ref mut slot } => slot.construct_list(), DeserializationTarget::List { list } => list.append_list(), DeserializationTarget::Dict { dict, key } => dict.set_list_key(&key), }; if let Some(size_hint) = access.size_hint() { value.reserve_size(size_hint); } while let Some(_) = access.next_element_seed(ValueVisitor { container: DeserializationTarget::List { list: &mut value }, recursion_depth_check: self.recursion_depth_check.recurse()?, })? {} Ok(()) } } impl<'de, 'elem, 'container> DeserializeSeed<'de> for ValueVisitor<'elem, 'container> { // Because we are deserializing into a pre-existing object. type Value = (); fn deserialize(self, deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_any(self) } }