1//===- CallGraphJSONFormat.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 "clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h"
10#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
11#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
12#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
13#include "llvm/ADT/STLExtras.h"
14#include "llvm/Support/JSON.h"
15#include <memory>
16
17using namespace llvm;
18using namespace clang;
19using namespace ssaf;
20
21static const char *FailedToReadObjectAtField =
22 "failed to read {0} from field '{1}': expected JSON {2}";
23static const char *FailedToReadObjectAtIndex =
24 "failed to read {0} from index '{1}': expected JSON {2}";
25static const char *ReadingFromField = "reading {0} from field '{1}'";
26static const char *ReadingFromIndex = "reading {0} from index '{1}'";
27
28static json::Object serialize(const EntitySummary &Summary,
29 JSONFormat::EntityIdToJSONFn ToJSON) {
30 const auto &S = static_cast<const CallGraphSummary &>(Summary);
31
32 json::Array DirectCalleesArray;
33 DirectCalleesArray.reserve(S: S.DirectCallees.size());
34 append_range(C&: DirectCalleesArray, R: map_range(C: S.DirectCallees, F: ToJSON));
35
36 json::Array VirtualCalleesArray;
37 VirtualCalleesArray.reserve(S: S.VirtualCallees.size());
38 append_range(C&: VirtualCalleesArray, R: map_range(C: S.VirtualCallees, F: ToJSON));
39
40 return json::Object{
41 {.K: "pretty_name", .V: json::Value(S.PrettyName)},
42 {.K: "direct_callees", .V: std::move(DirectCalleesArray)},
43 {.K: "virtual_callees", .V: std::move(VirtualCalleesArray)},
44 {.K: "def",
45 .V: json::Object{
46 {.K: "file", .V: json::Value(S.Definition.File)},
47 {.K: "line", .V: json::Value(S.Definition.Line)},
48 {.K: "col", .V: json::Value(S.Definition.Column)},
49 }},
50 };
51}
52
53static Expected<std::unique_ptr<EntitySummary>>
54deserialize(const json::Object &Obj, EntityIdTable &IdTable,
55 JSONFormat::EntityIdFromJSONFn FromJSON) {
56 auto Result = std::make_unique<CallGraphSummary>();
57
58 auto PrettyName = Obj.getString(K: "pretty_name");
59 if (!PrettyName) {
60 return ErrorBuilder::create(EC: std::errc::invalid_argument,
61 Fmt: FailedToReadObjectAtField, ArgVals: "PrettyName",
62 ArgVals: "pretty_name", ArgVals: "string")
63 .build();
64 }
65 Result->PrettyName = PrettyName->str();
66
67 const json::Array *CalleesArray = Obj.getArray(K: "direct_callees");
68 if (!CalleesArray) {
69 return ErrorBuilder::create(EC: std::errc::invalid_argument,
70 Fmt: FailedToReadObjectAtField, ArgVals: "DirectCallees",
71 ArgVals: "direct_callees", ArgVals: "array")
72 .build();
73 }
74 for (const auto &[Index, Value] : llvm::enumerate(First: *CalleesArray)) {
75 const json::Object *CalleeObj = Value.getAsObject();
76 if (!CalleeObj) {
77 return ErrorBuilder::create(EC: std::errc::invalid_argument,
78 Fmt: FailedToReadObjectAtIndex, ArgVals: "EntityId", ArgVals&: Index,
79 ArgVals: "object")
80 .context(Fmt: ReadingFromField, ArgVals: "DirectCallees", ArgVals: "direct_callees")
81 .build();
82 }
83 auto ExpectedId = FromJSON(*CalleeObj);
84 if (!ExpectedId) {
85 return ErrorBuilder::wrap(E: ExpectedId.takeError())
86 .context(Fmt: ReadingFromIndex, ArgVals: "EntityId", ArgVals&: Index)
87 .context(Fmt: ReadingFromField, ArgVals: "DirectCallees", ArgVals: "direct_callees")
88 .build();
89 }
90 Result->DirectCallees.insert(x: *ExpectedId);
91 }
92
93 const json::Array *VirtualCalleesArray = Obj.getArray(K: "virtual_callees");
94 if (!VirtualCalleesArray) {
95 return ErrorBuilder::create(EC: std::errc::invalid_argument,
96 Fmt: FailedToReadObjectAtField, ArgVals: "VirtualCallees",
97 ArgVals: "virtual_callees", ArgVals: "array")
98 .build();
99 }
100 for (const auto &[Index, Value] : llvm::enumerate(First: *VirtualCalleesArray)) {
101 const json::Object *CalleeObj = Value.getAsObject();
102 if (!CalleeObj) {
103 return ErrorBuilder::create(EC: std::errc::invalid_argument,
104 Fmt: FailedToReadObjectAtIndex, ArgVals: "EntityId", ArgVals&: Index,
105 ArgVals: "object")
106 .context(Fmt: ReadingFromField, ArgVals: "VirtualCallees", ArgVals: "virtual_callees")
107 .build();
108 }
109 auto ExpectedId = FromJSON(*CalleeObj);
110 if (!ExpectedId) {
111 return ErrorBuilder::wrap(E: ExpectedId.takeError())
112 .context(Fmt: ReadingFromIndex, ArgVals: "EntityId", ArgVals&: Index)
113 .context(Fmt: ReadingFromField, ArgVals: "VirtualCallees", ArgVals: "virtual_callees")
114 .build();
115 }
116 Result->VirtualCallees.insert(x: *ExpectedId);
117 }
118
119 const json::Object *DefObj = Obj.getObject(K: "def");
120 if (!DefObj) {
121 return ErrorBuilder::create(EC: std::errc::invalid_argument,
122 Fmt: FailedToReadObjectAtField, ArgVals: "SourceLocation",
123 ArgVals: "def", ArgVals: "object")
124 .build();
125 }
126 auto File = DefObj->getString(K: "file");
127 if (!File) {
128 return ErrorBuilder::create(EC: std::errc::invalid_argument,
129 Fmt: FailedToReadObjectAtField, ArgVals: "File", ArgVals: "file",
130 ArgVals: "string")
131 .context(Fmt: ReadingFromField, ArgVals: "SourceLocation", ArgVals: "def")
132 .build();
133 }
134 auto Line = DefObj->getInteger(K: "line");
135 if (!Line) {
136 return ErrorBuilder::create(EC: std::errc::invalid_argument,
137 Fmt: FailedToReadObjectAtField, ArgVals: "Line", ArgVals: "line",
138 ArgVals: "number")
139 .context(Fmt: ReadingFromField, ArgVals: "SourceLocation", ArgVals: "def")
140 .build();
141 }
142 auto Col = DefObj->getInteger(K: "col");
143 if (!Col) {
144 return ErrorBuilder::create(EC: std::errc::invalid_argument,
145 Fmt: FailedToReadObjectAtField, ArgVals: "Column", ArgVals: "col",
146 ArgVals: "number")
147 .context(Fmt: ReadingFromField, ArgVals: "SourceLocation", ArgVals: "def")
148 .build();
149 }
150 Result->Definition = {
151 .File: File->str(),
152 .Line: static_cast<unsigned>(*Line),
153 .Column: static_cast<unsigned>(*Col),
154 };
155
156 return std::move(Result);
157}
158
159namespace {
160struct CallGraphJSONFormatInfo final : JSONFormat::FormatInfo {
161 CallGraphJSONFormatInfo()
162 : JSONFormat::FormatInfo(SummaryName(CallGraphSummary::Name.str()),
163 serialize, deserialize) {}
164};
165} // namespace
166
167static llvm::Registry<JSONFormat::FormatInfo>::Add<CallGraphJSONFormatInfo>
168 RegisterFormatInfo(CallGraphSummary::Name,
169 "JSON Format info for CallGraph summary");
170
171// This anchor is used to force the linker to link in the generated object file
172// and thus register the JSON format for CallGraphSummary.
173// NOLINTNEXTLINE(misc-use-internal-linkage)
174volatile int CallGraphJSONFormatAnchorSource = 0;
175