Skip to content

Commit

Permalink
[J2KT] Move handling of imports out of Renderer to Environment, and n…
Browse files Browse the repository at this point in the history
…on-ObjC related code from ObjCNameRenderer to CompilationUnitRenderer.

PiperOrigin-RevId: 586677981
  • Loading branch information
Googler authored and copybara-github committed Nov 30, 2023
1 parent 4b0e182 commit 1c5037d
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ private fun fileCommentSource(compilationUnit: CompilationUnit) =
private val Renderer.fileAnnotationsSource: Source
get() = newLineSeparated(fileOptInAnnotationSource, suppressFileAnnotationSource)

private fun Renderer.fileOptInAnnotationSource(features: List<Source>): Source =
fileAnnotation(topLevelQualifiedNameSource("kotlin.OptIn"), features)

internal val Renderer.fileOptInAnnotationSource: Source
get() =
environment.importedOptInQualifiedNamesSet
.takeIf { it.isNotEmpty() }
?.map { KotlinSource.classLiteral(topLevelQualifiedNameSource(it)) }
?.let { fileOptInAnnotationSource(it) }
.orEmpty()

/** Returns source with file suppress annotation. */
private val Renderer.suppressFileAnnotationSource: Source
get() =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,20 @@ import com.google.j2cl.transpiler.backend.kotlin.ast.Import
*
* @property nameToIdentifierMap a map from named node to rendered identifier string
* @property identifierSet a set of used identifier strings, which potentially shadow imports
* @property importedSimpleNameToQualifiedNameMap a mutable map from simple name string to qualified
* name string of types to be imported, filled-in during code generation
* @property importedOptInQualifiedNames a mutable set with imported qualified names for [@OptIn]
* annotation, filled-in during code generation
* @property importedSimpleNameToQualifiedNameMutableMap a mutable map from simple name string to
* qualified name string of types to be imported, filled-in during code generation
* @property importedOptInQualifiedNamesMutableSet a mutable set with imported qualified names for
* [@OptIn] annotation, filled-in during code generation
* @property topLevelQualifiedNamesSet top-level qualified names, which will be rendered as simple
* name without import
*/
internal data class Environment(
private val nameToIdentifierMap: Map<HasName, String> = emptyMap(),
private val identifierSet: Set<String> = emptySet(),
val importedSimpleNameToQualifiedNameMap: MutableMap<String, String> = mutableMapOf(),
val importedOptInQualifiedNames: MutableSet<String> = mutableSetOf()
private val importedSimpleNameToQualifiedNameMutableMap: MutableMap<String, String> =
mutableMapOf(),
private val importedOptInQualifiedNamesMutableSet: MutableSet<String> = mutableSetOf(),
private val topLevelQualifiedNamesSet: Set<String> = setOf()
) {
/** Returns identifier for the given named node. */
fun identifier(hasName: HasName): String =
Expand All @@ -41,15 +45,57 @@ internal data class Environment(
/** Returns whether the given identifier is used. */
fun containsIdentifier(identifier: String): Boolean = identifierSet.contains(identifier)

/** A list of collected imports. */
internal val imports: List<Import>
/** A set of collected imports. */
val importsSet: Set<Import>
get() =
importedSimpleNameToQualifiedNameMap.entries.map { (simpleName, qualifiedName) ->
Import(
qualifiedName.qualifiedNameComponents(),
simpleName
.takeUnless { it == qualifiedName.qualifiedNameToSimpleName() }
?.let { Import.Suffix.WithAlias(it) }
)
importedSimpleNameToQualifiedNameMutableMap.entries
.map { (simpleName, qualifiedName) ->
Import(
qualifiedName.qualifiedNameComponents(),
simpleName
.takeUnless { it == qualifiedName.qualifiedNameToSimpleName() }
?.let { Import.Suffix.WithAlias(it) }
)
}
.toSet()

/** A set of collected opt-in qualified names. */
val importedOptInQualifiedNamesSet: Set<String>
get() = importedOptInQualifiedNamesMutableSet.toSet()

/** Converts the given qualified name to a simple name, aliasing if necessary. */
fun qualifiedToSimpleName(qualifiedName: String): String =
qualifiedToNonAliasedSimpleName(qualifiedName) ?: qualifiedToAliasedSimpleName(qualifiedName)

/** Converts the given qualified name to non-aliased simple name, or null if alias is required. */
fun qualifiedToNonAliasedSimpleName(qualifiedName: String): String? {
val simpleName = qualifiedName.qualifiedNameToSimpleName()
if (topLevelQualifiedNamesSet.contains(qualifiedName)) {
return simpleName
}
val importMap = importedSimpleNameToQualifiedNameMutableMap
val importedQualifiedName = importMap[simpleName]
if (importedQualifiedName == null) {
if (topLevelQualifiedNamesSet.any { it.qualifiedNameToSimpleName() == simpleName }) {
return null
}
importMap[simpleName] = qualifiedName
return simpleName
}
if (importedQualifiedName == qualifiedName) {
return simpleName
}
return null
}

/** Adds the given opt-in qualified name. */
fun addOptInQualifiedName(optInQualifiedName: String) {
importedOptInQualifiedNamesMutableSet.add(optInQualifiedName)
}

private fun qualifiedToAliasedSimpleName(qualifiedName: String): String {
return qualifiedName.qualifiedNameToAlias().also {
importedSimpleNameToQualifiedNameMutableMap[it] = qualifiedName
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.google.j2cl.transpiler.ast.HasName
import com.google.j2cl.transpiler.backend.kotlin.ast.Keywords
import com.google.j2cl.transpiler.backend.kotlin.common.inBackTicks
import com.google.j2cl.transpiler.backend.kotlin.common.letIf
import com.google.j2cl.transpiler.backend.kotlin.common.orIfNull
import com.google.j2cl.transpiler.backend.kotlin.source.Source
import com.google.j2cl.transpiler.backend.kotlin.source.Source.Companion.dotSeparated
import com.google.j2cl.transpiler.backend.kotlin.source.Source.Companion.source
Expand All @@ -40,50 +41,20 @@ internal fun Renderer.topLevelQualifiedNameSource(
qualifiedName: String,
optInQualifiedName: String? = null
): Source =
qualifiedToNonAliasedSimpleName(qualifiedName)
.let { simpleName ->
if (simpleName != null) {
identifierSource(simpleName)
} else {
qualifiedIdentifierSource(qualifiedName)
}
qualifiedName.qualifiedNameToSimpleName().let { simpleName ->
if (localNames.contains(simpleName) || environment.containsIdentifier(simpleName)) {
qualifiedIdentifierSource(qualifiedName)
} else {
environment
.qualifiedToNonAliasedSimpleName(qualifiedName)
?.let { identifierSource(it) }
.orIfNull { qualifiedIdentifierSource(qualifiedName) }
.also { optInQualifiedName?.let { environment.addOptInQualifiedName(it) } }
}
.also { optInQualifiedName?.let { environment.importedOptInQualifiedNames.add(it) } }

internal fun Renderer.extensionMemberQualifiedNameSource(qualifiedName: String): Source =
identifierSource(qualifiedToSimpleName(qualifiedName))

internal fun Renderer.qualifiedToSimpleName(qualifiedName: String): String =
qualifiedToNonAliasedSimpleName(qualifiedName) ?: qualifiedToAliasedSimpleName(qualifiedName)

internal fun Renderer.qualifiedToNonAliasedSimpleName(qualifiedName: String): String? {
val simpleName = qualifiedName.qualifiedNameToSimpleName()
if (localNames.contains(simpleName) || environment.containsIdentifier(simpleName)) {
return null
}
if (topLevelQualifiedNames.contains(qualifiedName)) {
return simpleName
}
val importMap = environment.importedSimpleNameToQualifiedNameMap
val importedQualifiedName = importMap[simpleName]
if (importedQualifiedName == null) {
if (topLevelQualifiedNames.any { it.qualifiedNameToSimpleName() == simpleName }) {
return null
}
importMap[simpleName] = qualifiedName
return simpleName
}
if (importedQualifiedName == qualifiedName) {
return simpleName
}
return null
}

internal fun Renderer.qualifiedToAliasedSimpleName(qualifiedName: String): String {
return qualifiedName.qualifiedNameToAlias().also {
environment.importedSimpleNameToQualifiedNameMap[it] = qualifiedName
}
}
internal fun Renderer.extensionMemberQualifiedNameSource(qualifiedName: String): Source =
identifierSource(environment.qualifiedToSimpleName(qualifiedName))

internal fun qualifiedIdentifierSource(identifier: String): Source =
dotSeparated(identifier.qualifiedNameComponents().map(::identifierSource))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ private val defaultImports: Set<Import>

/** A list of imports to render. */
private val Renderer.imports: List<Import>
get() = defaultImports.plus(environment.imports).sortedWith(lexicographicalOrder())
get() = defaultImports.plus(environment.importsSet).sortedWith(lexicographicalOrder())

/** Source for this import. */
private val Import.source: Source
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ class KotlinGeneratorStage(private val output: OutputUtils.Output, private val p
val environment =
Environment(
nameToIdentifierMap = nameToIdentifierMap,
identifierSet = nameToIdentifierMap.values.toSet()
identifierSet = nameToIdentifierMap.values.toSet(),
topLevelQualifiedNamesSet = compilationUnit.topLevelQualifiedNamesSet
)

val renderer =
Renderer(environment, topLevelQualifiedNames = compilationUnit.topLevelQualifiedNamesSet)
val renderer = Renderer(environment)

// Render types, collecting qualified names to import
val typesSource = renderer.typesSource(compilationUnit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ import com.google.j2cl.transpiler.ast.Variable
import com.google.j2cl.transpiler.ast.Visibility
import com.google.j2cl.transpiler.backend.kotlin.KotlinSource.annotation
import com.google.j2cl.transpiler.backend.kotlin.KotlinSource.assignment
import com.google.j2cl.transpiler.backend.kotlin.KotlinSource.classLiteral
import com.google.j2cl.transpiler.backend.kotlin.KotlinSource.fileAnnotation
import com.google.j2cl.transpiler.backend.kotlin.KotlinSource.literal
import com.google.j2cl.transpiler.backend.kotlin.ast.CompanionDeclaration
import com.google.j2cl.transpiler.backend.kotlin.ast.CompanionObject
Expand All @@ -44,17 +42,6 @@ import com.google.j2cl.transpiler.backend.kotlin.source.Source
import com.google.j2cl.transpiler.backend.kotlin.source.Source.Companion.source
import com.google.j2cl.transpiler.backend.kotlin.source.orEmpty

private fun Renderer.fileOptInAnnotationSource(features: List<Source>): Source =
fileAnnotation(topLevelQualifiedNameSource("kotlin.OptIn"), features)

internal val Renderer.fileOptInAnnotationSource: Source
get() =
environment.importedOptInQualifiedNames
.takeIf { it.isNotEmpty() }
?.map { classLiteral(topLevelQualifiedNameSource(it)) }
?.let { fileOptInAnnotationSource(it) }
.orEmpty()

internal fun Renderer.objCNameAnnotationSource(name: String, exact: Boolean? = null): Source =
annotation(
topLevelQualifiedNameSource(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,5 @@ internal data class Renderer(
val currentType: Type? = null,
// TODO(b/252138814): Remove when KT-54349 is fixed
val renderThisReferenceWithLabel: Boolean = false,
val localNames: Set<String> = setOf(),
val topLevelQualifiedNames: Set<String> = setOf()
val localNames: Set<String> = setOf()
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
*/
package com.google.j2cl.transpiler.backend.kotlin.common

fun <T> T.letIf(condition: Boolean, fn: (T) -> T): T = if (condition) fn(this) else this
/** Returns this object if [condition] is true, otherwise the result of applying [fn]. */
inline fun <T> T.letIf(condition: Boolean, fn: (T) -> T): T = if (condition) fn(this) else this

fun <T> T.runIf(condition: Boolean, fn: T.() -> T): T = if (condition) fn() else this
/** Returns this object if [condition] is true, otherwise the result of applying [fn]. */
inline fun <T> T.runIf(condition: Boolean, fn: T.() -> T): T = if (condition) fn() else this

/** Returns this object if it's not null, otherwise the result of [fn]. */
inline fun <T> T?.orIfNull(fn: () -> T): T = this ?: fn()

0 comments on commit 1c5037d

Please sign in to comment.