blob: 3260fd3e4cb3a5b614983d6af28007dc78ea332e [file] [edit]
//===- CallGraphJSONFormat.cpp --------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysis/Analyses/CallGraph/CallGraphSummary.h"
#include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysis/Core/Support/ErrorBuilder.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/JSON.h"
#include <memory>
using namespace llvm;
using namespace clang;
using namespace ssaf;
static const char *FailedToReadObjectAtField =
"failed to read {0} from field '{1}': expected JSON {2}";
static const char *FailedToReadObjectAtIndex =
"failed to read {0} from index '{1}': expected JSON {2}";
static const char *ReadingFromField = "reading {0} from field '{1}'";
static const char *ReadingFromIndex = "reading {0} from index '{1}'";
static json::Object serialize(const EntitySummary &Summary,
JSONFormat::EntityIdToJSONFn ToJSON) {
const auto &S = static_cast<const CallGraphSummary &>(Summary);
json::Array DirectCalleesArray;
DirectCalleesArray.reserve(S.DirectCallees.size());
append_range(DirectCalleesArray, map_range(S.DirectCallees, ToJSON));
json::Array VirtualCalleesArray;
VirtualCalleesArray.reserve(S.VirtualCallees.size());
append_range(VirtualCalleesArray, map_range(S.VirtualCallees, ToJSON));
return json::Object{
{"pretty_name", json::Value(S.PrettyName)},
{"direct_callees", std::move(DirectCalleesArray)},
{"virtual_callees", std::move(VirtualCalleesArray)},
{"def",
json::Object{
{"file", json::Value(S.Definition.File)},
{"line", json::Value(S.Definition.Line)},
{"col", json::Value(S.Definition.Column)},
}},
};
}
static Expected<std::unique_ptr<EntitySummary>>
deserialize(const json::Object &Obj, EntityIdTable &IdTable,
JSONFormat::EntityIdFromJSONFn FromJSON) {
auto Result = std::make_unique<CallGraphSummary>();
auto PrettyName = Obj.getString("pretty_name");
if (!PrettyName) {
return ErrorBuilder::create(std::errc::invalid_argument,
FailedToReadObjectAtField, "PrettyName",
"pretty_name", "string")
.build();
}
Result->PrettyName = PrettyName->str();
const json::Array *CalleesArray = Obj.getArray("direct_callees");
if (!CalleesArray) {
return ErrorBuilder::create(std::errc::invalid_argument,
FailedToReadObjectAtField, "DirectCallees",
"direct_callees", "array")
.build();
}
for (const auto &[Index, Value] : llvm::enumerate(*CalleesArray)) {
const json::Object *CalleeObj = Value.getAsObject();
if (!CalleeObj) {
return ErrorBuilder::create(std::errc::invalid_argument,
FailedToReadObjectAtIndex, "EntityId", Index,
"object")
.context(ReadingFromField, "DirectCallees", "direct_callees")
.build();
}
auto ExpectedId = FromJSON(*CalleeObj);
if (!ExpectedId) {
return ErrorBuilder::wrap(ExpectedId.takeError())
.context(ReadingFromIndex, "EntityId", Index)
.context(ReadingFromField, "DirectCallees", "direct_callees")
.build();
}
Result->DirectCallees.insert(*ExpectedId);
}
const json::Array *VirtualCalleesArray = Obj.getArray("virtual_callees");
if (!VirtualCalleesArray) {
return ErrorBuilder::create(std::errc::invalid_argument,
FailedToReadObjectAtField, "VirtualCallees",
"virtual_callees", "array")
.build();
}
for (const auto &[Index, Value] : llvm::enumerate(*VirtualCalleesArray)) {
const json::Object *CalleeObj = Value.getAsObject();
if (!CalleeObj) {
return ErrorBuilder::create(std::errc::invalid_argument,
FailedToReadObjectAtIndex, "EntityId", Index,
"object")
.context(ReadingFromField, "VirtualCallees", "virtual_callees")
.build();
}
auto ExpectedId = FromJSON(*CalleeObj);
if (!ExpectedId) {
return ErrorBuilder::wrap(ExpectedId.takeError())
.context(ReadingFromIndex, "EntityId", Index)
.context(ReadingFromField, "VirtualCallees", "virtual_callees")
.build();
}
Result->VirtualCallees.insert(*ExpectedId);
}
const json::Object *DefObj = Obj.getObject("def");
if (!DefObj) {
return ErrorBuilder::create(std::errc::invalid_argument,
FailedToReadObjectAtField, "SourceLocation",
"def", "object")
.build();
}
auto File = DefObj->getString("file");
if (!File) {
return ErrorBuilder::create(std::errc::invalid_argument,
FailedToReadObjectAtField, "File", "file",
"string")
.context(ReadingFromField, "SourceLocation", "def")
.build();
}
auto Line = DefObj->getInteger("line");
if (!Line) {
return ErrorBuilder::create(std::errc::invalid_argument,
FailedToReadObjectAtField, "Line", "line",
"number")
.context(ReadingFromField, "SourceLocation", "def")
.build();
}
auto Col = DefObj->getInteger("col");
if (!Col) {
return ErrorBuilder::create(std::errc::invalid_argument,
FailedToReadObjectAtField, "Column", "col",
"number")
.context(ReadingFromField, "SourceLocation", "def")
.build();
}
Result->Definition = {
File->str(),
static_cast<unsigned>(*Line),
static_cast<unsigned>(*Col),
};
return std::move(Result);
}
namespace {
struct CallGraphJSONFormatInfo final : JSONFormat::FormatInfo {
CallGraphJSONFormatInfo()
: JSONFormat::FormatInfo(SummaryName(CallGraphSummary::Name.str()),
serialize, deserialize) {}
};
} // namespace
static llvm::Registry<JSONFormat::FormatInfo>::Add<CallGraphJSONFormatInfo>
RegisterFormatInfo(CallGraphSummary::Name,
"JSON Format info for CallGraph summary");
namespace clang::ssaf {
// NOLINTNEXTLINE(misc-use-internal-linkage)
volatile int CallGraphJSONFormatAnchorSource = 0;
} // namespace clang::ssaf