xref: /aosp_15_r20/external/cronet/base/json/json_reader.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/json/json_reader.h"
6 
7 #include <string_view>
8 #include <utility>
9 
10 #include "base/features.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/rust_buildflags.h"
14 
15 #if BUILDFLAG(BUILD_RUST_JSON_READER)
16 #include "base/strings/string_piece_rust.h"
17 #include "third_party/rust/serde_json_lenient/v0_2/wrapper/functions.h"
18 #include "third_party/rust/serde_json_lenient/v0_2/wrapper/lib.rs.h"
19 #endif  // BUILDFLAG(BUILD_RUST_JSON_READER)
20 #include "base/json/json_parser.h"
21 
22 namespace base {
23 
24 #if BUILDFLAG(BUILD_RUST_JSON_READER)
25 
26 namespace {
27 using serde_json_lenient::ContextPointer;
28 
29 const char kSecurityJsonParsingTime[] = "Security.JSONParser.ParsingTime";
30 
ListAppendList(ContextPointer & ctx,size_t reserve)31 ContextPointer& ListAppendList(ContextPointer& ctx, size_t reserve) {
32   auto& value = reinterpret_cast<base::Value&>(ctx);
33   value.GetList().reserve(reserve);
34   value.GetList().Append(base::Value::List());
35   return reinterpret_cast<ContextPointer&>(value.GetList().back());
36 }
37 
ListAppendDict(ContextPointer & ctx)38 ContextPointer& ListAppendDict(ContextPointer& ctx) {
39   auto& value = reinterpret_cast<base::Value&>(ctx);
40   value.GetList().Append(base::Value::Dict());
41   return reinterpret_cast<ContextPointer&>(value.GetList().back());
42 }
43 
ListAppendNone(ContextPointer & ctx)44 void ListAppendNone(ContextPointer& ctx) {
45   auto& value = reinterpret_cast<base::Value&>(ctx);
46   value.GetList().Append(base::Value());
47 }
48 
49 template <class T, class As = T>
ListAppendValue(ContextPointer & ctx,T v)50 void ListAppendValue(ContextPointer& ctx, T v) {
51   auto& value = reinterpret_cast<base::Value&>(ctx);
52   value.GetList().Append(As{v});
53 }
54 
DictSetList(ContextPointer & ctx,rust::Str key,size_t reserve)55 ContextPointer& DictSetList(ContextPointer& ctx,
56                             rust::Str key,
57                             size_t reserve) {
58   auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
59   base::Value::List list;
60   list.reserve(reserve);
61   dict.Set(base::RustStrToStringPiece(key), std::move(list));
62   return reinterpret_cast<ContextPointer&>(
63       *dict.Find(base::RustStrToStringPiece(key)));
64 }
65 
DictSetDict(ContextPointer & ctx,rust::Str key)66 ContextPointer& DictSetDict(ContextPointer& ctx, rust::Str key) {
67   auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
68   dict.Set(base::RustStrToStringPiece(key), base::Value(base::Value::Dict()));
69   return reinterpret_cast<ContextPointer&>(
70       *dict.Find(base::RustStrToStringPiece(key)));
71 }
72 
DictSetNone(ContextPointer & ctx,rust::Str key)73 void DictSetNone(ContextPointer& ctx, rust::Str key) {
74   auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
75   dict.Set(base::RustStrToStringPiece(key), base::Value());
76 }
77 
78 template <class T, class As = T>
DictSetValue(ContextPointer & ctx,rust::Str key,T v)79 void DictSetValue(ContextPointer& ctx, rust::Str key, T v) {
80   auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
81   dict.Set(base::RustStrToStringPiece(key), base::Value(As{v}));
82 }
83 
DecodeJSONInRust(std::string_view json,int options,size_t max_depth)84 JSONReader::Result DecodeJSONInRust(std::string_view json,
85                                     int options,
86                                     size_t max_depth) {
87   const serde_json_lenient::JsonOptions rust_options = {
88       .allow_trailing_commas =
89           (options & base::JSON_ALLOW_TRAILING_COMMAS) != 0,
90       .replace_invalid_characters =
91           (options & base::JSON_REPLACE_INVALID_CHARACTERS) != 0,
92       .allow_comments = (options & base::JSON_ALLOW_COMMENTS) != 0,
93       .allow_newlines = (options & base::JSON_ALLOW_NEWLINES_IN_STRINGS) != 0,
94       .allow_control_chars = (options & base::JSON_ALLOW_CONTROL_CHARS) != 0,
95       .allow_vert_tab = (options & base::JSON_ALLOW_VERT_TAB) != 0,
96       .allow_x_escapes = (options & base::JSON_ALLOW_X_ESCAPES) != 0,
97       .max_depth = max_depth,
98   };
99   static constexpr serde_json_lenient::Functions functions = {
100       .list_append_none_fn = ListAppendNone,
101       .list_append_bool_fn = ListAppendValue<bool>,
102       .list_append_i32_fn = ListAppendValue<int32_t>,
103       .list_append_f64_fn = ListAppendValue<double>,
104       .list_append_str_fn = ListAppendValue<rust::Str, std::string>,
105       .list_append_list_fn = ListAppendList,
106       .list_append_dict_fn = ListAppendDict,
107       .dict_set_none_fn = DictSetNone,
108       .dict_set_bool_fn = DictSetValue<bool>,
109       .dict_set_i32_fn = DictSetValue<int32_t>,
110       .dict_set_f64_fn = DictSetValue<double>,
111       .dict_set_str_fn = DictSetValue<rust::Str, std::string>,
112       .dict_set_list_fn = DictSetList,
113       .dict_set_dict_fn = DictSetDict,
114   };
115 
116   base::Value value(base::Value::Type::LIST);
117   auto& ctx = reinterpret_cast<ContextPointer&>(value);
118   serde_json_lenient::DecodeError error;
119   bool ok = serde_json_lenient::decode_json(
120       base::StringPieceToRustSlice(json), rust_options, functions, ctx, error);
121 
122   if (!ok) {
123     return base::unexpected(base::JSONReader::Error{
124         .message = std::string(error.message),
125         .line = error.line,
126         .column = error.column,
127     });
128   }
129 
130   return std::move(std::move(value.GetList()).back());
131 }
132 
133 }  // anonymous namespace
134 
135 #endif  // BUILDFLAG(BUILD_RUST_JSON_READER)
136 
137 // static
Read(std::string_view json,int options,size_t max_depth)138 std::optional<Value> JSONReader::Read(std::string_view json,
139                                       int options,
140                                       size_t max_depth) {
141 #if BUILDFLAG(BUILD_RUST_JSON_READER)
142   SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime);
143   if (UsingRust()) {
144     JSONReader::Result result = DecodeJSONInRust(json, options, max_depth);
145     if (!result.has_value()) {
146       return std::nullopt;
147     }
148     return std::move(*result);
149   } else {
150     internal::JSONParser parser(options, max_depth);
151     return parser.Parse(json);
152   }
153 #else   // BUILDFLAG(BUILD_RUST_JSON_READER)
154   internal::JSONParser parser(options, max_depth);
155   return parser.Parse(json);
156 #endif  // BUILDFLAG(BUILD_RUST_JSON_READER)
157 }
158 
159 // static
ReadDict(std::string_view json,int options,size_t max_depth)160 std::optional<Value::Dict> JSONReader::ReadDict(std::string_view json,
161                                                 int options,
162                                                 size_t max_depth) {
163   std::optional<Value> value = Read(json, options, max_depth);
164   if (!value || !value->is_dict()) {
165     return std::nullopt;
166   }
167   return std::move(*value).TakeDict();
168 }
169 
170 // static
ReadAndReturnValueWithError(std::string_view json,int options)171 JSONReader::Result JSONReader::ReadAndReturnValueWithError(
172     std::string_view json,
173     int options) {
174 #if BUILDFLAG(BUILD_RUST_JSON_READER)
175   SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime);
176   if (UsingRust()) {
177     return DecodeJSONInRust(json, options, internal::kAbsoluteMaxDepth);
178   } else {
179     internal::JSONParser parser(options);
180     auto value = parser.Parse(json);
181     if (!value) {
182       Error error;
183       error.message = parser.GetErrorMessage();
184       error.line = parser.error_line();
185       error.column = parser.error_column();
186       return base::unexpected(std::move(error));
187     }
188 
189     return std::move(*value);
190   }
191 #else   // BUILDFLAG(BUILD_RUST_JSON_READER)
192   internal::JSONParser parser(options);
193   auto value = parser.Parse(json);
194   if (!value) {
195     Error error;
196     error.message = parser.GetErrorMessage();
197     error.line = parser.error_line();
198     error.column = parser.error_column();
199     return base::unexpected(std::move(error));
200   }
201 
202   return std::move(*value);
203 #endif  // BUILDFLAG(BUILD_RUST_JSON_READER)
204 }
205 
206 // static
UsingRust()207 bool JSONReader::UsingRust() {
208   // If features have not yet been enabled, we cannot check the feature, so fall
209   // back to the C++ parser. In practice, this seems to apply to
210   // `ReadPrefsFromDisk()`, which is parsing trusted JSON.
211   if (!base::FeatureList::GetInstance()) {
212     return false;
213   }
214 #if BUILDFLAG(BUILD_RUST_JSON_READER)
215   return base::FeatureList::IsEnabled(base::features::kUseRustJsonParser);
216 #else   // BUILDFLAG(BUILD_RUST_JSON_READER)
217   return false;
218 #endif  // BUILDFLAG(BUILD_RUST_JSON_READER)
219 }
220 
221 }  // namespace base
222