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 <utility>
8
9 #include "base/features.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/rust_buildflags.h"
13 #include "third_party/abseil-cpp/absl/types/optional.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_1/wrapper/functions.h"
18 #include "third_party/rust/serde_json_lenient/v0_1/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(const base::StringPiece & json,int options,size_t max_depth)84 JSONReader::Result DecodeJSONInRust(const base::StringPiece& 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_control_chars = (options & base::JSON_ALLOW_CONTROL_CHARS) != 0,
94 .allow_vert_tab = (options & base::JSON_ALLOW_VERT_TAB) != 0,
95 .allow_x_escapes = (options & base::JSON_ALLOW_X_ESCAPES) != 0,
96 .max_depth = max_depth,
97 };
98 static constexpr serde_json_lenient::Functions functions = {
99 .list_append_none_fn = ListAppendNone,
100 .list_append_bool_fn = ListAppendValue<bool>,
101 .list_append_i32_fn = ListAppendValue<int32_t>,
102 .list_append_f64_fn = ListAppendValue<double>,
103 .list_append_str_fn = ListAppendValue<rust::Str, std::string>,
104 .list_append_list_fn = ListAppendList,
105 .list_append_dict_fn = ListAppendDict,
106 .dict_set_none_fn = DictSetNone,
107 .dict_set_bool_fn = DictSetValue<bool>,
108 .dict_set_i32_fn = DictSetValue<int32_t>,
109 .dict_set_f64_fn = DictSetValue<double>,
110 .dict_set_str_fn = DictSetValue<rust::Str, std::string>,
111 .dict_set_list_fn = DictSetList,
112 .dict_set_dict_fn = DictSetDict,
113 };
114
115 base::Value value(base::Value::Type::LIST);
116 auto& ctx = reinterpret_cast<ContextPointer&>(value);
117 serde_json_lenient::DecodeError error;
118 bool ok = serde_json_lenient::decode_json(
119 base::StringPieceToRustSlice(json), rust_options, functions, ctx, error);
120
121 if (!ok) {
122 return base::unexpected(base::JSONReader::Error{
123 .message = std::string(error.message),
124 .line = error.line,
125 .column = error.column,
126 });
127 }
128
129 return std::move(std::move(value.GetList()).back());
130 }
131
132 } // anonymous namespace
133
134 #endif // BUILDFLAG(BUILD_RUST_JSON_READER)
135
136 // static
Read(StringPiece json,int options,size_t max_depth)137 absl::optional<Value> JSONReader::Read(StringPiece json,
138 int options,
139 size_t max_depth) {
140 #if BUILDFLAG(BUILD_RUST_JSON_READER)
141 SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime);
142 if (UsingRust()) {
143 JSONReader::Result result = DecodeJSONInRust(json, options, max_depth);
144 if (!result.has_value()) {
145 return absl::nullopt;
146 }
147 return std::move(*result);
148 } else {
149 internal::JSONParser parser(options, max_depth);
150 return parser.Parse(json);
151 }
152 #else // BUILDFLAG(BUILD_RUST_JSON_READER)
153 internal::JSONParser parser(options, max_depth);
154 return parser.Parse(json);
155 #endif // BUILDFLAG(BUILD_RUST_JSON_READER)
156 }
157
158 // static
ReadDict(StringPiece json,int options,size_t max_depth)159 absl::optional<Value::Dict> JSONReader::ReadDict(StringPiece json,
160 int options,
161 size_t max_depth) {
162 absl::optional<Value> value = Read(json, options, max_depth);
163 if (!value || !value->is_dict()) {
164 return absl::nullopt;
165 }
166 return std::move(*value).TakeDict();
167 }
168
169 // static
ReadAndReturnValueWithError(StringPiece json,int options)170 JSONReader::Result JSONReader::ReadAndReturnValueWithError(StringPiece json,
171 int options) {
172 #if BUILDFLAG(BUILD_RUST_JSON_READER)
173 SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime);
174 if (UsingRust()) {
175 return DecodeJSONInRust(json, options, internal::kAbsoluteMaxDepth);
176 } else {
177 internal::JSONParser parser(options);
178 auto value = parser.Parse(json);
179 if (!value) {
180 Error error;
181 error.message = parser.GetErrorMessage();
182 error.line = parser.error_line();
183 error.column = parser.error_column();
184 return base::unexpected(std::move(error));
185 }
186
187 return std::move(*value);
188 }
189 #else // BUILDFLAG(BUILD_RUST_JSON_READER)
190 internal::JSONParser parser(options);
191 auto value = parser.Parse(json);
192 if (!value) {
193 Error error;
194 error.message = parser.GetErrorMessage();
195 error.line = parser.error_line();
196 error.column = parser.error_column();
197 return base::unexpected(std::move(error));
198 }
199
200 return std::move(*value);
201 #endif // BUILDFLAG(BUILD_RUST_JSON_READER)
202 }
203
204 // static
UsingRust()205 bool JSONReader::UsingRust() {
206 // If features have not yet been enabled, we cannot check the feature, so fall
207 // back to the C++ parser. In practice, this seems to apply to
208 // `ReadPrefsFromDisk()`, which is parsing trusted JSON.
209 if (!base::FeatureList::GetInstance()) {
210 return false;
211 }
212 #if BUILDFLAG(BUILD_RUST_JSON_READER)
213 return base::FeatureList::IsEnabled(base::features::kUseRustJsonParser);
214 #else // BUILDFLAG(BUILD_RUST_JSON_READER)
215 return false;
216 #endif // BUILDFLAG(BUILD_RUST_JSON_READER)
217 }
218
219 } // namespace base
220