Skip to content

Commit

Permalink
Merge pull request #406 from uber/wbond/generated-code-compile-perf
Browse files Browse the repository at this point in the history
Significantly improve compile performance of generated code
  • Loading branch information
wbond authored Apr 14, 2022
2 parents ab3c5e4 + 8143133 commit aa1a16f
Show file tree
Hide file tree
Showing 16 changed files with 414 additions and 297 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,33 +56,53 @@ class DependencyProviderSerializerTask: AbstractTask<[SerializedProvider]> {

private func serialize(_ providers: [ProcessedDependencyProvider]) -> [SerializedProvider] {
var result = [SerializedProvider]()
let (baseClass, content) = serializedBase(for: providers.first!)
let (classNameSerializer, content) = serializedClass(for: providers.first!)
if providers.first?.isEmptyDependency == false {
result.append(SerializedProvider(content: content, registration: ""))
result.append(SerializedProvider(content: content, registration: "", attributes: ProviderAttributes()))
}
for provider in providers {
let content = provider.isEmptyDependency ? "" : serializedContent(for: provider, baseClassSerializer: baseClass)
let registration = DependencyProviderRegistrationSerializer(provider: provider).serialize()
result.append(SerializedProvider(content: content, registration: registration))
let paramsSerializer = DependencyProviderParamsSerializer(provider: provider)
let funcNameSerializer = DependencyProviderFuncNameSerializer(classNameSerializer: classNameSerializer, paramsSerializer: paramsSerializer)
let content = serializedContent(for: provider, classNameSerializer: classNameSerializer, paramsSerializer: paramsSerializer, funcNameSerializer: funcNameSerializer)
let registration = DependencyProviderRegistrationSerializer(provider: provider, factoryFuncNameSerializer: funcNameSerializer).serialize()
let attributes = calculateAttributes(for: provider, funcNameSerializer: funcNameSerializer)
result.append(SerializedProvider(content: content, registration: registration, attributes: attributes))
}
return result
}

private func serializedContent(for provider: ProcessedDependencyProvider, baseClassSerializer: Serializer) -> String {
let classNameSerializer = DependencyProviderClassNameSerializer(provider: provider)
let initBodySerializer = DependencyProviderInitBodySerializer(provider: provider)

let serializer = DependencyProviderSerializer(provider: provider, classNameSerializer: classNameSerializer, baseClassSerializer: baseClassSerializer, initBodySerializer: initBodySerializer)
return serializer.serialize()
private func serializedContent(for provider: ProcessedDependencyProvider, classNameSerializer: Serializer, paramsSerializer: Serializer, funcNameSerializer: Serializer) -> String {
if provider.isEmptyDependency {
return ""
}
return DependencyProviderFuncSerializer(provider: provider, funcNameSerializer: funcNameSerializer, classNameSerializer: classNameSerializer, paramsSerializer: paramsSerializer).serialize()
}

private func serializedBase(for provider: ProcessedDependencyProvider) -> (Serializer, String) {
let classNameSerializer = DependencyProviderBaseClassNameSerializer(provider: provider)
private func serializedClass(for provider: ProcessedDependencyProvider) -> (Serializer, String) {
let classNameSerializer = DependencyProviderClassNameSerializer(provider: provider)
let propertiesSerializer = PropertiesSerializer(processedProperties: provider.processedProperties)
let sourceComponentsSerializer = SourceComponentsSerializer(componentTypes: provider.levelMap.keys.sorted())
let initBodySerializer = DependencyProviderBaseInitSerializer(provider: provider)

let serializer = DependencyProviderBaseSerializer(provider: provider, classNameSerializer: classNameSerializer, propertiesSerializer: propertiesSerializer, sourceComponentsSerializer: sourceComponentsSerializer, initBodySerializer: initBodySerializer)
let serializer = DependencyProviderClassSerializer(provider: provider, classNameSerializer: classNameSerializer, propertiesSerializer: propertiesSerializer, sourceComponentsSerializer: sourceComponentsSerializer, initBodySerializer: initBodySerializer)
return (classNameSerializer, serializer.serialize())
}

private func calculateAttributes(for provider: ProcessedDependencyProvider, funcNameSerializer: Serializer) -> ProviderAttributes {
if provider.isEmptyDependency {
return ProviderAttributes()
}
var maxLevel: Int = 0
for (_, level) in provider.levelMap {
if level > maxLevel {
maxLevel = level
}
}
var attributes = ProviderAttributes()
if maxLevel > 0 {
attributes.maxLevel = maxLevel
}
attributes.factoryName = funcNameSerializer.serialize()
return attributes
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class PluginExtensionSerializerTask : AbstractTask<SerializedProvider> {
override func execute() -> SerializedProvider {
let content = PluginExtensionContentSerializer(component: component).serialize()
let registration = PluginExtensionRegistrationSerializer(component: component).serialize()
return SerializedProvider(content: content, registration: registration)
return SerializedProvider(content: content, registration: registration, attributes: ProviderAttributes())
}

// MARK: - Private
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,33 +57,53 @@ class PluginizedDependencyProviderSerializerTask: AbstractTask<[SerializedProvid

private func serialize(_ providers: [PluginizedProcessedDependencyProvider], baseCounter: Int) -> [SerializedProvider] {
var result = [SerializedProvider]()
let (baseClass, content) = serializedBase(for: providers.first!, counter: baseCounter)
let (classNameSerializer, content) = serializedClass(for: providers.first!, counter: baseCounter)
if providers.first?.data.isEmptyDependency == false {
result.append(SerializedProvider(content: content, registration: ""))
result.append(SerializedProvider(content: content, registration: "", attributes: ProviderAttributes()))
}
for (_, provider) in providers.enumerated() {
let content = provider.data.isEmptyDependency ? "" : serializedContent(for: provider, baseClassSerializer: baseClass)
let registration = DependencyProviderRegistrationSerializer(provider: provider.data).serialize()
result.append(SerializedProvider(content: content, registration: registration))
let paramsSerializer = DependencyProviderParamsSerializer(provider: provider.data)
let funcNameSerializer = DependencyProviderFuncNameSerializer(classNameSerializer: classNameSerializer, paramsSerializer: paramsSerializer)
let content = serializedContent(for: provider, classNameSerializer: classNameSerializer, paramsSerializer: paramsSerializer, funcNameSerializer: funcNameSerializer)
let registration = DependencyProviderRegistrationSerializer(provider: provider.data, factoryFuncNameSerializer: funcNameSerializer).serialize()
let attributes = calculateAttributes(for: provider.data, funcNameSerializer: funcNameSerializer)
result.append(SerializedProvider(content: content, registration: registration, attributes: attributes))
}
return result
}

private func serializedContent(for provider: PluginizedProcessedDependencyProvider, baseClassSerializer: Serializer) -> String {
let classNameSerializer = DependencyProviderClassNameSerializer(provider: provider.data)
let initBodySerializer = DependencyProviderInitBodySerializer(provider: provider.data)

let serializer = DependencyProviderSerializer(provider: provider.data, classNameSerializer: classNameSerializer, baseClassSerializer: baseClassSerializer, initBodySerializer: initBodySerializer)
return serializer.serialize()
private func serializedContent(for provider: PluginizedProcessedDependencyProvider, classNameSerializer: Serializer, paramsSerializer: Serializer, funcNameSerializer: Serializer) -> String {
if provider.data.isEmptyDependency {
return ""
}
return DependencyProviderFuncSerializer(provider: provider.data, funcNameSerializer: funcNameSerializer, classNameSerializer: classNameSerializer, paramsSerializer: paramsSerializer).serialize()
}

private func serializedBase(for provider: PluginizedProcessedDependencyProvider, counter: Int) -> (Serializer, String) {
let classNameSerializer = DependencyProviderBaseClassNameSerializer(provider: provider.data)
private func serializedClass(for provider: PluginizedProcessedDependencyProvider, counter: Int) -> (Serializer, String) {
let classNameSerializer = DependencyProviderClassNameSerializer(provider: provider.data)
let propertiesSerializer = PluginizedPropertiesSerializer(provider: provider)
let sourceComponentsSerializer = SourceComponentsSerializer(componentTypes: provider.data.levelMap.keys.sorted())
let initBodySerializer = DependencyProviderBaseInitSerializer(provider: provider.data)

let serializer = DependencyProviderBaseSerializer(provider: provider.data, classNameSerializer: classNameSerializer, propertiesSerializer: propertiesSerializer, sourceComponentsSerializer: sourceComponentsSerializer, initBodySerializer: initBodySerializer)
let serializer = DependencyProviderClassSerializer(provider: provider.data, classNameSerializer: classNameSerializer, propertiesSerializer: propertiesSerializer, sourceComponentsSerializer: sourceComponentsSerializer, initBodySerializer: initBodySerializer)
return (classNameSerializer, serializer.serialize())
}

private func calculateAttributes(for provider: ProcessedDependencyProvider, funcNameSerializer: Serializer) -> ProviderAttributes {
if provider.isEmptyDependency {
return ProviderAttributes()
}
var maxLevel: Int = 0
for (_, level) in provider.levelMap {
if level > maxLevel {
maxLevel = level
}
}
var attributes = ProviderAttributes()
if maxLevel > 0 {
attributes.maxLevel = maxLevel
}
attributes.factoryName = funcNameSerializer.serialize()
return attributes
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,35 @@

import Foundation

/// A serializer that produces the class name for the dependency provider.
class DependencyProviderClassNameSerializer: Serializer {
/// A serializer that produces the func name for the dependency provider.
class DependencyProviderFuncNameSerializer: Serializer {

/// Initializer.
///
/// - parameter provider: The provider to generate class name for.
init(provider: ProcessedDependencyProvider) {
self.provider = provider
/// - parameter classNameSerializer: The serializer for the class name.
/// - parameter paramsSerializer: The serializer for the parameters.
init(classNameSerializer: Serializer, paramsSerializer: Serializer) {
self.classNameSerializer = classNameSerializer
self.paramsSerializer = paramsSerializer
}

/// Serialize the data model and produce the class name code.
/// Serialize the data model and produce the func name code.
///
/// - returns: The class name code.
/// - returns: The func name code.
func serialize() -> String {
let pathId = String(provider.unprocessed.pathString.shortSHA256Value)
return "\(provider.unprocessed.dependency.name)\(pathId)Provider"
let classId = String(classNameSerializer.serialize().shortSHA256Value)
let paramsId = String(paramsSerializer.serialize().shortSHA256Value)
return "factory\(classId)\(paramsId)"
}

// MARK: - Private

private let provider: ProcessedDependencyProvider
private let classNameSerializer: Serializer
private let paramsSerializer: Serializer
}

/// A serializer that produces the class name for the dependency provider base class.
final class DependencyProviderBaseClassNameSerializer: Serializer {
final class DependencyProviderClassNameSerializer: Serializer {

/// Initializer.
///
Expand All @@ -55,7 +59,7 @@ final class DependencyProviderBaseClassNameSerializer: Serializer {
/// - returns: The class name code.
func serialize() -> String {
let pathId = String(provider.unprocessed.pathString.shortSHA256Value)
return "\(provider.unprocessed.dependency.name)\(pathId)BaseProvider"
return "\(provider.unprocessed.dependency.name)\(pathId)Provider"
}

// MARK: - Private
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import Foundation

/// A serializer that produces the source code for the entire dependency
/// provider.
final class DependencyProviderBaseSerializer: Serializer {
final class DependencyProviderClassSerializer: Serializer {

/// Initializer.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,6 @@

import Foundation

/// A serializer that produces the initializer body code for the dependency
/// provider.
final class DependencyProviderInitBodySerializer: Serializer {

/// Initializer.
///
/// - parameter provider: The provider to generate initializer body
/// source code for.
init(provider: ProcessedDependencyProvider) {
self.provider = provider
}

/// Serialize the data model and produce the initializer body code.
///
/// - returns: The initializer body source code.
func serialize() -> String {
return provider.levelMap
.sorted(by: { $0.key < $1.key })
.map { (componentType: String, level: Int) in
return "\(componentType.lowercasedFirstChar()): component\(String(repeating: ".parent", count: level)) as! \(componentType)"
}
.joined(separator: ", ")
}

// MARK: - Private

private let provider: ProcessedDependencyProvider
}

/// A serializer that produces the initializer body code for the dependency
/// provider.
class DependencyProviderBaseInitSerializer: Serializer {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// Copyright (c) 2018. Uber Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

/// A serializer that produces the params to pass to the dependency
/// provider.
final class DependencyProviderParamsSerializer: Serializer {

/// Initializer.
///
/// - parameter provider: The provider to generate params source code for.
init(provider: ProcessedDependencyProvider) {
self.provider = provider
}

/// Serialize the data model and produce the params code.
///
/// - returns: The params source code.
func serialize() -> String {
return provider.levelMap
.sorted(by: { $0.key < $1.key })
.map { (componentType: String, level: Int) in
return "\(componentType.lowercasedFirstChar()): parent\(level)(component) as! \(componentType)"
}
.joined(separator: ", ")
}

// MARK: - Private

private let provider: ProcessedDependencyProvider
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,25 @@ class DependencyProviderRegistrationSerializer: Serializer {
/// Initializer.
///
/// - parameter provider: The provider to generate registration code
/// - parameter factoryFuncNameSerializer: The serializer to generate the factory func name
/// for.
init(provider: ProcessedDependencyProvider) {
init(provider: ProcessedDependencyProvider, factoryFuncNameSerializer: Serializer) {
self.provider = provider
self.factoryFuncNameSerializer = factoryFuncNameSerializer
}

/// Serialize the data model and produce the registration source code.
///
/// - returns: The registration source code.
func serialize() -> String {
let providerName = provider.isEmptyDependency ? "EmptyDependencyProvider" : DependencyProviderClassNameSerializer(provider: provider).serialize()
let factoryName = provider.isEmptyDependency ? "factoryEmptyDependencyProvider" : factoryFuncNameSerializer.serialize()
return """
__DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: "\(provider.unprocessed.pathString)") { component in
return \(providerName)(component: component)
}\n
registerProviderFactory("\(provider.unprocessed.pathString)", \(factoryName))\n
"""
}

// MARK: - Private

private let provider: ProcessedDependencyProvider
private let factoryFuncNameSerializer: Serializer
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ import Foundation

/// A serializer that produces the source code for the dependency
/// provider. It's mostly empty as the core logic lives in the
/// superclass
class DependencyProviderSerializer: Serializer {
/// class.
class DependencyProviderFuncSerializer: Serializer {

init(provider: ProcessedDependencyProvider, classNameSerializer: Serializer, baseClassSerializer: Serializer, initBodySerializer: Serializer) {
self.classNameSerializer = classNameSerializer
self.baseClassSerializer = baseClassSerializer
self.initBodySerializer = initBodySerializer
init(provider: ProcessedDependencyProvider, funcNameSerializer: Serializer, classNameSerializer: Serializer, paramsSerializer: Serializer) {
self.provider = provider
self.funcNameSerializer = funcNameSerializer
self.classNameSerializer = classNameSerializer
self.paramsSerializer = paramsSerializer
}

/// Serialize the data model and produce the entire dependency provider
Expand All @@ -35,18 +35,16 @@ class DependencyProviderSerializer: Serializer {
func serialize() -> String {
return """
/// \(provider.unprocessed.pathString)
private class \(classNameSerializer.serialize()): \(baseClassSerializer.serialize()) {
init(component: NeedleFoundation.Scope) {
super.init(\(initBodySerializer.serialize()))
}
private func \(funcNameSerializer.serialize())(_ component: NeedleFoundation.Scope) -> AnyObject {
return \(classNameSerializer.serialize())(\(paramsSerializer.serialize()))
}\n
"""
}

// MARK: - Private

private let provider: ProcessedDependencyProvider
private let funcNameSerializer: Serializer
private let classNameSerializer: Serializer
private let baseClassSerializer: Serializer
private let initBodySerializer: Serializer
private let paramsSerializer: Serializer
}
Loading

0 comments on commit aa1a16f

Please sign in to comment.