Track expression dependencies (#2113)

This commit is contained in:
Tim Sylvester
2024-03-07 10:36:50 -08:00
committed by GitHub
parent 9614b566d8
commit 25678a4d99
105 changed files with 1082 additions and 339 deletions

View File

@@ -24,8 +24,7 @@ private:
}
public:
ColorRampPropertyValue()
: value(nullptr) {}
ColorRampPropertyValue() noexcept = default;
ColorRampPropertyValue(std::shared_ptr<expression::Expression> value_)
: value(std::move(value_)) {}
@@ -46,6 +45,9 @@ public:
bool hasDataDrivenPropertyDifference(const ColorRampPropertyValue&) const { return false; }
const expression::Expression& getExpression() const { return *value; }
using Dependency = style::expression::Dependency;
Dependency getDependencies() const noexcept { return value ? value->dependencies : Dependency::None; }
};
} // namespace style

View File

@@ -11,7 +11,7 @@ namespace expression {
class At : public Expression {
public:
At(std::unique_ptr<Expression> index_, std::unique_ptr<Expression> input_)
: Expression(Kind::At, input_->getType().get<type::Array>().itemType),
: Expression(Kind::At, input_->getType().get<type::Array>().itemType, depsOf(index_) | depsOf(input_)),
index(std::move(index_)),
input(std::move(input_)) {}

View File

@@ -12,7 +12,7 @@ namespace expression {
class Any : public Expression {
public:
Any(std::vector<std::unique_ptr<Expression>> inputs_)
: Expression(Kind::Any, type::Boolean),
: Expression(Kind::Any, type::Boolean, collectDependencies(inputs_)),
inputs(std::move(inputs_)) {}
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
@@ -31,7 +31,7 @@ private:
class All : public Expression {
public:
All(std::vector<std::unique_ptr<Expression>> inputs_)
: Expression(Kind::All, type::Boolean),
: Expression(Kind::All, type::Boolean, collectDependencies(inputs_)),
inputs(std::move(inputs_)) {}
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);

View File

@@ -17,7 +17,7 @@ public:
using Branch = std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>;
Case(type::Type type_, std::vector<Branch> branches_, std::unique_ptr<Expression> otherwise_)
: Expression(Kind::Case, std::move(type_)),
: Expression(Kind::Case, std::move(type_), collectDependencies(branches_) | depsOf(otherwise_)),
branches(std::move(branches_)),
otherwise(std::move(otherwise_)) {}
@@ -32,6 +32,12 @@ public:
std::string getOperator() const override { return "case"; }
protected:
using Expression::collectDependencies;
static Dependency collectDependencies(const std::vector<Branch>& branches) {
return collectDependencies(branches, [](const Branch& v) { return depsOf(v.first) | depsOf(v.second); });
}
private:
std::vector<Branch> branches;
std::unique_ptr<Expression> otherwise;

View File

@@ -15,7 +15,7 @@ class Coalesce : public Expression {
public:
using Args = std::vector<std::unique_ptr<Expression>>;
Coalesce(const type::Type& type_, Args args_)
: Expression(Kind::Coalesce, type_),
: Expression(Kind::Coalesce, type_, collectDependencies(args_)),
args(std::move(args_)) {}
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);

View File

@@ -12,7 +12,7 @@ namespace expression {
class Coercion : public Expression {
public:
Coercion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_);
Coercion(const type::Type& type_, std::vector<std::unique_ptr<Expression>> inputs_);
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);

View File

@@ -21,7 +21,7 @@ public:
void eachChild(const std::function<void(const Expression&)>&) const override;
bool operator==(const Expression& e) const override;
bool operator==(const Expression&) const override;
std::vector<std::optional<Value>> possibleOutputs() const override {
// Technically the set of possible outputs is the combinatoric set of

View File

@@ -11,7 +11,7 @@ namespace expression {
class Error : public Expression {
public:
Error(std::string message_)
: Expression(Kind::Error, type::Error),
: Expression(Kind::Error, type::Error, Dependency::None),
message(std::move(message_)) {}
void eachChild(const std::function<void(const Expression&)>&) const override {}

View File

@@ -5,12 +5,14 @@
#include <mbgl/style/expression/value.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/traits.hpp>
#include <mbgl/util/variant.hpp>
#include <array>
#include <vector>
#include <memory>
#include <numeric>
#include <optional>
#include <vector>
namespace mbgl {
@@ -135,23 +137,23 @@ public:
};
/**
Expression is an abstract class that serves as an interface and base class
for particular expression implementations.
Expression is an abstract class that serves as an interface and base class
for particular expression implementations.
CompoundExpression implements the majority of expressions in the spec by
inferring the argument and output from a simple function (const T0& arg0,
const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are member types of
mbgl::style::expression::Value.
CompoundExpression implements the majority of expressions in the spec by
inferring the argument and output from a simple function (const T0& arg0,
const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are member types of
mbgl::style::expression::Value.
The other Expression subclasses (Let, Curve, Match, etc.) exist in order to
implement expressions that need specialized parsing, type checking, or
evaluation logic that can't be handled by CompoundExpression's inference
mechanism.
The other Expression subclasses (Let, Curve, Match, etc.) exist in order to
implement expressions that need specialized parsing, type checking, or
evaluation logic that can't be handled by CompoundExpression's inference
mechanism.
Each Expression subclass also provides a static
ParseResult ExpressionClass::parse(const V&, ParsingContext),
which handles parsing a style-spec JSON representation of the expression.
*/
Each Expression subclass also provides a static
ParseResult ExpressionClass::parse(const V&, ParsingContext),
which handles parsing a style-spec JSON representation of the expression.
*/
enum class Kind : int32_t {
Coalesce,
@@ -183,10 +185,33 @@ enum class Kind : int32_t {
Slice
};
enum class Dependency : uint32_t {
None = 0,
Feature = 1 << 0, // Data reference
Image = 1 << 1, // Image reference (equivalent to not "runtime constant")
Zoom = 1 << 2, // Zoom level
Location = 1 << 3, // Not used yet, "distance-from-center" not supported
Bind = 1 << 4, // Create variable binding ("let")
Var = 1 << 5, // Use variable binding
Override = 1 << 6, // Property override
MaskCount = 7,
};
inline constexpr static Dependency operator|(Dependency x, Dependency y) noexcept {
return Dependency{mbgl::underlying_type(x) | mbgl::underlying_type(y)};
}
inline constexpr static Dependency operator&(Dependency x, Dependency y) noexcept {
return Dependency{mbgl::underlying_type(x) & mbgl::underlying_type(y)};
}
inline static Dependency& operator|=(Dependency& target, Dependency source) noexcept {
target = target | source;
return target;
}
class Expression {
public:
Expression(Kind kind_, type::Type type_)
: kind(kind_),
Expression(Kind kind_, type::Type type_, Dependency dependencies_)
: dependencies(dependencies_),
kind(kind_),
type(std::move(type_)) {}
virtual ~Expression() = default;
@@ -195,8 +220,8 @@ public:
virtual bool operator==(const Expression&) const = 0;
bool operator!=(const Expression& rhs) const { return !operator==(rhs); }
Kind getKind() const { return kind; };
type::Type getType() const { return type; };
Kind getKind() const { return kind; }
const type::Type& getType() const { return type; }
EvaluationResult evaluate(std::optional<float> zoom,
const Feature& feature,
@@ -229,6 +254,8 @@ public:
virtual std::string getOperator() const = 0;
const Dependency dependencies;
protected:
template <typename T>
static bool childrenEqual(const T& lhs, const T& rhs) {
@@ -261,6 +288,43 @@ protected:
return *(lhs.first) == *(rhs.first) && *(lhs.second) == *(rhs.second);
}
static Dependency depsOf(const std::shared_ptr<Expression>& ex) { return ex ? ex->dependencies : Dependency::None; }
static Dependency depsOf(const std::unique_ptr<Expression>& ex) { return ex ? ex->dependencies : Dependency::None; }
template <typename T>
static Dependency depsOf(const std::optional<T>& ex) {
return ex ? depsOf(*ex) : Dependency::None;
}
/// Combine the dependencies of all the expressions in a container of shared- or unique-pointers to expressions
template <typename T>
static constexpr Dependency collectDependencies(const std::vector<T>& container) {
return std::accumulate(container.begin(), container.end(), Dependency::None, [](auto a, const auto& ex) {
return a | depsOf(ex);
});
}
template <typename T, typename F>
static constexpr Dependency collectDependencies(const std::vector<T>& container, F op) {
return std::accumulate(container.begin(), container.end(), Dependency::None, [&](auto a, const auto& item) {
return a | op(item);
});
}
template <typename K, typename T>
static constexpr Dependency collectDependencies(const std::map<K, T>& container) {
return std::accumulate(container.begin(), container.end(), Dependency::None, [](auto a, const auto& v) {
return a | depsOf(v.second);
});
}
template <typename K, typename T>
static constexpr Dependency collectDependencies(const std::unordered_map<K, T>& container) {
return std::accumulate(container.begin(), container.end(), Dependency::None, [](auto a, const auto& kv) {
return a | depsOf(kv.second);
});
}
private:
Kind kind;
type::Type type;
@@ -268,4 +332,11 @@ private:
} // namespace expression
} // namespace style
namespace util {
std::string toString(style::expression::Dependency);
} // namespace util
std::ostream& operator<<(std::ostream&, style::expression::Dependency);
} // namespace mbgl

View File

@@ -1,9 +1,8 @@
#pragma once
#include <mbgl/style/expression/parsing_context.hpp>
#include <mbgl/style/expression/interpolate.hpp>
#include <mbgl/style/expression/parsing_context.hpp>
#include <mbgl/style/expression/step.hpp>
#include <mbgl/util/variant.hpp>
#include <optional>
@@ -12,9 +11,9 @@ namespace mbgl {
namespace style {
namespace expression {
std::optional<variant<const Interpolate*, const Step*, ParsingError>> findZoomCurve(const expression::Expression* e);
std::optional<variant<const Interpolate*, const Step*, ParsingError>> findZoomCurve(const expression::Expression*);
variant<std::nullptr_t, const Interpolate*, const Step*> findZoomCurveChecked(const expression::Expression* e);
variant<std::nullptr_t, const Interpolate*, const Step*> findZoomCurveChecked(const expression::Expression&);
} // namespace expression
} // namespace style

View File

@@ -10,17 +10,17 @@ namespace expression {
struct FormatExpressionSection {
explicit FormatExpressionSection(std::unique_ptr<Expression> content_);
void setTextSectionOptions(std::optional<std::unique_ptr<Expression>> fontScale_,
std::optional<std::unique_ptr<Expression>> textFont_,
std::optional<std::unique_ptr<Expression>> textColor_);
void setTextSectionOptions(std::unique_ptr<Expression>&& fontScale_,
std::unique_ptr<Expression>&& textFont_,
std::unique_ptr<Expression>&& textColor_);
// Content can be expression that evaluates to String or Image.
std::shared_ptr<Expression> content;
// Text related section options.
std::optional<std::shared_ptr<Expression>> fontScale;
std::optional<std::shared_ptr<Expression>> textFont;
std::optional<std::shared_ptr<Expression>> textColor;
std::shared_ptr<Expression> fontScale;
std::shared_ptr<Expression> textFont;
std::shared_ptr<Expression> textColor;
};
class FormatExpression final : public Expression {
@@ -45,6 +45,11 @@ public:
mbgl::Value serialize() const override;
std::string getOperator() const override { return "format"; }
private:
static Dependency depsOfSection(const FormatExpressionSection& s) {
return depsOf(s.content) | depsOf(s.fontScale) | depsOf(s.textFont) | depsOf(s.textColor);
}
private:
std::vector<FormatExpressionSection> sections;
};

View File

@@ -13,7 +13,9 @@ public:
FormatSectionOverride(const type::Type& type_,
PossiblyEvaluatedPropertyValue<T> defaultValue_,
std::string propertyName_)
: Expression(Kind::FormatSectionOverride, type_),
: Expression(Kind::FormatSectionOverride,
type_,
defaultValue_.getDependencies() | Dependency::Override | Dependency::Feature),
defaultValue(std::move(defaultValue_)),
propertyName(std::move(propertyName_)) {}

View File

@@ -17,7 +17,7 @@ public:
using Bindings = std::map<std::string, std::shared_ptr<Expression>>;
Let(Bindings bindings_, std::unique_ptr<Expression> result_)
: Expression(Kind::Let, result_->getType()),
: Expression(Kind::Let, result_->getType(), collectDependencies(bindings_) | Dependency::Bind),
bindings(std::move(bindings_)),
result(std::move(result_)) {}
@@ -49,7 +49,7 @@ private:
class Var : public Expression {
public:
Var(std::string name_, std::shared_ptr<Expression> value_)
: Expression(Kind::Var, value_->getType()),
: Expression(Kind::Var, value_->getType(), depsOf(value_) | Dependency::Var),
name(std::move(name_)),
value(std::move(value_)) {}

View File

@@ -13,11 +13,11 @@ namespace expression {
class Literal : public Expression {
public:
Literal(const Value& value_)
: Expression(Kind::Literal, typeOf(value_)),
: Expression(Kind::Literal, typeOf(value_), Dependency::None),
value(value_) {}
Literal(const type::Array& type_, std::vector<Value> value_)
: Expression(Kind::Literal, type_),
: Expression(Kind::Literal, type_, Dependency::None),
value(std::move(value_)) {}
EvaluationResult evaluate(const EvaluationContext&) const override { return value; }

View File

@@ -19,7 +19,7 @@ public:
std::unique_ptr<Expression> input_,
Branches branches_,
std::unique_ptr<Expression> otherwise_)
: Expression(Kind::Match, type_),
: Expression(Kind::Match, type_, depsOf(input_) | depsOf(otherwise_) | collectDependencies(branches_)),
input(std::move(input_)),
branches(std::move(branches_)),
otherwise(std::move(otherwise_)) {}

View File

@@ -16,6 +16,9 @@
namespace mbgl {
namespace style {
namespace expression {
enum class Dependency : uint32_t;
} // namespace expression
class LayerObserver;
class Filter;
@@ -159,6 +162,9 @@ public:
mapbox::base::WeakPtr<Layer> makeWeakPtr() { return weakFactory.makeWeakPtr(); }
/// Collect dependencies
expression::Dependency getDependencies() const noexcept;
protected:
virtual Mutable<Impl> mutableBaseImpl() const = 0;
void serializeProperty(Value&, const StyleProperty&, const char* propertyName, bool isPaint) const;

View File

@@ -20,18 +20,20 @@ public:
/// Contains render passes used by the renderer, see `mbgl::RenderPass`.
uint8_t renderPasses = 0u;
virtual expression::Dependency getDependencies() const noexcept = 0;
protected:
LayerProperties(Immutable<Layer::Impl> impl)
: baseImpl(std::move(impl)) {}
};
template <class Derived>
inline const auto& getEvaluated(const Immutable<LayerProperties>& properties) {
inline const auto& getEvaluated(const Immutable<LayerProperties>& properties) noexcept {
return static_cast<const Derived&>(*properties).evaluated;
}
template <class Derived>
inline const auto& getCrossfade(const Immutable<LayerProperties>& properties) {
inline const auto& getCrossfade(const Immutable<LayerProperties>& properties) noexcept {
return static_cast<const Derived&>(*properties).crossfade;
}

View File

@@ -16,9 +16,9 @@ class PropertyExpressionBase {
public:
explicit PropertyExpressionBase(std::unique_ptr<expression::Expression>);
bool isZoomConstant() const noexcept;
bool isFeatureConstant() const noexcept;
bool isRuntimeConstant() const noexcept;
bool isZoomConstant() const noexcept { return isZoomConstant_; }
bool isFeatureConstant() const noexcept { return isFeatureConstant_; }
bool isRuntimeConstant() const noexcept { return isRuntimeConstant_; }
float interpolationFactor(const Range<float>&, float) const noexcept;
Range<float> getCoveringStops(float, float) const noexcept;
const expression::Expression& getExpression() const noexcept;
@@ -30,12 +30,20 @@ public:
bool useIntegerZoom = false;
using Dependency = expression::Dependency;
Dependency getDependencies() const noexcept {
auto v = expression ? expression->dependencies : Dependency::None;
assert(isZoomConstant_ == !(underlying_type(v) & underlying_type(Dependency::Zoom)));
assert(isFeatureConstant_ == !(underlying_type(v) & underlying_type(Dependency::Feature)));
return v;
}
protected:
std::shared_ptr<const expression::Expression> expression;
variant<std::nullptr_t, const expression::Interpolate*, const expression::Step*> zoomCurve;
bool isZoomConstant_;
bool isFeatureConstant_;
bool isRuntimeConstant_;
variant<std::nullptr_t, const expression::Interpolate*, const expression::Step*> zoomCurve;
};
template <class T>

View File

@@ -62,6 +62,13 @@ public:
bool hasDataDrivenPropertyDifference(const PropertyValue<T>& other) const {
return *this != other && (isDataDriven() || other.isDataDriven());
}
using Dependency = style::expression::Dependency;
Dependency getDependencies() const noexcept {
return value.match([](const Undefined&) noexcept { return Dependency::None; },
[](const T&) noexcept { return Dependency::None; },
[](const PropertyExpression<T>& ex) noexcept { return ex.getDependencies(); });
}
};
} // namespace style