diff --git a/.mapping.json b/.mapping.json index 1b82bcde9..d205422a2 100644 --- a/.mapping.json +++ b/.mapping.json @@ -20600,11 +20600,12 @@ "test_data/expression_test_data/variables_names.json":"divkit/public/test_data/expression_test_data/variables_names.json", "test_data/expression_test_data/variables_values.json":"divkit/public/test_data/expression_test_data/variables_values.json", "test_data/expression_test_data/whitespace.json":"divkit/public/test_data/expression_test_data/whitespace.json", + "test_data/integration_test_data/decl_expressions_cycle.json":"divkit/public/test_data/integration_test_data/decl_expressions_cycle.json", "test_data/integration_test_data/decl_expressions_item_builder.json":"divkit/public/test_data/integration_test_data/decl_expressions_item_builder.json", "test_data/integration_test_data/decl_expressions_item_builder_override.json":"divkit/public/test_data/integration_test_data/decl_expressions_item_builder_override.json", - "test_data/integration_test_data/decl_extressions_nesting.json":"divkit/public/test_data/integration_test_data/decl_extressions_nesting.json", - "test_data/integration_test_data/decl_extressions_simple.json":"divkit/public/test_data/integration_test_data/decl_extressions_simple.json", - "test_data/integration_test_data/decl_extressions_transitive.json":"divkit/public/test_data/integration_test_data/decl_extressions_transitive.json", + "test_data/integration_test_data/decl_expressions_nesting.json":"divkit/public/test_data/integration_test_data/decl_expressions_nesting.json", + "test_data/integration_test_data/decl_expressions_simple.json":"divkit/public/test_data/integration_test_data/decl_expressions_simple.json", + "test_data/integration_test_data/decl_expressions_transitive.json":"divkit/public/test_data/integration_test_data/decl_expressions_transitive.json", "test_data/integration_test_data/expression_with_several_local_functions.json":"divkit/public/test_data/integration_test_data/expression_with_several_local_functions.json", "test_data/integration_test_data/local_functions_array.json":"divkit/public/test_data/integration_test_data/local_functions_array.json", "test_data/integration_test_data/local_functions_color.json":"divkit/public/test_data/integration_test_data/local_functions_color.json", diff --git a/client/ios/DivKit/DivKitComponents.swift b/client/ios/DivKit/DivKitComponents.swift index f6d504ab5..5236950c2 100644 --- a/client/ios/DivKit/DivKitComponents.swift +++ b/client/ios/DivKit/DivKitComponents.swift @@ -345,7 +345,14 @@ public final class DivKitComponents { public func setVariablesAndTriggers(divData: DivData, cardId: DivCardID) { updateAggregator.performWithNoUpdates { - let divDataVariables = divData.variables?.extractDivVariableValues() ?? [:] + let resolver = ExpressionResolver( + path: cardId.path, + variablesStorage: variablesStorage, + functionsStorage: functionsStorage, + persistentValuesStorage: persistentValuesStorage, + reporter: reporter + ) + let divDataVariables = divData.variables?.extractDivVariableValues(resolver) ?? [:] variablesStorage.append( variables: divDataVariables, for: cardId, diff --git a/client/ios/DivKit/Expressions/ExpressionResolver.swift b/client/ios/DivKit/Expressions/ExpressionResolver.swift index 5ce6f5468..d9ea76d35 100644 --- a/client/ios/DivKit/Expressions/ExpressionResolver.swift +++ b/client/ios/DivKit/Expressions/ExpressionResolver.swift @@ -296,3 +296,16 @@ public final class ExpressionResolver { } } } + +extension ExpressionResolver { + func modifying(variableValueProvider: @escaping (String) -> Any?) -> ExpressionResolver { + ExpressionResolver( + functionsProvider: functionsProvider, + customFunctionsStorageProvider: customFunctionsStorageProvider, + variableValueProvider: { + variableValueProvider($0) ?? self.variableValueProvider($0) + }, + errorTracker: errorTracker + ) + } +} diff --git a/client/ios/DivKit/Extensions/DivBase/DivBaseExtensions.swift b/client/ios/DivKit/Extensions/DivBase/DivBaseExtensions.swift index a5137dc00..fe93a2a8a 100644 --- a/client/ios/DivKit/Extensions/DivBase/DivBaseExtensions.swift +++ b/client/ios/DivKit/Extensions/DivBase/DivBaseExtensions.swift @@ -144,7 +144,7 @@ extension DivBase { context.variablesStorage.initializeIfNeeded( path: path, - variables: variables?.extractDivVariableValues() ?? [:] + variables: variables?.extractDivVariableValues(context.expressionResolver) ?? [:] ) context.triggersStorage?.setIfNeeded( diff --git a/client/ios/DivKit/Variables/DivVariablesStorage.swift b/client/ios/DivKit/Variables/DivVariablesStorage.swift index d77577334..7e89d3c97 100644 --- a/client/ios/DivKit/Variables/DivVariablesStorage.swift +++ b/client/ios/DivKit/Variables/DivVariablesStorage.swift @@ -351,38 +351,55 @@ private func parseCollectionVar(_ val: String) -> T? { } extension Collection { - public func extractDivVariableValues() -> DivVariables { + public func extractDivVariableValues(_ resolver: ExpressionResolver) -> DivVariables { var variables = DivVariables() forEach { variable in + let resolver = resolver.modifying(variableValueProvider: { + variables[DivVariableName(rawValue: $0)]?.typedValue() + }) switch variable { case let .stringVariable(stringVariable): let name = DivVariableName(rawValue: stringVariable.name) - if variables.keys.contains(name) { return } - variables[name] = .string(stringVariable.value) + guard !variables.keys.contains(name), let value = stringVariable.resolveValue(resolver) + else { return } + + variables[name] = .string(value) case let .numberVariable(numberVariable): let name = DivVariableName(rawValue: numberVariable.name) - if variables.keys.contains(name) { return } - variables[name] = .number(numberVariable.value) + guard !variables.keys.contains(name), let value = numberVariable.resolveValue(resolver) + else { return } + + variables[name] = .number(value) case let .integerVariable(integerVariable): let name = DivVariableName(rawValue: integerVariable.name) - if variables.keys.contains(name) { return } - variables[name] = .integer(integerVariable.value) + guard !variables.keys.contains(name), let value = integerVariable.resolveValue(resolver) + else { return } + + variables[name] = .integer(value) case let .booleanVariable(booleanVariable): let name = DivVariableName(rawValue: booleanVariable.name) - if variables.keys.contains(name) { return } - variables[name] = .bool(booleanVariable.value) + guard !variables.keys.contains(name), let value = booleanVariable.resolveValue(resolver) + else { return } + + variables[name] = .bool(value) case let .colorVariable(colorVariable): let name = DivVariableName(rawValue: colorVariable.name) - if variables.keys.contains(name) { return } - variables[name] = .color(colorVariable.value) + guard !variables.keys.contains(name), let value = colorVariable.resolveValue(resolver) + else { return } + + variables[name] = .color(value) case let .urlVariable(urlVariable): let name = DivVariableName(rawValue: urlVariable.name) - if variables.keys.contains(name) { return } - variables[name] = .url(urlVariable.value) + guard !variables.keys.contains(name), let value = urlVariable.resolveValue(resolver) + else { return } + + variables[name] = .url(value) case let .dictVariable(dictVariable): let name = DivVariableName(rawValue: dictVariable.name) - if variables.keys.contains(name) { return } - if let dictionary = DivDictionary.fromAny(dictVariable.value) { + guard !variables.keys.contains(name), let value = dictVariable.resolveValue(resolver) + else { return } + + if let dictionary = DivDictionary.fromAny(value) { variables[name] = .dict(dictionary) } else { DivKitLogger.error("Incorrect value for dict variable \(name): \(dictVariable.value)") @@ -390,8 +407,10 @@ extension Collection { } case let .arrayVariable(arrayVariable): let name = DivVariableName(rawValue: arrayVariable.name) - if variables.keys.contains(name) { return } - if let array = DivArray.fromAny(arrayVariable.value) { + guard !variables.keys.contains(name), let value = arrayVariable.resolveValue(resolver) + else { return } + + if let array = DivArray.fromAny(value) { variables[name] = .array(array) } else { DivKitLogger.error("Incorrect value for array variable \(name): \(arrayVariable.value)") diff --git a/client/ios/DivKit/generated_sources/ArrayVariable.swift b/client/ios/DivKit/generated_sources/ArrayVariable.swift index 7ab862e85..7fcbd8086 100644 --- a/client/ios/DivKit/generated_sources/ArrayVariable.swift +++ b/client/ios/DivKit/generated_sources/ArrayVariable.swift @@ -7,11 +7,15 @@ import VGSL public final class ArrayVariable: @unchecked Sendable { public static let type: String = "array" public let name: String - public let value: [Any] + public let value: Expression<[Any]> + + public func resolveValue(_ resolver: ExpressionResolver) -> [Any]? { + resolver.resolveArray(value) + } init( name: String, - value: [Any] + value: Expression<[Any]> ) { self.name = name self.value = value @@ -37,7 +41,7 @@ extension ArrayVariable: Serializable { var result: [String: ValidSerializationValue] = [:] result["type"] = Self.type result["name"] = name - result["value"] = value + result["value"] = value.toValidSerializationValue() return result } } diff --git a/client/ios/DivKit/generated_sources/ArrayVariableTemplate.swift b/client/ios/DivKit/generated_sources/ArrayVariableTemplate.swift index 730973c54..5a7c4493b 100644 --- a/client/ios/DivKit/generated_sources/ArrayVariableTemplate.swift +++ b/client/ios/DivKit/generated_sources/ArrayVariableTemplate.swift @@ -8,20 +8,20 @@ public final class ArrayVariableTemplate: TemplateValue, @unchecked Sendable { public static let type: String = "array" public let parent: String? public let name: Field? - public let value: Field<[Any]>? + public let value: Field>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( parent: dictionary["type"] as? String, name: dictionary.getOptionalField("name"), - value: dictionary.getOptionalField("value") + value: dictionary.getOptionalExpressionField("value") ) } init( parent: String?, name: Field? = nil, - value: Field<[Any]>? = nil + value: Field>? = nil ) { self.parent = parent self.name = name @@ -59,7 +59,7 @@ public final class ArrayVariableTemplate: TemplateValue, @unchecked Sendable { return resolveOnlyLinks(context: context, parent: parent) } var nameValue: DeserializationResult = { parent?.name?.value() ?? .noValue }() - var valueValue: DeserializationResult<[Any]> = { parent?.value?.value() ?? .noValue }() + var valueValue: DeserializationResult> = { parent?.value?.value() ?? .noValue }() _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable diff --git a/client/ios/DivKit/generated_sources/BooleanVariable.swift b/client/ios/DivKit/generated_sources/BooleanVariable.swift index 2f2905c7a..8f093654b 100644 --- a/client/ios/DivKit/generated_sources/BooleanVariable.swift +++ b/client/ios/DivKit/generated_sources/BooleanVariable.swift @@ -7,11 +7,15 @@ import VGSL public final class BooleanVariable: Sendable { public static let type: String = "boolean" public let name: String - public let value: Bool + public let value: Expression + + public func resolveValue(_ resolver: ExpressionResolver) -> Bool? { + resolver.resolveNumeric(value) + } init( name: String, - value: Bool + value: Expression ) { self.name = name self.value = value @@ -37,7 +41,7 @@ extension BooleanVariable: Serializable { var result: [String: ValidSerializationValue] = [:] result["type"] = Self.type result["name"] = name - result["value"] = value + result["value"] = value.toValidSerializationValue() return result } } diff --git a/client/ios/DivKit/generated_sources/BooleanVariableTemplate.swift b/client/ios/DivKit/generated_sources/BooleanVariableTemplate.swift index f22e7009c..4b4734c5c 100644 --- a/client/ios/DivKit/generated_sources/BooleanVariableTemplate.swift +++ b/client/ios/DivKit/generated_sources/BooleanVariableTemplate.swift @@ -8,20 +8,20 @@ public final class BooleanVariableTemplate: TemplateValue, Sendable { public static let type: String = "boolean" public let parent: String? public let name: Field? - public let value: Field? + public let value: Field>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( parent: dictionary["type"] as? String, name: dictionary.getOptionalField("name"), - value: dictionary.getOptionalField("value") + value: dictionary.getOptionalExpressionField("value") ) } init( parent: String?, name: Field? = nil, - value: Field? = nil + value: Field>? = nil ) { self.parent = parent self.name = name @@ -59,7 +59,7 @@ public final class BooleanVariableTemplate: TemplateValue, Sendable { return resolveOnlyLinks(context: context, parent: parent) } var nameValue: DeserializationResult = { parent?.name?.value() ?? .noValue }() - var valueValue: DeserializationResult = { parent?.value?.value() ?? .noValue }() + var valueValue: DeserializationResult> = { parent?.value?.value() ?? .noValue }() _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable diff --git a/client/ios/DivKit/generated_sources/ColorVariable.swift b/client/ios/DivKit/generated_sources/ColorVariable.swift index 833b9ebf5..b0128efd2 100644 --- a/client/ios/DivKit/generated_sources/ColorVariable.swift +++ b/client/ios/DivKit/generated_sources/ColorVariable.swift @@ -7,11 +7,15 @@ import VGSL public final class ColorVariable: Sendable { public static let type: String = "color" public let name: String - public let value: Color + public let value: Expression + + public func resolveValue(_ resolver: ExpressionResolver) -> Color? { + resolver.resolveColor(value) + } init( name: String, - value: Color + value: Expression ) { self.name = name self.value = value @@ -37,7 +41,7 @@ extension ColorVariable: Serializable { var result: [String: ValidSerializationValue] = [:] result["type"] = Self.type result["name"] = name - result["value"] = value.hexString + result["value"] = value.toValidSerializationValue() return result } } diff --git a/client/ios/DivKit/generated_sources/ColorVariableTemplate.swift b/client/ios/DivKit/generated_sources/ColorVariableTemplate.swift index be8ef7251..72dceb311 100644 --- a/client/ios/DivKit/generated_sources/ColorVariableTemplate.swift +++ b/client/ios/DivKit/generated_sources/ColorVariableTemplate.swift @@ -8,20 +8,20 @@ public final class ColorVariableTemplate: TemplateValue, Sendable { public static let type: String = "color" public let parent: String? public let name: Field? - public let value: Field? + public let value: Field>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( parent: dictionary["type"] as? String, name: dictionary.getOptionalField("name"), - value: dictionary.getOptionalField("value", transform: Color.color(withHexString:)) + value: dictionary.getOptionalExpressionField("value", transform: Color.color(withHexString:)) ) } init( parent: String?, name: Field? = nil, - value: Field? = nil + value: Field>? = nil ) { self.parent = parent self.name = name @@ -59,7 +59,7 @@ public final class ColorVariableTemplate: TemplateValue, Sendable { return resolveOnlyLinks(context: context, parent: parent) } var nameValue: DeserializationResult = { parent?.name?.value() ?? .noValue }() - var valueValue: DeserializationResult = { parent?.value?.value() ?? .noValue }() + var valueValue: DeserializationResult> = { parent?.value?.value() ?? .noValue }() _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable diff --git a/client/ios/DivKit/generated_sources/DictVariable.swift b/client/ios/DivKit/generated_sources/DictVariable.swift index d94ce093f..c526bb57d 100644 --- a/client/ios/DivKit/generated_sources/DictVariable.swift +++ b/client/ios/DivKit/generated_sources/DictVariable.swift @@ -7,11 +7,15 @@ import VGSL public final class DictVariable: @unchecked Sendable { public static let type: String = "dict" public let name: String - public let value: [String: Any] + public let value: Expression<[String: Any]> + + public func resolveValue(_ resolver: ExpressionResolver) -> [String: Any]? { + resolver.resolveDict(value) + } init( name: String, - value: [String: Any] + value: Expression<[String: Any]> ) { self.name = name self.value = value @@ -37,7 +41,7 @@ extension DictVariable: Serializable { var result: [String: ValidSerializationValue] = [:] result["type"] = Self.type result["name"] = name - result["value"] = value + result["value"] = value.toValidSerializationValue() return result } } diff --git a/client/ios/DivKit/generated_sources/DictVariableTemplate.swift b/client/ios/DivKit/generated_sources/DictVariableTemplate.swift index 0c1607aa4..2ce95db38 100644 --- a/client/ios/DivKit/generated_sources/DictVariableTemplate.swift +++ b/client/ios/DivKit/generated_sources/DictVariableTemplate.swift @@ -8,20 +8,20 @@ public final class DictVariableTemplate: TemplateValue, @unchecked Sendable { public static let type: String = "dict" public let parent: String? public let name: Field? - public let value: Field<[String: Any]>? + public let value: Field>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( parent: dictionary["type"] as? String, name: dictionary.getOptionalField("name"), - value: dictionary.getOptionalField("value") + value: dictionary.getOptionalExpressionField("value") ) } init( parent: String?, name: Field? = nil, - value: Field<[String: Any]>? = nil + value: Field>? = nil ) { self.parent = parent self.name = name @@ -59,7 +59,7 @@ public final class DictVariableTemplate: TemplateValue, @unchecked Sendable { return resolveOnlyLinks(context: context, parent: parent) } var nameValue: DeserializationResult = { parent?.name?.value() ?? .noValue }() - var valueValue: DeserializationResult<[String: Any]> = { parent?.value?.value() ?? .noValue }() + var valueValue: DeserializationResult> = { parent?.value?.value() ?? .noValue }() _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable diff --git a/client/ios/DivKit/generated_sources/IntegerVariable.swift b/client/ios/DivKit/generated_sources/IntegerVariable.swift index dbf63800c..0cc33ec0b 100644 --- a/client/ios/DivKit/generated_sources/IntegerVariable.swift +++ b/client/ios/DivKit/generated_sources/IntegerVariable.swift @@ -7,11 +7,15 @@ import VGSL public final class IntegerVariable: Sendable { public static let type: String = "integer" public let name: String - public let value: Int + public let value: Expression + + public func resolveValue(_ resolver: ExpressionResolver) -> Int? { + resolver.resolveNumeric(value) + } init( name: String, - value: Int + value: Expression ) { self.name = name self.value = value @@ -37,7 +41,7 @@ extension IntegerVariable: Serializable { var result: [String: ValidSerializationValue] = [:] result["type"] = Self.type result["name"] = name - result["value"] = value + result["value"] = value.toValidSerializationValue() return result } } diff --git a/client/ios/DivKit/generated_sources/IntegerVariableTemplate.swift b/client/ios/DivKit/generated_sources/IntegerVariableTemplate.swift index 508f8821d..f95674fdd 100644 --- a/client/ios/DivKit/generated_sources/IntegerVariableTemplate.swift +++ b/client/ios/DivKit/generated_sources/IntegerVariableTemplate.swift @@ -8,20 +8,20 @@ public final class IntegerVariableTemplate: TemplateValue, Sendable { public static let type: String = "integer" public let parent: String? public let name: Field? - public let value: Field? + public let value: Field>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( parent: dictionary["type"] as? String, name: dictionary.getOptionalField("name"), - value: dictionary.getOptionalField("value") + value: dictionary.getOptionalExpressionField("value") ) } init( parent: String?, name: Field? = nil, - value: Field? = nil + value: Field>? = nil ) { self.parent = parent self.name = name @@ -59,7 +59,7 @@ public final class IntegerVariableTemplate: TemplateValue, Sendable { return resolveOnlyLinks(context: context, parent: parent) } var nameValue: DeserializationResult = { parent?.name?.value() ?? .noValue }() - var valueValue: DeserializationResult = { parent?.value?.value() ?? .noValue }() + var valueValue: DeserializationResult> = { parent?.value?.value() ?? .noValue }() _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable diff --git a/client/ios/DivKit/generated_sources/NumberVariable.swift b/client/ios/DivKit/generated_sources/NumberVariable.swift index 495d33b54..d0f9dae76 100644 --- a/client/ios/DivKit/generated_sources/NumberVariable.swift +++ b/client/ios/DivKit/generated_sources/NumberVariable.swift @@ -7,11 +7,15 @@ import VGSL public final class NumberVariable: Sendable { public static let type: String = "number" public let name: String - public let value: Double + public let value: Expression + + public func resolveValue(_ resolver: ExpressionResolver) -> Double? { + resolver.resolveNumeric(value) + } init( name: String, - value: Double + value: Expression ) { self.name = name self.value = value @@ -37,7 +41,7 @@ extension NumberVariable: Serializable { var result: [String: ValidSerializationValue] = [:] result["type"] = Self.type result["name"] = name - result["value"] = value + result["value"] = value.toValidSerializationValue() return result } } diff --git a/client/ios/DivKit/generated_sources/NumberVariableTemplate.swift b/client/ios/DivKit/generated_sources/NumberVariableTemplate.swift index e5933235f..92a78d100 100644 --- a/client/ios/DivKit/generated_sources/NumberVariableTemplate.swift +++ b/client/ios/DivKit/generated_sources/NumberVariableTemplate.swift @@ -8,20 +8,20 @@ public final class NumberVariableTemplate: TemplateValue, Sendable { public static let type: String = "number" public let parent: String? public let name: Field? - public let value: Field? + public let value: Field>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( parent: dictionary["type"] as? String, name: dictionary.getOptionalField("name"), - value: dictionary.getOptionalField("value") + value: dictionary.getOptionalExpressionField("value") ) } init( parent: String?, name: Field? = nil, - value: Field? = nil + value: Field>? = nil ) { self.parent = parent self.name = name @@ -59,7 +59,7 @@ public final class NumberVariableTemplate: TemplateValue, Sendable { return resolveOnlyLinks(context: context, parent: parent) } var nameValue: DeserializationResult = { parent?.name?.value() ?? .noValue }() - var valueValue: DeserializationResult = { parent?.value?.value() ?? .noValue }() + var valueValue: DeserializationResult> = { parent?.value?.value() ?? .noValue }() _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable diff --git a/client/ios/DivKit/generated_sources/StringVariable.swift b/client/ios/DivKit/generated_sources/StringVariable.swift index 89ba2df69..4c94b490a 100644 --- a/client/ios/DivKit/generated_sources/StringVariable.swift +++ b/client/ios/DivKit/generated_sources/StringVariable.swift @@ -7,11 +7,15 @@ import VGSL public final class StringVariable: Sendable { public static let type: String = "string" public let name: String - public let value: String + public let value: Expression + + public func resolveValue(_ resolver: ExpressionResolver) -> String? { + resolver.resolveString(value) + } init( name: String, - value: String + value: Expression ) { self.name = name self.value = value @@ -37,7 +41,7 @@ extension StringVariable: Serializable { var result: [String: ValidSerializationValue] = [:] result["type"] = Self.type result["name"] = name - result["value"] = value + result["value"] = value.toValidSerializationValue() return result } } diff --git a/client/ios/DivKit/generated_sources/StringVariableTemplate.swift b/client/ios/DivKit/generated_sources/StringVariableTemplate.swift index c87ac9e81..e3b524477 100644 --- a/client/ios/DivKit/generated_sources/StringVariableTemplate.swift +++ b/client/ios/DivKit/generated_sources/StringVariableTemplate.swift @@ -8,20 +8,20 @@ public final class StringVariableTemplate: TemplateValue, Sendable { public static let type: String = "string" public let parent: String? public let name: Field? - public let value: Field? + public let value: Field>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( parent: dictionary["type"] as? String, name: dictionary.getOptionalField("name"), - value: dictionary.getOptionalField("value") + value: dictionary.getOptionalExpressionField("value") ) } init( parent: String?, name: Field? = nil, - value: Field? = nil + value: Field>? = nil ) { self.parent = parent self.name = name @@ -59,7 +59,7 @@ public final class StringVariableTemplate: TemplateValue, Sendable { return resolveOnlyLinks(context: context, parent: parent) } var nameValue: DeserializationResult = { parent?.name?.value() ?? .noValue }() - var valueValue: DeserializationResult = { parent?.value?.value() ?? .noValue }() + var valueValue: DeserializationResult> = { parent?.value?.value() ?? .noValue }() _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable diff --git a/client/ios/DivKit/generated_sources/UrlVariable.swift b/client/ios/DivKit/generated_sources/UrlVariable.swift index 5f186ed85..a08a29483 100644 --- a/client/ios/DivKit/generated_sources/UrlVariable.swift +++ b/client/ios/DivKit/generated_sources/UrlVariable.swift @@ -7,11 +7,15 @@ import VGSL public final class UrlVariable: Sendable { public static let type: String = "url" public let name: String - public let value: URL + public let value: Expression + + public func resolveValue(_ resolver: ExpressionResolver) -> URL? { + resolver.resolveUrl(value) + } init( name: String, - value: URL + value: Expression ) { self.name = name self.value = value @@ -37,7 +41,7 @@ extension UrlVariable: Serializable { var result: [String: ValidSerializationValue] = [:] result["type"] = Self.type result["name"] = name - result["value"] = value.absoluteString + result["value"] = value.toValidSerializationValue() return result } } diff --git a/client/ios/DivKit/generated_sources/UrlVariableTemplate.swift b/client/ios/DivKit/generated_sources/UrlVariableTemplate.swift index a0ba9d006..f755deb82 100644 --- a/client/ios/DivKit/generated_sources/UrlVariableTemplate.swift +++ b/client/ios/DivKit/generated_sources/UrlVariableTemplate.swift @@ -8,20 +8,20 @@ public final class UrlVariableTemplate: TemplateValue, Sendable { public static let type: String = "url" public let parent: String? public let name: Field? - public let value: Field? + public let value: Field>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( parent: dictionary["type"] as? String, name: dictionary.getOptionalField("name"), - value: dictionary.getOptionalField("value", transform: URL.init(stringToEncode:)) + value: dictionary.getOptionalExpressionField("value", transform: URL.init(stringToEncode:)) ) } init( parent: String?, name: Field? = nil, - value: Field? = nil + value: Field>? = nil ) { self.parent = parent self.name = name @@ -59,7 +59,7 @@ public final class UrlVariableTemplate: TemplateValue, Sendable { return resolveOnlyLinks(context: context, parent: parent) } var nameValue: DeserializationResult = { parent?.name?.value() ?? .noValue }() - var valueValue: DeserializationResult = { parent?.value?.value() ?? .noValue }() + var valueValue: DeserializationResult> = { parent?.value?.value() ?? .noValue }() _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable diff --git a/client/ios/DivKitTests/DivSerializationTests.swift b/client/ios/DivKitTests/DivSerializationTests.swift index e177109de..21d4a441a 100644 --- a/client/ios/DivKitTests/DivSerializationTests.swift +++ b/client/ios/DivKitTests/DivSerializationTests.swift @@ -60,7 +60,7 @@ final class DivSerializationTests: XCTestCase { func test_Serialize_DoubleValue() { let variable = NumberVariable( name: "var", - value: 123.45 + value: .value(123.45) ) let dictionary = variable.toDictionary() XCTAssertEqual(123.45, dictionary["value"] as! Double) @@ -85,7 +85,7 @@ final class DivSerializationTests: XCTestCase { func test_Serialize_ArrayValue() { let variable = ArrayVariable( name: "var", - value: [1, "aba", 2.3] + value: .value([1, "aba", 2.3]) ) let dictionary = variable.toDictionary() XCTAssertEqual([1, "aba", 2.3], dictionary["value"] as! [AnyHashable]) diff --git a/client/ios/DivKitTests/Expressions/ExpressionTests.swift b/client/ios/DivKitTests/Expressions/ExpressionTests.swift index 7a71c07e0..fdc64d70b 100644 --- a/client/ios/DivKitTests/Expressions/ExpressionTests.swift +++ b/client/ios/DivKitTests/Expressions/ExpressionTests.swift @@ -85,7 +85,13 @@ private struct ExpressionTestCase: Decodable { let variable = try variablesContainer.decode(DivVariable.self) variables.append(variable) } - self.variables = variables.extractDivVariableValues() + self.variables = variables.extractDivVariableValues( + ExpressionResolver( + functionsProvider: FunctionsProvider(persistentValuesStorage: DivPersistentValuesStorage()), + variableValueProvider: { _ in nil }, + errorTracker: { XCTFail($0.description) } + ) + ) } var description: String { diff --git a/client/ios/DivKitTests/IntegrationTests.swift b/client/ios/DivKitTests/IntegrationTests.swift index 53697e012..a6335f376 100644 --- a/client/ios/DivKitTests/IntegrationTests.swift +++ b/client/ios/DivKitTests/IntegrationTests.swift @@ -209,7 +209,7 @@ extension DivVariablesStorage { case .dict: .dict([:]) } - append(variables: [name: defaultValue], for: path.cardId) + append(variables: [name: defaultValue], for: path.cardId, replaceExisting: false) } } diff --git a/client/ios/DivKitTests/Utils/DivBuilders.swift b/client/ios/DivKitTests/Utils/DivBuilders.swift index bc540cc0a..99ae15339 100644 --- a/client/ios/DivKitTests/Utils/DivBuilders.swift +++ b/client/ios/DivKitTests/Utils/DivBuilders.swift @@ -543,7 +543,7 @@ func point(x: Double, y: Double) -> DivPoint { } func variable(_ name: String, _ value: String) -> DivVariable { - .stringVariable(StringVariable(name: name, value: value)) + .stringVariable(StringVariable(name: name, value: .value(value))) } func solidBackground(_ color: RGBAColor) -> DivBackground { diff --git a/test_data/expression_test_data/variables_values.json b/test_data/expression_test_data/variables_values.json index 250d71a63..d2b6dbf5d 100644 --- a/test_data/expression_test_data/variables_values.json +++ b/test_data/expression_test_data/variables_values.json @@ -180,6 +180,142 @@ "flutter" ] }, + { + "expression": "@{integer_var_with_expression}", + "expected": { + "type": "integer", + "value": 123 + }, + "variables": [ + { + "name": "integer_var_with_expression", + "type": "integer", + "value": "@{123}" + } + ], + "platforms": [ + "ios" + ] + }, + { + "expression": "@{number_var_with_expression}", + "expected": { + "type": "number", + "value": 123.0 + }, + "variables": [ + { + "name": "number_var_with_expression", + "type": "number", + "value": "@{123.0}" + } + ], + "platforms": [ + "ios" + ] + }, + { + "expression": "@{string_var_with_expression}", + "expected": { + "type": "string", + "value": "test string" + }, + "variables": [ + { + "name": "string_var_with_expression", + "type": "string", + "value": "@{'test string'}" + } + ], + "platforms": [ + "ios" + ] + }, + { + "expression": "@{boolean_var_with_expression}", + "expected": { + "type": "boolean", + "value": false + }, + "variables": [ + { + "name": "boolean_var_with_expression", + "type": "boolean", + "value": "@{true && false}" + } + ], + "platforms": [ + "ios" + ] + }, + { + "expression": "@{color_var_with_expression}", + "expected": { + "type": "color", + "value": "#FF0000" + }, + "variables": [ + { + "name": "color_var_with_expression", + "type": "color", + "value": "@{'#FF0000'}" + } + ], + "platforms": [ + "ios" + ] + }, + { + "expression": "@{url_var_with_expression}", + "expected": { + "type": "url", + "value": "https://ya.ru" + }, + "variables": [ + { + "name": "url_var_with_expression", + "type": "url", + "value": "@{'https://ya.ru'}" + } + ], + "platforms": [ + "ios" + ] + }, + { + "expression": "@{var_from_func}", + "expected": { + "type": "integer", + "value": 5 + }, + "variables": [ + { + "name": "var_from_func", + "type": "integer", + "value": "@{len('Hello')}" + } + ], + "platforms": [ + "ios" + ] + }, + { + "expression": "@{circular_ref}", + "expected": { + "type": "error", + "value": "Variable 'circular_ref' is missing." + }, + "variables": [ + { + "name": "circular_ref", + "type": "integer", + "value": "@{circular_ref}" + } + ], + "platforms": [ + "ios" + ] + }, { "expression": "@{undeclred_var}", "expected": { diff --git a/test_data/integration_test_data/decl_expressions_cycle.json b/test_data/integration_test_data/decl_expressions_cycle.json new file mode 100644 index 000000000..491ff8603 --- /dev/null +++ b/test_data/integration_test_data/decl_expressions_cycle.json @@ -0,0 +1,42 @@ +{ + "description": "Decl variable with expression, using previously declared variable.", + "div_data": { + "card": { + "log_id": "decl_expressions", + "variables": [ + { + "name": "val", + "type": "integer", + "value": "@{val2 + 2}" + }, + { + "name": "val2", + "type": "integer", + "value": "@{val + 1}" + } + ], + "states": [ + { + "state_id": 0, + "div": { + "type": "text", + "text": "text" + } + } + ] + } + }, + "cases": [ + { + "expected": [ + { + "type": "error", + "value": "Variable 'val' is missing. Expression: @{val + 1}" + } + ], + "platforms": [ + "ios" + ] + } + ] +} diff --git a/test_data/integration_test_data/decl_expressions_item_builder.json b/test_data/integration_test_data/decl_expressions_item_builder.json index 252149f39..08852df92 100644 --- a/test_data/integration_test_data/decl_expressions_item_builder.json +++ b/test_data/integration_test_data/decl_expressions_item_builder.json @@ -95,6 +95,7 @@ } ], "platforms": [ + "ios", "web" ] } diff --git a/test_data/integration_test_data/decl_expressions_item_builder_override.json b/test_data/integration_test_data/decl_expressions_item_builder_override.json index 429b663a4..141a5e050 100644 --- a/test_data/integration_test_data/decl_expressions_item_builder_override.json +++ b/test_data/integration_test_data/decl_expressions_item_builder_override.json @@ -95,6 +95,7 @@ } ], "platforms": [ + "ios", "web" ] } diff --git a/test_data/integration_test_data/decl_extressions_nesting.json b/test_data/integration_test_data/decl_expressions_nesting.json similarity index 99% rename from test_data/integration_test_data/decl_extressions_nesting.json rename to test_data/integration_test_data/decl_expressions_nesting.json index e0136d988..900a25cdf 100644 --- a/test_data/integration_test_data/decl_extressions_nesting.json +++ b/test_data/integration_test_data/decl_expressions_nesting.json @@ -69,6 +69,7 @@ ], "platforms": [ "android", + "ios", "web" ] } diff --git a/test_data/integration_test_data/decl_extressions_simple.json b/test_data/integration_test_data/decl_expressions_simple.json similarity index 97% rename from test_data/integration_test_data/decl_extressions_simple.json rename to test_data/integration_test_data/decl_expressions_simple.json index 3cd81d35c..a46f07dee 100644 --- a/test_data/integration_test_data/decl_extressions_simple.json +++ b/test_data/integration_test_data/decl_expressions_simple.json @@ -35,6 +35,7 @@ ], "platforms": [ "android", + "ios", "web" ] } diff --git a/test_data/integration_test_data/decl_extressions_transitive.json b/test_data/integration_test_data/decl_expressions_transitive.json similarity index 98% rename from test_data/integration_test_data/decl_extressions_transitive.json rename to test_data/integration_test_data/decl_expressions_transitive.json index f83500af9..0967c1f34 100644 --- a/test_data/integration_test_data/decl_extressions_transitive.json +++ b/test_data/integration_test_data/decl_expressions_transitive.json @@ -40,6 +40,7 @@ ], "platforms": [ "android", + "ios", "web" ] }