Skip to content

Commit

Permalink
[J2KT] Improve import resolver to preserve mapping between simple-nam…
Browse files Browse the repository at this point in the history
…e and qualified-name, and use it for more precise import resolution.

PiperOrigin-RevId: 601248338
  • Loading branch information
Googler authored and copybara-github committed Jan 24, 2024
1 parent 7a95d61 commit 7ae61d9
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ internal data class Environment(
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 Down Expand Up @@ -70,15 +69,9 @@ internal data class Environment(
/** 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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ class KotlinGeneratorStage(private val output: OutputUtils.Output, private val p
Environment(
nameToIdentifierMap = nameToIdentifierMap,
identifierSet = nameToIdentifierMap.values.toSet(),
topLevelQualifiedNamesSet = compilationUnit.topLevelQualifiedNamesSet,
)

val nameRenderer = NameRenderer(environment)
val nameRenderer =
NameRenderer(environment).plusLocalTypeNameMap(compilationUnit.localTypeNames)

val compilationUnitRenderer = CompilationUnitRenderer(nameRenderer)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package com.google.j2cl.transpiler.backend.kotlin

import com.google.j2cl.transpiler.ast.DeclaredTypeDescriptor
import com.google.j2cl.transpiler.ast.HasName
import com.google.j2cl.transpiler.ast.Type
import com.google.j2cl.transpiler.ast.TypeDescriptor
import com.google.j2cl.transpiler.backend.kotlin.common.orIfNull
import com.google.j2cl.transpiler.backend.kotlin.source.Source
Expand All @@ -25,14 +26,22 @@ import com.google.j2cl.transpiler.backend.kotlin.source.Source
* Renderer of Kotlin names, with import resolution and alias generation.
*
* @property environment rendering environment
* @property localNames a set of local names which are potentially shadowing imports
* @property localTypeNameMap a map from local names to qualified names
* @property localFieldNames a set of local field names
*/
internal data class NameRenderer(
val environment: Environment,
val localNames: Set<String> = setOf(),
val localTypeNameMap: Map<String, String> = mapOf(),
val localFieldNames: Set<String> = setOf(),
) {
fun plusLocalNames(localNames: Collection<String>): NameRenderer =
copy(localNames = this.localNames + localNames)
fun plusLocalNames(type: Type): NameRenderer =
plusLocalTypeNameMap(type.localTypeNameMap).plusLocalFieldNames(type.localFieldNames)

fun plusLocalTypeNameMap(localNameMap: Map<String, String>): NameRenderer =
copy(localTypeNameMap = this.localTypeNameMap + localNameMap)

fun plusLocalFieldNames(localNames: Set<String>): NameRenderer =
copy(localFieldNames = this.localFieldNames + localNames)

/** Returns source containing name of the given node. */
fun nameSource(hasName: HasName) = identifierSource(environment.identifier(hasName))
Expand All @@ -43,14 +52,36 @@ internal data class NameRenderer(
* @param qualifiedName top-level qualified name
*/
fun topLevelQualifiedNameSource(qualifiedName: String): Source =
topLevelSimpleNameOrNull(qualifiedName)
?.let { identifierSource(it) }
.orIfNull { qualifiedIdentifierSource(qualifiedName) }

/**
* Returns simple name to use for the given top-level qualified name, or null if using simple name
* is not valid.
*
* @param qualifiedName top-level qualified name
* @return simple name or null
*/
private fun topLevelSimpleNameOrNull(qualifiedName: String): String? =
qualifiedName.qualifiedNameToSimpleName().let { simpleName ->
if (localNames.contains(simpleName) || environment.containsIdentifier(simpleName)) {
qualifiedIdentifierSource(qualifiedName)
} else {
environment
.qualifiedToNonAliasedSimpleName(qualifiedName)
?.let { identifierSource(it) }
.orIfNull { qualifiedIdentifierSource(qualifiedName) }
when {
// Simple name shadowed by a field name.
localFieldNames.contains(simpleName) -> null

// Simple name shadowed by name in the environment.
environment.containsIdentifier(simpleName) -> null

// Look for local name, and use it if it refers to the same qualified name.
// Otherwise, take local name from environment, which will resolve imports.
else ->
localTypeNameMap[simpleName].let { localQualifiedName ->
if (localQualifiedName != null) {
simpleName.takeIf { localQualifiedName == qualifiedName }
} else {
environment.qualifiedToNonAliasedSimpleName(qualifiedName)
}
}
}
}

Expand Down
25 changes: 21 additions & 4 deletions transpiler/java/com/google/j2cl/transpiler/backend/kotlin/Names.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,34 @@ import com.google.j2cl.transpiler.ast.TypeDeclaration
import com.google.j2cl.transpiler.ast.TypeDescriptor
import com.google.j2cl.transpiler.ast.Visibility

/** Map entry from simple name to qualified name. */
internal val TypeDeclaration.nameMapEntry: Pair<String, String>
get() = ktSimpleName to ktQualifiedName

/** A map of member names used in this type. */
internal val TypeDeclaration.memberTypeNameMap: Map<String, String>
get() = memberTypeDeclarations.mapNotNull { it.nameMapEntry }.let { mapOf(*it.toTypedArray()) }

/** A map of local member names used in this type. */
internal val TypeDeclaration.localTypeNameMap: Map<String, String>
get() = memberTypeNameMap.plus(nameMapEntry)

/** A map of local names used in this type. */
internal val Type.localTypeNames: Set<String>
get() = declaration.memberTypeDeclarations.mapNotNull { it.ktSimpleName }.toSet()
internal val Type.localTypeNameMap: Map<String, String>
get() = declaration.localTypeNameMap

/** A set of field names used in this type. */
internal val Type.localFieldNames: Set<String>
get() = fields.map { it.descriptor.ktName!! }.toSet()

/** A set of top-level qualified name strings in this compilation unit. */
internal val CompilationUnit.topLevelQualifiedNamesSet: Set<String>
get() = types.map { it.declaration }.filter { !it.isKtNative }.map { it.ktQualifiedName }.toSet()
internal val CompilationUnit.localTypeNames: Map<String, String>
get() =
types
.map { it.declaration }
.filter { !it.isKtNative }
.map { it.ktSimpleName to it.ktQualifiedName }
.let { mapOf(*it.toTypedArray()) }

/** Kotlin mangled name for this member descriptor. */
internal val MemberDescriptor.ktMangledName: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ import com.google.j2cl.transpiler.backend.kotlin.source.orEmpty
* @property nameRenderer underlying name renderer
*/
internal data class TypeRenderer(val nameRenderer: NameRenderer) {
private fun NameRenderer.plusLocalNames(type: Type): NameRenderer =
plusLocalNames(type.localTypeNames).plusLocalNames(type.localFieldNames)

private fun memberRenderer(type: Type): MemberRenderer =
MemberRenderer(nameRenderer.plusLocalNames(type), type)

Expand Down

0 comments on commit 7ae61d9

Please sign in to comment.