proxygen
DynamicParser.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2016-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /*
17  * Copyright (c) 2015, Facebook, Inc.
18  * All rights reserved.
19  *
20  * This source code is licensed under the BSD-style license found in the
21  * LICENSE file in the root directory of this source tree. An additional grant
22  * of patent rights can be found in the PATENTS file in the same directory.
23  *
24  */
26 
27 #include <sstream>
28 
29 #include <folly/Optional.h>
30 
31 namespace folly {
32 
33 namespace {
34 folly::dynamic& insertAtKey(
35  folly::dynamic* d,
36  bool allow_non_string_keys,
37  const folly::dynamic& key) {
38  if (key.isString()) {
39  return (*d)[key];
40  } else if (key.isNumber() || key.isBool()) {
41  // folly::dynamic allows non-null scalars for keys.
42  return allow_non_string_keys ? (*d)[key] : (*d)[key.asString()];
43  }
44  // One cause might be oddness like p.optional(dynamic::array(...), ...);
45  throw DynamicParserLogicError(
46  "Unsupported key type ",
47  key.typeName(),
48  " of ",
50 }
51 } // namespace
52 
54  const folly::dynamic* lookup_k,
55  const std::exception& ex) {
56  // If descendants of this item, or other keys on it, already reported an
57  // error, the error object would already exist.
59 
60  // Save the original, unparseable value of the item causing the error.
61  //
62  // value() can throw here, but if it does, it is due to programmer error,
63  // so we don't want to report it as a parse error anyway.
64  if (auto* e_val_ptr = e.get_ptr("value")) {
65  // Failing to access distinct keys on the same value can generate
66  // multiple errors, but the value should remain the same.
67  if (*e_val_ptr != value()) {
69  "Overwriting value: ",
70  detail::toPseudoJson(*e_val_ptr),
71  " with ",
73  " for error ",
74  ex.what());
75  }
76  } else {
77  // The e["value"].isNull() trick cannot be used because value().type()
78  // *can* be folly::dynamic::Type::NULLT, so we must hash again.
79  e["value"] = value();
80  }
81 
82  // Differentiate between "parsing value" and "looking up key" errors.
83  auto& e_msg = [&]() -> folly::dynamic& {
84  if (lookup_k == nullptr) { // {object,array}Items, or post-key-lookup
85  return e["error"];
86  }
87  // Multiple key lookups can report errors on the same collection.
88  auto& key_errors = e["key_errors"];
89  if (key_errors.isNull()) {
90  // Treat arrays as integer-keyed objects.
91  key_errors = folly::dynamic::object();
92  }
93  return insertAtKey(&key_errors, allowNonStringKeyErrors_, *lookup_k);
94  }();
95  if (!e_msg.isNull()) {
97  "Overwriting error: ",
98  detail::toPseudoJson(e_msg),
99  " with: ",
100  ex.what());
101  }
102  e_msg = ex.what();
103 
104  switch (onError_) {
105  case OnError::RECORD:
106  break; // Continue parsing
107  case OnError::THROW:
108  stack_.throwErrors(); // Package releaseErrors() into an exception.
109  default:
110  LOG(FATAL) << "Bad onError_: " << static_cast<int>(onError_);
111  }
112 }
113 
115  stackPtr_->key_ = key_;
116  stackPtr_->value_ = value_;
117  if (stackPtr_->unmaterializedSubErrorKeys_.empty()) {
118  // There should be the current error, and the root.
119  CHECK_GE(stackPtr_->subErrors_.size(), 2u)
120  << "Internal bug: out of suberrors";
121  stackPtr_->subErrors_.pop_back();
122  } else {
123  // Errors were never materialized for this subtree, so errors_ only has
124  // ancestors of the item being processed.
125  stackPtr_->unmaterializedSubErrorKeys_.pop_back();
126  CHECK(!stackPtr_->subErrors_.empty()) << "Internal bug: out of suberrors";
127  }
128 }
129 
131  const folly::dynamic& k,
132  const folly::dynamic& v) noexcept {
133  // Save the previous state of the parser.
135  key_ = &k;
136  value_ = &v;
137  // We create errors_ sub-objects lazily to keep the result small.
138  unmaterializedSubErrorKeys_.emplace_back(key_);
139  return guard;
140 }
141 
142 // `noexcept` because if the materialization loop threw, we'd end up with
143 // more suberrors than we started with.
145  bool allow_non_string_keys) noexcept {
146  // Materialize the lazy "key + parent's type" error objects we'll need.
147  CHECK(!subErrors_.empty()) << "Internal bug: out of suberrors";
148  for (const auto& suberror_key : unmaterializedSubErrorKeys_) {
149  auto& nested = (*subErrors_.back())["nested"];
150  if (nested.isNull()) {
151  nested = folly::dynamic::object();
152  }
153  // Find, or insert a dummy entry for the current key
154  auto& my_errors =
155  insertAtKey(&nested, allow_non_string_keys, *suberror_key);
156  if (my_errors.isNull()) {
157  my_errors = folly::dynamic::object();
158  }
159  subErrors_.emplace_back(&my_errors);
160  }
161  unmaterializedSubErrorKeys_.clear();
162  return *subErrors_.back();
163 }
164 
166  if (key_ || unmaterializedSubErrorKeys_.size() != 0 ||
167  subErrors_.size() != 1) {
169  "Do not releaseErrors() while parsing: ",
170  key_ != nullptr,
171  " / ",
172  unmaterializedSubErrorKeys_.size(),
173  " / ",
174  subErrors_.size());
175  }
176  return releaseErrorsImpl();
177 }
178 
180  throw DynamicParserParseError(releaseErrorsImpl());
181 }
182 
184  if (errors_.isNull()) {
185  throw DynamicParserLogicError("Do not releaseErrors() twice");
186  }
187  auto errors = std::move(errors_);
188  errors_ = nullptr; // Prevent a second release.
189  value_ = nullptr; // Break attempts to parse again.
190  return errors;
191 }
192 
193 namespace detail {
195  std::stringstream ss;
196  ss << d;
197  return ss.str();
198 }
199 } // namespace detail
200 
201 } // namespace folly
static ObjectMaker object()
Definition: dynamic-inl.h:240
auto v
const char * typeName() const
Definition: dynamic.cpp:45
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires E e noexcept(noexcept(s.error(std::move(e))))
bool isBool() const
Definition: dynamic-inl.h:495
bool isNumber() const
Definition: dynamic-inl.h:510
const folly::dynamic & value() const
std::string asString() const
Definition: dynamic-inl.h:518
bool isString() const
Definition: dynamic-inl.h:489
std::string toPseudoJson(const folly::dynamic &d)
GuardImpl guard(ErrorHandler &&handler)
Definition: Base.h:840
folly::dynamic & errors(bool allow_non_string_keys) noexcept
PopGuard push(const folly::dynamic &k, const folly::dynamic &v) noexcept
const char * string
Definition: Conv.cpp:212
void reportError(const folly::dynamic *lookup_k, const std::exception &ex)
KeyT k