diff options
author | Anand Thakker <github@anandthakker.net> | 2017-08-15 14:02:18 -0400 |
---|---|---|
committer | Anand Thakker <github@anandthakker.net> | 2017-10-13 12:50:50 -0400 |
commit | d26c30152a4fbe7510d64430504b85d8b5ab4a1d (patch) | |
tree | 103414764c24728742aa60a5c5f89b82645d2682 | |
parent | 4ec5cabcbb2ecdde73b9b278b0503d7b74e6f3a6 (diff) | |
download | qtlocation-mapboxgl-d26c30152a4fbe7510d64430504b85d8b5ab4a1d.tar.gz |
Use doubles for intermediate values, check overflow
-rw-r--r-- | include/mbgl/style/expression/curve.hpp | 41 | ||||
-rw-r--r-- | include/mbgl/style/expression/expression.hpp | 4 | ||||
-rw-r--r-- | include/mbgl/style/expression/literal.hpp | 61 | ||||
-rw-r--r-- | include/mbgl/style/expression/match.hpp | 57 | ||||
-rw-r--r-- | include/mbgl/style/expression/parse.hpp | 2 | ||||
-rw-r--r-- | include/mbgl/style/expression/value.hpp | 15 | ||||
-rw-r--r-- | include/mbgl/util/interpolate.hpp | 8 | ||||
-rw-r--r-- | platform/node/src/node_expression.cpp | 9 | ||||
-rw-r--r-- | src/mbgl/style/expression/compound_expression.cpp | 50 | ||||
-rw-r--r-- | src/mbgl/style/expression/value.cpp | 94 |
10 files changed, 241 insertions, 100 deletions
diff --git a/include/mbgl/style/expression/curve.hpp b/include/mbgl/style/expression/curve.hpp index 11cff10239..9b7a6b64f5 100644 --- a/include/mbgl/style/expression/curve.hpp +++ b/include/mbgl/style/expression/curve.hpp @@ -50,11 +50,11 @@ public: float interpolationFactor(const Range<float>&, const float&) const { return 0; } - float interpolate(const Range<Value>&, const float&) const { + EvaluationResult interpolate(const Range<Value>&, const float&) const { // Assume that Curve::evaluate() will always short circuit due to // interpolationFactor always returning 0. assert(false); - return 0.0f; + return Null; } }; @@ -207,15 +207,46 @@ struct ParseCurve { double previous = - std::numeric_limits<double>::infinity(); for (std::size_t i = 3; i + 1 < length; i += 2) { - const auto& label = toDouble(arrayMember(value, i)); + const optional<mbgl::Value>& labelValue = toValue(arrayMember(value, i)); + optional<double> label; + optional<std::string> labelError; + if (labelValue) { + labelValue->match( + [&](uint64_t n) { + if (!Value::isSafeNumericValue(n)) { + labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."}; + } else { + label = {static_cast<double>(n)}; + } + }, + [&](int64_t n) { + if (!Value::isSafeNumericValue(n)) { + labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."}; + } else { + label = {static_cast<double>(n)}; + } + }, + [&](double n) { + if (!Value::isSafeNumericValue(n)) { + labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."}; + } else { + label = {static_cast<double>(n)}; + } + }, + [&](const auto&) {} + ); + } if (!label) { - ctx.error(R"(Input/output pairs for "curve" expressions must be defined using literal numeric values (not computed expressions) for the input values.)"); + ctx.error(labelError ? *labelError : + R"(Input/output pairs for "curve" expressions must be defined using literal numeric values (not computed expressions) for the input values.)", + i); return ParseResult(); } if (*label < previous) { ctx.error( - R"(Input/output pairs for "curve" expressions must be arranged with input values in strictly ascending order.)" + R"(Input/output pairs for "curve" expressions must be arranged with input values in strictly ascending order.)", + i ); return ParseResult(); } diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp index 0e303f0580..1a3666767b 100644 --- a/include/mbgl/style/expression/expression.hpp +++ b/include/mbgl/style/expression/expression.hpp @@ -69,8 +69,8 @@ public: struct EvaluationResult : public Result<Value> { using Result::Result; - EvaluationResult(const std::array<float, 4>& arr) : - Result(std::vector<Value>(arr.begin(), arr.end())) + EvaluationResult(const std::array<double, 4>& arr) : + Result(toExpressionValue(arr)) {} }; diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp index 7a3e4ba0cc..6a6b62103b 100644 --- a/include/mbgl/style/expression/literal.hpp +++ b/include/mbgl/style/expression/literal.hpp @@ -28,52 +28,87 @@ public: template <class V> static ParseResult parse(const V& value, ParsingContext ctx) { - const Value& parsedValue = parseValue(value); + const optional<Value>& parsedValue = parseValue(value, ctx); + + if (!parsedValue) { + return ParseResult(); + } // special case: infer the item type if possible for zero-length arrays if ( ctx.expected && ctx.expected->template is<type::Array>() && - parsedValue.template is<std::vector<Value>>() + parsedValue->template is<std::vector<Value>>() ) { - auto type = typeOf(parsedValue).template get<type::Array>(); + auto type = typeOf(*parsedValue).template get<type::Array>(); auto expected = ctx.expected->template get<type::Array>(); if ( type.N && (*type.N == 0) && (!expected.N || (*expected.N == 0)) ) { - return ParseResult(std::make_unique<Literal>(expected, parsedValue.template get<std::vector<Value>>())); + return ParseResult(std::make_unique<Literal>(expected, parsedValue->template get<std::vector<Value>>())); } } - return ParseResult(std::make_unique<Literal>(parsedValue)); + return ParseResult(std::make_unique<Literal>(*parsedValue)); } private: template <class V> - static Value parseValue(const V& value) { + static optional<Value> parseValue(const V& value, ParsingContext ctx) { using namespace mbgl::style::conversion; - if (isUndefined(value)) return Null; + if (isUndefined(value)) return {Null}; if (isObject(value)) { std::unordered_map<std::string, Value> result; + bool error = false; eachMember(value, [&] (const std::string& k, const V& v) -> optional<conversion::Error> { - result.emplace(k, parseValue(v)); + if (!error) { + optional<Value> memberValue = parseValue(v, ctx); + if (memberValue) { + result.emplace(k, *memberValue); + } else { + error = true; + } + } return {}; }); - return result; + return error ? optional<Value>() : optional<Value>(result); } + if (isArray(value)) { std::vector<Value> result; const auto length = arrayLength(value); for(std::size_t i = 0; i < length; i++) { - result.emplace_back(parseValue(arrayMember(value, i))); + optional<Value> item = parseValue(arrayMember(value, i), ctx); + if (item) { + result.emplace_back(*item); + } else { + return optional<Value>(); + } } - return result; + return optional<Value>(result); } optional<mbgl::Value> v = toValue(value); assert(v); - - return Value(toExpressionValue(*v)); + + return v->match( + [&](uint64_t n) { return checkNumber(n, ctx); }, + [&](int64_t n) { return checkNumber(n, ctx); }, + [&](double n) { return checkNumber(n, ctx); }, + [&](const auto&) { + return optional<Value>(toExpressionValue(*v)); + } + ); + } + + template <typename T> + static optional<Value> checkNumber(T n, ParsingContext ctx) { + if (!Value::isSafeNumericValue(n)) { + ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + "."); + return optional<Value>(); + } else { + return {static_cast<double>(n)}; + } } Value value; diff --git a/include/mbgl/style/expression/match.hpp b/include/mbgl/style/expression/match.hpp index 9c96f0ae0e..54a3cec9a1 100644 --- a/include/mbgl/style/expression/match.hpp +++ b/include/mbgl/style/expression/match.hpp @@ -153,15 +153,49 @@ private: optional<type::Type> type; auto value = toValue(input); - if (value && isIntegerValue(*value)) { - type = {type::Number}; - result = {*numericValue<int64_t>(*value)}; - } else if (value && value->template is<std::string>()) { - type = {type::String}; - result = {value->template get<std::string>()}; + + if (value) { + value->match( + [&] (uint64_t n) { + if (!Value::isSafeNumericValue(n)) { + ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + "."); + } else { + type = {type::Number}; + result = {static_cast<int64_t>(n)}; + } + }, + [&] (int64_t n) { + if (!Value::isSafeNumericValue(n)) { + ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + "."); + } else { + type = {type::Number}; + result = {n}; + } + }, + [&] (double n) { + if (!Value::isSafeNumericValue(n)) { + ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + "."); + } else if (n != ceilf(n)) { + ctx.error("Numeric branch labels must be integer values."); + } else { + type = {type::Number}; + result = {static_cast<int64_t>(n)}; + } + }, + [&] (const std::string& s) { + type = {type::String}; + result = {s}; + }, + [&] (const auto&) { + ctx.error("Branch labels must be numbers or strings."); + } + ); } else { ctx.error("Branch labels must be numbers or strings."); - return optional<InputType>(); + } + + if (!type) { + return result; } if (!inputType) { @@ -204,15 +238,6 @@ private: std::move(otherwise) )); } - - static bool isIntegerValue(const mbgl::Value& v) { - return v.match( - [] (uint64_t) { return true; }, - [] (int64_t) { return true; }, - [] (double t) { return t == ceilf(t); }, - [] (const auto&) { return false; } - ); - } }; } // namespace expression diff --git a/include/mbgl/style/expression/parse.hpp b/include/mbgl/style/expression/parse.hpp index 1fa1f5efa4..79a3276227 100644 --- a/include/mbgl/style/expression/parse.hpp +++ b/include/mbgl/style/expression/parse.hpp @@ -69,7 +69,7 @@ ParseResult parseExpression(const V& value, ParsingContext context) return ParseResult(); } - parsed = Literal::parse(arrayMember(value, 1), ParsingContext(context, 1, context.expected)); + parsed = Literal::parse(arrayMember(value, 1), context); } else if (*op == "match") { parsed = ParseMatch::parse(value, context); } else if (*op == "curve") { diff --git a/include/mbgl/style/expression/value.hpp b/include/mbgl/style/expression/value.hpp index 8a9cafcec1..5bb893059c 100644 --- a/include/mbgl/style/expression/value.hpp +++ b/include/mbgl/style/expression/value.hpp @@ -19,14 +19,25 @@ struct Value; using ValueBase = variant< NullValue, bool, - // TODO: should we break up the numeric types here like mbgl::Value does? - float, + double, std::string, mbgl::Color, mapbox::util::recursive_wrapper<std::vector<Value>>, mapbox::util::recursive_wrapper<std::unordered_map<std::string, Value>>>; struct Value : ValueBase { using ValueBase::ValueBase; + + // Javascript's Number.MAX_SAFE_INTEGER + static uint64_t max() { return 9007199254740991ULL; } + + static bool isSafeNumericValue(uint64_t x) { return x <= max(); }; + static bool isSafeNumericValue(int64_t x) { + return static_cast<uint64_t>(x > 0 ? x : -x) <= max(); + } + static bool isSafeNumericValue(double x) { + return static_cast<uint64_t>(x > 0 ? x : -x) <= max(); + } + }; Value toExpressionValue(const Value&); diff --git a/include/mbgl/util/interpolate.hpp b/include/mbgl/util/interpolate.hpp index c5e04c459f..aff730a0a2 100644 --- a/include/mbgl/util/interpolate.hpp +++ b/include/mbgl/util/interpolate.hpp @@ -66,11 +66,11 @@ struct Interpolator<std::vector<style::expression::Value>> { if (a.size() == 0) return {}; std::vector<style::expression::Value> result; for (std::size_t i = 0; i < a.size(); i++) { - assert(a[i].template is<float>()); - assert(b[i].template is<float>()); + assert(a[i].template is<double>()); + assert(b[i].template is<double>()); style::expression::Value item = interpolate( - a[i].template get<float>(), - b[i].template get<float>(), + a[i].template get<double>(), + b[i].template get<double>(), t); result.push_back(item); } diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp index c72fc26702..65a9439ddb 100644 --- a/platform/node/src/node_expression.cpp +++ b/platform/node/src/node_expression.cpp @@ -121,7 +121,7 @@ struct ToValue { return scope.Escape(Nan::New(t)); } - v8::Local<v8::Value> operator()(float t) { + v8::Local<v8::Value> operator()(double t) { Nan::EscapableHandleScope scope; return scope.Escape(Nan::New(t)); } @@ -141,7 +141,12 @@ struct ToValue { } v8::Local<v8::Value> operator()(const mbgl::Color& color) { - return operator()(std::vector<Value> { color.r, color.g, color.b, color.a }); + return operator()(std::vector<Value> { + static_cast<double>(color.r), + static_cast<double>(color.g), + static_cast<double>(color.b), + static_cast<double>(color.a) + }); } v8::Local<v8::Value> operator()(const std::unordered_map<std::string, Value>& map) { diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index e7f3fe773c..97d1860fa3 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -133,7 +133,7 @@ static Definition defineGet() { // Define "zoom" expression, it being the only one for which isZoomConstant == false. static std::pair<std::string, Definition> defineZoom() { - auto zoom = [](const EvaluationParameters& params) -> Result<float> { + auto zoom = [](const EvaluationParameters& params) -> Result<double> { if (!params.zoom) { return EvaluationError { "The 'zoom' expression is unavailable in the current evaluation context." @@ -157,13 +157,13 @@ Result<T> assertion(const Value& v) { return v.get<T>(); } -std::string stringifyColor(float r, float g, float b, float a) { +std::string stringifyColor(double r, double g, double b, double a) { return stringify(r) + ", " + stringify(g) + ", " + stringify(b) + ", " + stringify(a); } -Result<mbgl::Color> rgba(float r, float g, float b, float a) { +Result<mbgl::Color> rgba(double r, double g, double b, double a) { if ( r < 0 || r > 255 || g < 0 || g > 255 || @@ -191,27 +191,27 @@ std::unordered_map<std::string, CompoundExpressions::Definition> initializeDefin } std::unordered_map<std::string, Definition> CompoundExpressions::definitions = initializeDefinitions( - define("e", []() -> Result<float> { return 2.718281828459045f; }), - define("pi", []() -> Result<float> { return 3.141592653589793f; }), - define("ln2", []() -> Result<float> { return 0.6931471805599453; }), + define("e", []() -> Result<double> { return 2.718281828459045f; }), + define("pi", []() -> Result<double> { return 3.141592653589793f; }), + define("ln2", []() -> Result<double> { return 0.6931471805599453; }), define("typeof", [](const Value& v) -> Result<std::string> { return toString(typeOf(v)); }), - define("number", assertion<float>), + define("number", assertion<double>), define("string", assertion<std::string>), define("boolean", assertion<bool>), define("to_string", [](const Value& v) -> Result<std::string> { return stringify(v); }), - define("to_number", [](const Value& v) -> Result<float> { - optional<float> result = v.match( - [](const float f) -> optional<float> { return f; }, - [](const std::string& s) -> optional<float> { + define("to_number", [](const Value& v) -> Result<double> { + optional<double> result = v.match( + [](const double f) -> optional<double> { return f; }, + [](const std::string& s) -> optional<double> { try { return std::stof(s); } catch(std::exception) { - return optional<float>(); + return optional<double>(); } }, - [](const auto&) { return optional<float>(); } + [](const auto&) { return optional<double>(); } ); if (!result) { return EvaluationError { @@ -222,15 +222,15 @@ std::unordered_map<std::string, Definition> CompoundExpressions::definitions = i }), define("to_boolean", [](const Value& v) -> Result<bool> { return v.match( - [&] (float f) { return (bool)f; }, + [&] (double f) { return (bool)f; }, [&] (const std::string& s) { return s.length() > 0; }, [&] (bool b) { return b; }, [&] (const NullValue&) { return false; }, [&] (const auto&) { return true; } ); }), - define("to_rgba", [](const mbgl::Color& color) -> Result<std::array<float, 4>> { - return std::array<float, 4> {{ color.r, color.g, color.b, color.a }}; + define("to_rgba", [](const mbgl::Color& color) -> Result<std::array<double, 4>> { + return std::array<double, 4> {{ color.r, color.g, color.b, color.a }}; }), define("parse_color", [](const std::string& colorString) -> Result<mbgl::Color> { @@ -242,16 +242,16 @@ std::unordered_map<std::string, Definition> CompoundExpressions::definitions = i }), define("rgba", rgba), - define("rgb", [](float r, float g, float b) { return rgba(r, g, b, 1.0f); }), + define("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); }), defineZoom(), std::pair<std::string, Definition>("has", defineHas()), std::pair<std::string, Definition>("get", defineGet()), - define("length", [](const std::vector<Value>& arr) -> Result<float> { + define("length", [](const std::vector<Value>& arr) -> Result<double> { return arr.size(); - }, [] (const std::string s) -> Result<float> { + }, [] (const std::string s) -> Result<double> { return s.size(); }), @@ -308,22 +308,22 @@ std::unordered_map<std::string, Definition> CompoundExpressions::definitions = i ); }), - define("+", [](const Varargs<float>& args) -> Result<float> { - float sum = 0.0f; + define("+", [](const Varargs<double>& args) -> Result<double> { + double sum = 0.0f; for (auto arg : args) { sum += arg; } return sum; }), - define("-", [](float a, float b) -> Result<float> { return a - b; }), - define("*", [](const Varargs<float>& args) -> Result<float> { - float prod = 1.0f; + define("-", [](double a, double b) -> Result<double> { return a - b; }), + define("*", [](const Varargs<double>& args) -> Result<double> { + double prod = 1.0f; for (auto arg : args) { prod *= arg; } return prod; }), - define("/", [](float a, float b) -> Result<float> { return a / b; }), + define("/", [](double a, double b) -> Result<double> { return a / b; }), define("&&", [](const Varargs<bool>& args) -> Result<bool> { bool result = true; diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp index c6d6bc2a9a..cbcf7e2206 100644 --- a/src/mbgl/style/expression/value.cpp +++ b/src/mbgl/style/expression/value.cpp @@ -8,7 +8,7 @@ namespace expression { type::Type typeOf(const Value& value) { return value.match( [&](bool) -> type::Type { return type::Boolean; }, - [&](float) -> type::Type { return type::Number; }, + [&](double) -> type::Type { return type::Number; }, [&](const std::string&) -> type::Type { return type::String; }, [&](const mbgl::Color&) -> type::Type { return type::Color; }, [&](const NullValue&) -> type::Type { return type::Null; }, @@ -39,7 +39,7 @@ std::string stringify(const Value& value) { return value.match( [&] (const NullValue&) { return std::string("null"); }, [&] (bool b) { return std::string(b ? "true" : "false"); }, - [&] (float f) { + [&] (double f) { std::stringstream ss; ss << f; return ss.str(); @@ -76,6 +76,27 @@ struct Converter { } }; +template <> +struct Converter<float> { + static Value toExpressionValue(const float& value) { + return static_cast<double>(value); + } + + static optional<float> fromExpressionValue(const Value& value) { + if (value.template is<double>()) { + double v = value.template get<double>(); + if (v <= Value::max()) { + return static_cast<float>(v); + } + } + return optional<float>(); + } + + static type::Type expressionType() { + return type::Number; + } +}; + template<> struct Converter<mbgl::Value> { static Value toExpressionValue(const mbgl::Value& value) { @@ -103,33 +124,50 @@ struct Converter<mbgl::Value> { Value operator()(const std::string& s) { return s; } Value operator()(const bool& b) { return b; } Value operator()(const mbgl::NullValue) { return Null; } - - template <typename T> - Value operator()(const T& v) { return *numericValue<float>(v); } + Value operator()(const double& v) { return v; } + Value operator()(const uint64_t& v) { + return v > static_cast<uint64_t>(Value::max()) ? static_cast<double>(Value::max()) : v; + } + Value operator()(const int64_t& v) { + return v > std::numeric_limits<double>::max() ? std::numeric_limits<double>::max() : v; + } }; +template <typename T, typename Container> +std::vector<Value> toArrayValue(const Container& value) { + std::vector<Value> result; + for (const T& item : value) { + result.push_back(Converter<T>::toExpressionValue(item)); + } + return result; +} + +template <typename T, typename Container> +optional<Container> fromArrayValue(const std::vector<Value>& value) { + Container result; + auto it = result.begin(); + for(const Value& item : value) { + optional<T> convertedItem = Converter<T>::fromExpressionValue(item); + if (!convertedItem) { + return optional<Container>(); + } + *it = *convertedItem; + it = std::next(it); + } + return result; +} + template <typename T, std::size_t N> struct Converter<std::array<T, N>> { static Value toExpressionValue(const std::array<T, N>& value) { - std::vector<Value> result(N); - std::copy_n(value.begin(), N, result.begin()); - return result; + return toArrayValue<T>(value); } static optional<std::array<T, N>> fromExpressionValue(const Value& value) { return value.match( [&] (const std::vector<Value>& v) -> optional<std::array<T, N>> { if (v.size() != N) return optional<std::array<T, N>>(); - std::array<T, N> result; - auto it = result.begin(); - for(const auto& item : v) { - if (!item.template is<T>()) { - return optional<std::array<T, N>>(); - } - *it = item.template get<T>(); - it = std::next(it); - } - return result; + return fromArrayValue<T, std::array<T, N>>(v); }, [&] (const auto&) { return optional<std::array<T, N>>(); } ); @@ -143,22 +181,13 @@ struct Converter<std::array<T, N>> { template <typename T> struct Converter<std::vector<T>> { static Value toExpressionValue(const std::vector<T>& value) { - std::vector<Value> v(value.size()); - std::copy(value.begin(), value.end(), v.begin()); - return v; + return toArrayValue<T>(value); } static optional<std::vector<T>> fromExpressionValue(const Value& value) { return value.match( [&] (const std::vector<Value>& v) -> optional<std::vector<T>> { - std::vector<T> result; - for(const auto& item : v) { - if (!item.template is<T>()) { - return optional<std::vector<T>>(); - } - result.push_back(item.template get<T>()); - } - return result; + return fromArrayValue<T, std::vector<T>>(v); }, [&] (const auto&) { return optional<std::vector<T>>(); } ); @@ -227,7 +256,7 @@ type::Type valueTypeToExpressionType() { template <> type::Type valueTypeToExpressionType<Value>() { return type::Value; } template <> type::Type valueTypeToExpressionType<NullValue>() { return type::Null; } template <> type::Type valueTypeToExpressionType<bool>() { return type::Boolean; } -template <> type::Type valueTypeToExpressionType<float>() { return type::Number; } +template <> type::Type valueTypeToExpressionType<double>() { return type::Number; } template <> type::Type valueTypeToExpressionType<std::string>() { return type::String; } template <> type::Type valueTypeToExpressionType<mbgl::Color>() { return type::Color; } template <> type::Type valueTypeToExpressionType<std::unordered_map<std::string, Value>>() { return type::Object; } @@ -245,6 +274,11 @@ struct instantiate { } }; +// for to_rgba expression +template struct instantiate<std::array<double, 4>>; + +// layout/paint property types +template struct instantiate<float>; template struct instantiate<std::array<float, 2>>; template struct instantiate<std::array<float, 4>>; template struct instantiate<std::vector<float>>; |