Files
divkit/client/ios/DivKit/Expressions/CustomFunctions/DivFunctionsStorage.swift
T
pkurchatov 0663870bf9 Swiftformat
commit_hash:52ce6e4a9f35a44ddef28565cec76ddaabdc2d08
2025-09-18 12:40:09 +03:00

138 lines
3.8 KiB
Swift

import LayoutKit
import VGSL
@_spi(Internal)
public final class DivFunctionsStorage {
let outerStorage: DivFunctionsStorage?
private var storages: [UIElementPath: DivFunctionsStorage] = [:]
private var functionNamesByCard: [DivCardID: Set<String>] = [:]
private var functionSignatures: Set<CustomFunction.Signature> = []
private var functionsByName: [String: [CustomFunction]] = [:]
private let reporter: DivReporter
private let lock = AllocatedUnfairLock()
init(
outerStorage: DivFunctionsStorage? = DivFunctionsStorage(outerStorage: nil),
reporter: DivReporter = DefaultDivReporter()
) {
self.outerStorage = outerStorage
self.reporter = reporter
}
func set(cardId: DivCardID, functions: [DivFunction]) {
reset(cardId: cardId)
setIfNeeded(path: cardId.path, functions: functions)
}
func setIfNeeded(path: UIElementPath, functions: [DivFunction]) {
lock.withLock {
if storages[path] != nil {
return
}
let nearestStorage = getNearestStorage(path.parent)
if functions.isEmpty {
storages[path] = nearestStorage
} else {
let storage = DivFunctionsStorage(outerStorage: nearestStorage)
let cardId = path.cardId
let expressionErrorTracker = reporter.asExpressionErrorTracker(cardId: cardId)
for function in functions {
functionNamesByCard[cardId, default: []].insert(function.name)
storage.addFunction(
function,
reporter: expressionErrorTracker
)
}
storages[path] = storage
}
}
}
func getStorage(
path: UIElementPath,
contains name: String
) -> DivFunctionsStorage? {
lock.withLock {
guard functionNamesByCard[path.cardId]?.contains(name) == true else { return nil }
var storage: DivFunctionsStorage? = getNearestStorage(path)
while storage != nil {
if let functions = storage?.getFunctions(with: name), !functions.isEmpty {
return storage
}
storage = storage?.outerStorage
}
return nil
}
}
func getFunctions(with name: String) -> [CustomFunction] {
lock.withLock {
functionsByName[name] ?? []
}
}
func reset() {
lock.withLock {
storages.removeAll()
functionNamesByCard.removeAll()
}
}
func reset(cardId: DivCardID) {
lock.withLock {
storages.keys
.filter { $0.root == cardId.rawValue }
.forEach { storages[$0] = nil }
functionNamesByCard[cardId] = nil
}
}
private func getNearestStorage(_ path: UIElementPath?) -> DivFunctionsStorage? {
var currentPath: UIElementPath? = path
while let path = currentPath {
if let storage = storages[path] {
return storage
}
currentPath = path.parent
}
return outerStorage
}
private func addFunction(_ function: DivFunction, reporter: ExpressionErrorTracker) {
let signature = CustomFunction.Signature(
name: function.name,
arguments: function.arguments.map(\.type)
)
if functionSignatures.contains(signature) {
reporter(
ExpressionError(
makeErrorMessage(
name: function.name,
arguments: function.arguments.map(\.type.rawValue)
)
)
)
} else {
let customFunction = CustomFunction(
name: function.name,
arguments: function.arguments.compactMap { arg in
CustomFunction.Argument(name: arg.name, type: arg.type)
},
body: function.body,
returnType: function.returnType.systemType
)
functionsByName[function.name, default: []].append(customFunction)
functionSignatures.insert(signature)
}
}
}
private func makeErrorMessage(name: String, arguments: [String]) -> String {
"Function \(name)(\(arguments.joined(separator: ", "))) declared multiple times."
}