source/FieldModel.cpp (197 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include <Show.h> #include <mariana-trench/Access.h> #include <mariana-trench/Assert.h> #include <mariana-trench/FieldModel.h> #include <mariana-trench/Log.h> #include <mariana-trench/Positions.h> namespace marianatrench { namespace { class FieldModelConsistencyError { public: static void raise(const std::string& what) { ERROR(1, "Field Model Consistency Error: {}", what); } }; } // namespace FieldModel::FieldModel( const Field* field, const std::vector<Frame>& sources, const std::vector<Frame>& sinks) : field_(field) { for (const auto& frame : sources) { add_source(frame); } for (const auto& frame : sinks) { add_sink(frame); } } bool FieldModel::operator==(const FieldModel& other) const { return sources_ == other.sources_ && sinks_ == other.sinks_; } bool FieldModel::operator!=(const FieldModel& other) const { return !(*this == other); } FieldModel FieldModel::instantiate(const Field* field) const { FieldModel field_model(field); for (const auto& sources_set : sources_) { for (const auto& source : sources_set) { field_model.add_source(source); } } for (const auto& sinks_set : sinks_) { for (const auto& sink : sinks_set) { field_model.add_sink(sink); } } return field_model; } bool FieldModel::empty() const { return sources_.is_bottom() && sinks_.is_bottom(); } void FieldModel::check_frame_consistency( const Frame& frame, std::string_view kind) const { if (frame.is_bottom()) { FieldModelConsistencyError::raise(fmt::format( "Model for field `{}` contains a bottom {}.", show(field_), kind)); } if (frame.is_artificial_source()) { FieldModelConsistencyError::raise(fmt::format( "Model for field `{}` contains an artificial {}.", show(field_), kind)); } if (field_ && frame.field_origins().empty()) { FieldModelConsistencyError::raise(fmt::format( "Model for field `{}` contains a {} without field origins.", show(field_), kind)); } if (!frame.callee_port().root().is_leaf() || frame.call_position() != nullptr || frame.distance() != 0 || !frame.origins().is_bottom() || frame.via_type_of_ports().size() != 0 || !frame.local_positions().empty() || frame.canonical_names().size() != 0) { FieldModelConsistencyError::raise(fmt::format( "Frame {} in {}s for field `{}` contains an unexpected non-empty or non-bottom value for a field.", show(frame), show(kind), show(field_))); } } namespace { Frame add_field_callee(const Field* MT_NULLABLE field, const Frame& frame) { if (field == nullptr) { return frame; } return Frame( frame.kind(), frame.callee_port(), frame.callee(), field, frame.call_position(), frame.distance(), frame.origins(), frame.field_origins(), frame.inferred_features(), frame.locally_inferred_features(), frame.user_features(), frame.via_type_of_ports(), frame.via_value_of_ports(), frame.local_positions(), frame.canonical_names()); } } // namespace void FieldModel::add_source(Frame source) { mt_assert(source.is_leaf()); if (field_ && source.field_origins().empty()) { source.set_field_origins(FieldSet{field_}); } check_frame_consistency(source, "source"); sources_.add(add_field_callee(field_, source)); } void FieldModel::add_sink(Frame sink) { mt_assert(sink.is_leaf()); if (field_ && sink.field_origins().empty()) { sink.set_field_origins(FieldSet{field_}); } check_frame_consistency(sink, "sink"); sinks_.add(add_field_callee(field_, sink)); } void FieldModel::join_with(const FieldModel& other) { if (this == &other) { return; } mt_if_expensive_assert(auto previous = *this); sources_.join_with(other.sources_); sinks_.join_with(other.sinks_); mt_expensive_assert(previous.leq(*this) && other.leq(*this)); } FieldModel FieldModel::from_json( const Field* MT_NULLABLE field, const Json::Value& value, Context& context) { JsonValidation::validate_object(value); FieldModel model(field); for (auto source_value : JsonValidation::null_or_array(value, /* field */ "sources")) { model.add_source(Frame::from_json(source_value, context)); } for (auto sink_value : JsonValidation::null_or_array(value, /* field */ "sinks")) { model.add_sink(Frame::from_json(sink_value, context)); } return model; } Json::Value FieldModel::to_json() const { auto value = Json::Value(Json::objectValue); if (field_) { value["field"] = field_->to_json(); } if (!sources_.is_bottom()) { auto sources_value = Json::Value(Json::arrayValue); for (const auto& sources : sources_) { for (const auto& source : sources) { mt_assert(!source.is_bottom()); sources_value.append(source.to_json()); } } value["sources"] = sources_value; } if (!sinks_.is_bottom()) { auto sinks_value = Json::Value(Json::arrayValue); for (const auto& sinks : sinks_) { for (const auto& sink : sinks) { mt_assert(!sink.is_bottom()); sinks_value.append(sink.to_json()); } } value["sinks"] = sinks_value; } return value; } Json::Value FieldModel::to_json(Context& context) const { auto value = to_json(); value["position"] = context.positions->unknown()->to_json(); return value; } std::ostream& operator<<(std::ostream& out, const FieldModel& model) { out << "\nFieldModel(field=`" << show(model.field_) << "`"; if (!model.sources_.is_bottom()) { out << ",\n sources={\n"; for (const auto& sources : model.sources_) { for (const auto& source : sources) { out << " " << source << ",\n"; } } out << " }"; } if (!model.sinks_.is_bottom()) { out << ",\n sinks={\n"; for (const auto& sinks : model.sinks_) { for (const auto& sink : sinks) { out << " " << sink << ",\n"; } } out << " }"; } return out << ")"; } } // namespace marianatrench