Skip to content

Commit b3f1e52

Browse files
handle RapidYAML errors and convert them to Jsonnet runtime errors
RapidYAML primarily uses a callback based error handling mechanism. Define an error handling callback which throws an exception type, then catch it in the std.parseYaml implementation and generate the appropriate Jsonnet runtime error. Fixes #1293
1 parent b230e92 commit b3f1e52

File tree

3 files changed

+50
-16
lines changed

3 files changed

+50
-16
lines changed

core/vm.cpp

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ limitations under the License.
2121
#include <memory>
2222
#include <set>
2323
#include <string>
24+
#include <string_view>
2425

2526
#include "desugarer.h"
2627
#include "json.h"
@@ -52,6 +53,15 @@ using json = nlohmann::json;
5253

5354
namespace {
5455

56+
// Custom exception type thrown by RapidYAML error handling callback.
57+
// Should be caught and converted to a runtime error.
58+
struct RapidYamlError {
59+
// Construct any way that std::string can be constructed.
60+
template <typename... Args>
61+
explicit RapidYamlError(Args&&... args): msg_(std::forward<Args>(args)...) {}
62+
std::string msg_;
63+
};
64+
5565
/** Stack frames.
5666
*
5767
* Of these, FRAME_CALL is the most special, as it is the only frame the stack
@@ -1683,26 +1693,31 @@ class Interpreter {
16831693
const AST *builtinParseYaml(const LocationRange &loc, const std::vector<Value> &args)
16841694
{
16851695
validateBuiltinArgs(loc, "parseYaml", args, {Value::STRING});
1686-
16871696
std::string value = encode_utf8(static_cast<HeapString *>(args[0].v.h)->value);
1688-
1689-
ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(value));
1690-
16911697
json j;
1692-
if (tree.type(tree.root_id()).is_notype()) {
1693-
scratch = makeNull();
1694-
return nullptr;
1695-
} else if (tree.is_stream(tree.root_id())) {
1696-
ryml::ConstNodeRef root = tree.crootref();
1697-
for (ryml::ConstNodeRef node : root.children()) {
1698+
try {
1699+
// Use a custom EventHandler so we can attach error handling.
1700+
ryml::EventHandlerTree et{ryml::Callbacks{
1701+
nullptr, nullptr, nullptr, &Interpreter::handleRapidYamlError
1702+
}};
1703+
ryml::Parser pe(&et);
1704+
ryml::Tree tree = ryml::parse_in_arena(&pe, ryml::to_csubstr(value));
1705+
1706+
if (tree.type(tree.root_id()).is_notype()) {
1707+
// Nothing to do; `j` is already null!
1708+
} else if (tree.is_stream(tree.root_id())) {
1709+
for (ryml::ConstNodeRef node : tree.crootref().children()) {
1710+
std::ostringstream jsonText;
1711+
jsonText << ryml::as_json(node);
1712+
j.push_back(json::parse(jsonText.str()));
1713+
}
1714+
} else {
16981715
std::ostringstream jsonText;
1699-
jsonText << ryml::as_json(node);
1700-
j.push_back(json::parse(jsonText.str()));
1716+
jsonText << ryml::as_json(tree);
1717+
j = json::parse(jsonText.str());
17011718
}
1702-
} else {
1703-
std::ostringstream jsonText;
1704-
jsonText << ryml::as_json(tree);
1705-
j = json::parse(jsonText.str());
1719+
} catch (const RapidYamlError& exc) {
1720+
throw makeError(loc, exc.msg_);
17061721
}
17071722
bool filled_unused;
17081723
otherJsonToHeap(j, filled_unused, scratch);
@@ -3407,6 +3422,19 @@ class Interpreter {
34073422
}
34083423
return r;
34093424
}
3425+
3426+
static void handleRapidYamlError(const char* inner_msg, size_t length, ryml::Location loc, void * /* unused: userdata */)
3427+
{
3428+
std::ostringstream msg;
3429+
msg << "YAML error: " << loc.line << ":";
3430+
if (loc.col) {
3431+
msg << loc.col << ":";
3432+
} else if (loc.offset) {
3433+
msg << loc.offset << ":";
3434+
}
3435+
msg << " " << std::string_view(inner_msg, length);
3436+
throw RapidYamlError(std::move(msg.str()));
3437+
}
34103438
};
34113439

34123440
} // namespace
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
std.parseYaml('a: b:')
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
RUNTIME ERROR: YAML error: 1:6: ERROR: two colons on same line
2+
1:6: a: b: (size=5)
3+
^ (cols 6-6)
4+
5+
error.std_parseYaml1.jsonnet:1:1-23

0 commit comments

Comments
 (0)