1///===- llvm/Frontend/Offloading/PropertySet.cpp --------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/Frontend/Offloading/PropertySet.h"
10#include "llvm/Support/Base64.h"
11#include "llvm/Support/JSON.h"
12#include "llvm/Support/MemoryBufferRef.h"
13
14using namespace llvm;
15using namespace llvm::offloading;
16
17void llvm::offloading::writePropertiesToJSON(
18 const PropertySetRegistry &PSRegistry, raw_ostream &Out) {
19 json::OStream J(Out);
20 J.object(Contents: [&] {
21 for (const auto &[CategoryName, PropSet] : PSRegistry) {
22 auto PropSetCapture = PropSet;
23 J.attributeObject(Key: CategoryName, Contents: [&] {
24 for (const auto &[PropName, PropVal] : PropSetCapture) {
25 switch (PropVal.index()) {
26 case 0:
27 J.attribute(Key: PropName, Contents: std::get<uint32_t>(v: PropVal));
28 break;
29 case 1:
30 J.attribute(Key: PropName, Contents: encodeBase64(Bytes: std::get<ByteArray>(v: PropVal)));
31 break;
32 default:
33 llvm_unreachable("unsupported property type");
34 }
35 }
36 });
37 }
38 });
39}
40
41// note: createStringError has an overload that takes a format string,
42// but it uses llvm::format instead of llvm::formatv, which does
43// not work with json::Value. This is a helper function to use
44// llvm::formatv with createStringError.
45template <typename... Ts> auto createStringErrorV(Ts &&...Args) {
46 return createStringError(formatv(std::forward<Ts>(Args)...));
47}
48
49Expected<PropertyValue>
50readPropertyValueFromJSON(const json::Value &PropValueVal) {
51 if (std::optional<uint64_t> Val = PropValueVal.getAsUINT64())
52 return PropertyValue(static_cast<uint32_t>(*Val));
53
54 if (std::optional<StringRef> Val = PropValueVal.getAsString()) {
55 std::vector<char> Decoded;
56 if (Error E = decodeBase64(Input: *Val, Output&: Decoded))
57 return createStringErrorV(Args: "unable to base64 decode the string {0}: {1}",
58 Args&: Val, Args: toString(E: std::move(E)));
59 return PropertyValue(ByteArray(Decoded.begin(), Decoded.end()));
60 }
61
62 return createStringErrorV(Args: "expected a uint64 or a string, got {0}",
63 Args: PropValueVal);
64}
65
66Expected<PropertySetRegistry>
67llvm::offloading::readPropertiesFromJSON(MemoryBufferRef Buf) {
68 PropertySetRegistry Res;
69 Expected<json::Value> V = json::parse(JSON: Buf.getBuffer());
70 if (Error E = V.takeError())
71 return E;
72
73 const json::Object *O = V->getAsObject();
74 if (!O)
75 return createStringErrorV(
76 Args: "error while deserializing property set registry: "
77 "expected JSON object, got {0}",
78 Args&: *V);
79
80 for (const auto &[CategoryName, Value] : *O) {
81 const json::Object *PropSetVal = Value.getAsObject();
82 if (!PropSetVal)
83 return createStringErrorV(Args: "error while deserializing property set {0}: "
84 "expected JSON array, got {1}",
85 Args: CategoryName.str(), Args: Value);
86
87 PropertySet &PropSet = Res[CategoryName.str()];
88 for (const auto &[PropName, PropValueVal] : *PropSetVal) {
89 Expected<PropertyValue> Prop = readPropertyValueFromJSON(PropValueVal);
90 if (Error E = Prop.takeError())
91 return createStringErrorV(
92 Args: "error while deserializing property {0} in property set {1}: {2}",
93 Args: PropName.str(), Args: CategoryName.str(), Args: toString(E: std::move(E)));
94
95 auto [It, Inserted] =
96 PropSet.try_emplace(k: PropName.str(), args: std::move(*Prop));
97 assert(Inserted && "Property already exists in PropertySet");
98 (void)Inserted;
99 }
100 }
101 return Res;
102}
103