[JS IR BE] Initial export generation
This commit is contained in:
@@ -29,10 +29,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrDynamicTypeImpl
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
@@ -54,7 +51,7 @@ class JsIrBackendContext(
|
||||
|
||||
override var inVerbosePhase: Boolean = false
|
||||
|
||||
var externalPackageFragment = mutableMapOf<FqName, IrPackageFragment>()
|
||||
var externalPackageFragment = mutableMapOf<IrFileSymbol, IrFile>()
|
||||
lateinit var bodilessBuiltInsPackageFragment: IrPackageFragment
|
||||
|
||||
val externalNestedClasses = mutableListOf<IrClass>()
|
||||
|
||||
@@ -78,14 +78,6 @@ private val validateIrAfterLowering = makeCustomJsModulePhase(
|
||||
description = "Validate IR after lowering"
|
||||
)
|
||||
|
||||
private val moveBodilessDeclarationsToSeparatePlacePhase = makeCustomJsModulePhase(
|
||||
{ context, module ->
|
||||
moveBodilessDeclarationsToSeparatePlace(context, module)
|
||||
},
|
||||
name = "MoveBodilessDeclarationsToSeparatePlace",
|
||||
description = "Move `external` and `built-in` declarations into separate place to make the following lowerings do not care about them"
|
||||
)
|
||||
|
||||
private val expectDeclarationsRemovingPhase = makeJsModulePhase(
|
||||
::ExpectDeclarationsRemoveLowering,
|
||||
name = "ExpectDeclarationsRemoving",
|
||||
@@ -419,7 +411,6 @@ val jsPhases = namedIrModulePhase(
|
||||
primaryConstructorLoweringPhase then
|
||||
initializersLoweringPhase then
|
||||
// Common prefix ends
|
||||
moveBodilessDeclarationsToSeparatePlacePhase then
|
||||
enumClassLoweringPhase then
|
||||
enumUsageLoweringPhase then
|
||||
suspendFunctionsLoweringPhase then
|
||||
@@ -447,6 +438,5 @@ val jsPhases = namedIrModulePhase(
|
||||
objectDeclarationLoweringPhase then
|
||||
objectUsageLoweringPhase then
|
||||
callsLoweringPhase then
|
||||
staticMembersLoweringPhase then
|
||||
validateIrAfterLowering
|
||||
)
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
@@ -28,6 +29,11 @@ fun sortDependencies(dependencies: Collection<IrModuleFragment>): Collection<IrM
|
||||
}.reversed()
|
||||
}
|
||||
|
||||
class CompilerResult(
|
||||
val jsCode: String,
|
||||
val tsDefinitions: String? = null
|
||||
)
|
||||
|
||||
fun compile(
|
||||
project: Project,
|
||||
files: List<KtFile>,
|
||||
@@ -37,7 +43,7 @@ fun compile(
|
||||
friendDependencies: List<KotlinLibrary>,
|
||||
mainArguments: List<String>?,
|
||||
exportedDeclarations: Set<FqName> = emptySet()
|
||||
): String {
|
||||
): CompilerResult {
|
||||
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
|
||||
loadIr(project, files, configuration, allDependencies, friendDependencies)
|
||||
|
||||
@@ -71,9 +77,10 @@ fun compile(
|
||||
).generateUnboundSymbolsAsDependencies()
|
||||
moduleFragment.patchDeclarationParents()
|
||||
|
||||
moveBodilessDeclarationsToSeparatePlace(context, moduleFragment)
|
||||
|
||||
jsPhases.invokeToplevel(phaseConfig, context, moduleFragment)
|
||||
|
||||
val jsProgram =
|
||||
moduleFragment.accept(IrModuleToJsTransformer(context, mainFunction, mainArguments), null)
|
||||
return jsProgram.toString()
|
||||
val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments)
|
||||
return transformer.generateModule(moduleFragment)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
|
||||
sealed class ExportedDeclaration
|
||||
|
||||
data class ExportedModule(
|
||||
val name: String,
|
||||
val declarations: List<ExportedDeclaration>
|
||||
)
|
||||
|
||||
class ExportedNamespace(
|
||||
val name: String,
|
||||
val declarations: List<ExportedDeclaration>
|
||||
) : ExportedDeclaration()
|
||||
|
||||
class ExportedFunction(
|
||||
val name: String,
|
||||
val returnType: ExportedType,
|
||||
val parameters: List<ExportedParameter>,
|
||||
val typeParameters: List<String> = emptyList(),
|
||||
val isMember: Boolean = false,
|
||||
val isStatic: Boolean = false,
|
||||
val isAbstract: Boolean = false,
|
||||
val ir: IrSimpleFunction
|
||||
) : ExportedDeclaration()
|
||||
|
||||
class ExportedConstructor(
|
||||
val parameters: List<ExportedParameter>
|
||||
) : ExportedDeclaration()
|
||||
|
||||
class ExportedProperty(
|
||||
val name: String,
|
||||
val type: ExportedType,
|
||||
val mutable: Boolean,
|
||||
val isMember: Boolean = false,
|
||||
val isStatic: Boolean = false,
|
||||
val isAbstract: Boolean,
|
||||
val ir: IrProperty
|
||||
) : ExportedDeclaration()
|
||||
|
||||
|
||||
// TODO: Cover all cases with frontend and disable error declarations
|
||||
class ErrorDeclaration(val message: String) : ExportedDeclaration()
|
||||
|
||||
class ExportedClass(
|
||||
val name: String,
|
||||
val isInterface: Boolean = false,
|
||||
val isAbstract: Boolean = false,
|
||||
val superClass: ExportedType? = null,
|
||||
val superInterfaces: List<ExportedType> = emptyList(),
|
||||
val typeParameters: List<String>,
|
||||
val members: List<ExportedDeclaration>,
|
||||
val statics: List<ExportedDeclaration>,
|
||||
val ir: IrClass
|
||||
) : ExportedDeclaration()
|
||||
|
||||
class ExportedParameter(
|
||||
val name: String,
|
||||
val type: ExportedType
|
||||
)
|
||||
|
||||
sealed class ExportedType {
|
||||
sealed class Primitive(val typescript: kotlin.String) : ExportedType() {
|
||||
object Boolean : Primitive("boolean")
|
||||
object Number : Primitive("number")
|
||||
object ByteArray : Primitive("Int8Array")
|
||||
object ShortArray : Primitive("Int16Array")
|
||||
object IntArray : Primitive("Int32Array")
|
||||
object FloatArray : Primitive("Float32Array")
|
||||
object DoubleArray : Primitive("Float64Array")
|
||||
object String : Primitive("string")
|
||||
object Throwable : Primitive("Error")
|
||||
object Any : Primitive("any")
|
||||
object Unit : Primitive("void")
|
||||
object Nothing : Primitive("never")
|
||||
}
|
||||
|
||||
class Array(val elementType: ExportedType) : ExportedType()
|
||||
class Function(
|
||||
val parameterTypes: List<ExportedType>,
|
||||
val returnType: ExportedType
|
||||
) : ExportedType()
|
||||
|
||||
class ClassType(val name: String, val arguments: List<ExportedType>) : ExportedType()
|
||||
class TypeParameter(val name: String) : ExportedType()
|
||||
class Nullable(val baseType: ExportedType) : ExportedType()
|
||||
class ErrorType(val comment: String) : ExportedType()
|
||||
|
||||
fun withNullability(nullable: Boolean) =
|
||||
if (nullable) Nullable(this) else this
|
||||
}
|
||||
+408
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.isExpect
|
||||
import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.isJsExport
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
|
||||
private fun generateExport(file: IrPackageFragment): List<ExportedDeclaration> {
|
||||
val namespaceFqName = file.fqName
|
||||
val exports = file.declarations.flatMap { declaration -> listOfNotNull(exportDeclaration(declaration)) }
|
||||
return when {
|
||||
exports.isEmpty() -> emptyList()
|
||||
namespaceFqName.isRoot -> exports
|
||||
else -> listOf(ExportedNamespace(namespaceFqName.toString(), exports))
|
||||
}
|
||||
}
|
||||
|
||||
fun generateExport(module: IrModuleFragment): ExportedModule =
|
||||
ExportedModule(
|
||||
sanitizeName(context.configuration[CommonConfigurationKeys.MODULE_NAME]!!),
|
||||
(context.externalPackageFragment.values + module.files).flatMap {
|
||||
generateExport(it)
|
||||
}
|
||||
)
|
||||
|
||||
private fun getExportCandidate(declaration: IrDeclaration): IrDeclarationWithName? {
|
||||
// Only actual public declarations with name can be exported
|
||||
if (declaration !is IrDeclarationWithVisibility ||
|
||||
declaration !is IrDeclarationWithName ||
|
||||
declaration.visibility != Visibilities.PUBLIC ||
|
||||
declaration.isExpect
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Workaround to get property declarations instead of its lowered accessors.
|
||||
if (declaration is IrSimpleFunction) {
|
||||
val property = declaration.correspondingPropertySymbol?.owner
|
||||
if (property != null) {
|
||||
// Return property for getter accessors only to prevent
|
||||
// returning it twice (for getter and setter) in the same scope
|
||||
return if (property.getter == declaration)
|
||||
property
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun exportDeclaration(declaration: IrDeclaration): ExportedDeclaration? {
|
||||
val candidate = getExportCandidate(declaration) ?: return null
|
||||
if (!shouldDeclarationBeExported(candidate)) return null
|
||||
|
||||
return when (candidate) {
|
||||
is IrSimpleFunction -> exportFunction(candidate)
|
||||
is IrProperty -> exportProperty(candidate)
|
||||
is IrClass -> exportClass(candidate)
|
||||
is IrField -> null
|
||||
else -> error("Can't export declaration $candidate")
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportFunction(function: IrSimpleFunction): ExportedDeclaration? =
|
||||
when (val exportability = functionExportability(function)) {
|
||||
is Exportability.NotNeeded -> null
|
||||
is Exportability.Prohibited -> ErrorDeclaration(exportability.reason)
|
||||
is Exportability.Allowed -> {
|
||||
val parent = function.parent
|
||||
ExportedFunction(
|
||||
function.getExportedIdentifier(),
|
||||
returnType = exportType(function.returnType),
|
||||
parameters = (listOfNotNull(function.extensionReceiverParameter) + function.valueParameters).map { exportParameter(it) },
|
||||
typeParameters = function.typeParameters.map { it.name.identifier },
|
||||
isMember = parent is IrClass,
|
||||
isStatic = function.isStaticMethodOfClass,
|
||||
isAbstract = parent is IrClass && !parent.isInterface && function.modality == Modality.ABSTRACT,
|
||||
ir = function
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportConstructor(constructor: IrConstructor): ExportedDeclaration? {
|
||||
if (!constructor.isPrimary) return null
|
||||
val allValueParameters = listOfNotNull(constructor.extensionReceiverParameter) + constructor.valueParameters
|
||||
return ExportedConstructor(allValueParameters.map { exportParameter(it) })
|
||||
}
|
||||
|
||||
private fun exportParameter(parameter: IrValueParameter): ExportedParameter {
|
||||
// Parameter names do not matter in d.ts files. They can be renamed as we like
|
||||
var parameterName = sanitizeName(parameter.name.asString())
|
||||
if (parameterName in allReservedWords)
|
||||
parameterName = "_$parameterName"
|
||||
|
||||
return ExportedParameter(parameterName, exportType(parameter.type))
|
||||
}
|
||||
|
||||
private fun exportProperty(property: IrProperty): ExportedDeclaration? {
|
||||
for (accessor in listOfNotNull(property.getter, property.setter)) {
|
||||
// TODO: Report a frontend error
|
||||
if (accessor.extensionReceiverParameter != null)
|
||||
return null
|
||||
if (accessor.isFakeOverride) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
val parentClass = property.parent as? IrClass
|
||||
|
||||
return ExportedProperty(
|
||||
property.getExportedIdentifier(),
|
||||
exportType(property.getter!!.returnType),
|
||||
mutable = property.isVar,
|
||||
isMember = parentClass != null,
|
||||
isStatic = false,
|
||||
isAbstract = parentClass?.isInterface == false && property.modality == Modality.ABSTRACT,
|
||||
ir = property
|
||||
)
|
||||
}
|
||||
|
||||
private fun classExportability(klass: IrClass): Exportability {
|
||||
when (klass.kind) {
|
||||
ClassKind.ANNOTATION_CLASS,
|
||||
ClassKind.ENUM_CLASS,
|
||||
ClassKind.ENUM_ENTRY,
|
||||
ClassKind.OBJECT ->
|
||||
return Exportability.Prohibited("Class ${klass.fqNameWhenAvailable} with kind: ${klass.kind}")
|
||||
|
||||
ClassKind.CLASS,
|
||||
ClassKind.INTERFACE -> {
|
||||
}
|
||||
}
|
||||
|
||||
if (klass.isInline)
|
||||
return Exportability.Prohibited("Inline class ${klass.fqNameWhenAvailable}")
|
||||
|
||||
return Exportability.Allowed
|
||||
}
|
||||
|
||||
private fun exportClass(
|
||||
klass: IrClass
|
||||
): ExportedDeclaration? {
|
||||
when (val exportability = classExportability(klass)) {
|
||||
is Exportability.Prohibited -> return ErrorDeclaration(exportability.reason)
|
||||
is Exportability.NotNeeded -> return null
|
||||
}
|
||||
|
||||
val members = mutableListOf<ExportedDeclaration>()
|
||||
val statics = mutableListOf<ExportedDeclaration>()
|
||||
|
||||
for (declaration in klass.declarations) {
|
||||
val candidate = getExportCandidate(declaration) ?: continue
|
||||
if (!shouldDeclarationBeExported(candidate)) continue
|
||||
|
||||
when (candidate) {
|
||||
is IrSimpleFunction ->
|
||||
members.addIfNotNull(exportFunction(candidate))
|
||||
|
||||
is IrConstructor ->
|
||||
members.addIfNotNull(exportConstructor(candidate))
|
||||
|
||||
is IrProperty ->
|
||||
members.addIfNotNull(exportProperty(candidate))
|
||||
|
||||
is IrClass ->
|
||||
statics.addIfNotNull(exportClass(candidate))
|
||||
|
||||
is IrField -> {
|
||||
assert(candidate.correspondingPropertySymbol != null) {
|
||||
"Properties without fields are not supported ${candidate.fqNameWhenAvailable}"
|
||||
}
|
||||
}
|
||||
|
||||
else -> error("Can't export member declaration $declaration")
|
||||
}
|
||||
}
|
||||
|
||||
val typeParameters = klass.typeParameters.map { it.name.identifier }
|
||||
|
||||
// TODO: Handle non-exported super types
|
||||
|
||||
val superType = klass.superTypes
|
||||
.firstOrNull { !it.classifierOrFail.isInterface && !it.isAny() }
|
||||
?.let { exportType(it).takeIf { it !is ExportedType.ErrorType } }
|
||||
|
||||
val superInterfaces = klass.superTypes
|
||||
.filter {it.classifierOrFail.isInterface }
|
||||
.map { exportType(it) }
|
||||
.filter { it !is ExportedType.ErrorType }
|
||||
|
||||
val name = klass.getExportedIdentifier()
|
||||
|
||||
return ExportedClass(
|
||||
name = name,
|
||||
isInterface = klass.isInterface,
|
||||
isAbstract = klass.modality == Modality.ABSTRACT,
|
||||
superClass = superType,
|
||||
superInterfaces = superInterfaces,
|
||||
typeParameters = typeParameters,
|
||||
members = members,
|
||||
statics = statics,
|
||||
ir = klass
|
||||
)
|
||||
}
|
||||
|
||||
private fun exportTypeArgument(type: IrTypeArgument): ExportedType {
|
||||
if (type is IrTypeProjection)
|
||||
return exportType(type.type)
|
||||
|
||||
if (type is IrType)
|
||||
return exportType(type)
|
||||
|
||||
return ExportedType.ErrorType("UnknownType ${type.render()}")
|
||||
}
|
||||
|
||||
private fun exportType(type: IrType): ExportedType {
|
||||
if (type is IrDynamicType)
|
||||
return ExportedType.Primitive.Any
|
||||
|
||||
if (type !is IrSimpleType)
|
||||
return ExportedType.ErrorType("NonSimpleType ${type.render()}")
|
||||
|
||||
val classifier = type.classifier
|
||||
val isNullable = type.hasQuestionMark
|
||||
val nonNullType = type.makeNotNull() as IrSimpleType
|
||||
|
||||
val exportedType = when {
|
||||
nonNullType.isBoolean() -> ExportedType.Primitive.Boolean
|
||||
nonNullType.isPrimitiveType() && (!nonNullType.isLong() && !nonNullType.isChar()) ->
|
||||
ExportedType.Primitive.Number
|
||||
|
||||
nonNullType.isByteArray() -> ExportedType.Primitive.ByteArray
|
||||
nonNullType.isShortArray() -> ExportedType.Primitive.ShortArray
|
||||
nonNullType.isIntArray() -> ExportedType.Primitive.IntArray
|
||||
nonNullType.isFloatArray() -> ExportedType.Primitive.FloatArray
|
||||
nonNullType.isDoubleArray() -> ExportedType.Primitive.DoubleArray
|
||||
|
||||
// TODO: Cover these in frontend
|
||||
nonNullType.isBooleanArray() -> ExportedType.ErrorType("BooleanArray")
|
||||
nonNullType.isLongArray() -> ExportedType.ErrorType("LongArray")
|
||||
nonNullType.isCharArray() -> ExportedType.ErrorType("CharArray")
|
||||
|
||||
nonNullType.isString() -> ExportedType.Primitive.String
|
||||
nonNullType.isThrowable() -> ExportedType.Primitive.Throwable
|
||||
nonNullType.isAny() -> ExportedType.Primitive.Any // TODO: Should we wrap Any in a Nullable type?
|
||||
nonNullType.isUnit() -> ExportedType.Primitive.Unit
|
||||
nonNullType.isNothing() -> ExportedType.Primitive.Nothing
|
||||
nonNullType.isArray() -> ExportedType.Array(exportTypeArgument(nonNullType.arguments[0]))
|
||||
nonNullType.isSuspendFunction() -> ExportedType.ErrorType("Suspend functions are not supported")
|
||||
nonNullType.isFunction() -> ExportedType.Function(
|
||||
parameterTypes = nonNullType.arguments.dropLast(1).map { exportTypeArgument(it) },
|
||||
returnType = exportTypeArgument(nonNullType.arguments.last())
|
||||
)
|
||||
|
||||
classifier is IrTypeParameterSymbol -> ExportedType.TypeParameter(classifier.owner.name.identifier)
|
||||
|
||||
classifier is IrClassSymbol -> {
|
||||
val klass = classifier.owner
|
||||
when (val exportability = classExportability(klass)) {
|
||||
is Exportability.Prohibited -> ExportedType.ErrorType(exportability.reason)
|
||||
is Exportability.NotNeeded -> error("Not needed classes types cannot be used")
|
||||
else -> ExportedType.ClassType(
|
||||
klass.fqNameWhenAvailable!!.asString(),
|
||||
type.arguments.map { exportTypeArgument(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> error("Unexpected classifier $classifier")
|
||||
}
|
||||
|
||||
return exportedType.withNullability(isNullable)
|
||||
}
|
||||
|
||||
private fun IrDeclarationWithName.getExportedIdentifier(): String =
|
||||
with(getJsNameOrKotlinName()) {
|
||||
if (isSpecial)
|
||||
error("Cannot export special name: ${name.asString()} for declaration $fqNameWhenAvailable")
|
||||
else identifier
|
||||
}
|
||||
|
||||
private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName): Boolean {
|
||||
if (declaration.fqNameWhenAvailable in context.additionalExportedDeclarations)
|
||||
return true
|
||||
|
||||
if (declaration.isJsExport())
|
||||
return true
|
||||
|
||||
return when (val parent = declaration.parent) {
|
||||
is IrDeclarationWithName -> shouldDeclarationBeExported(parent)
|
||||
is IrAnnotationContainer -> parent.isJsExport()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun functionExportability(function: IrSimpleFunction): Exportability {
|
||||
if (function.isInline && function.typeParameters.any { it.isReified })
|
||||
return Exportability.Prohibited("Inline reified function")
|
||||
if (function.isSuspend)
|
||||
return Exportability.Prohibited("Suspend function")
|
||||
if (function.isFakeOverride)
|
||||
return Exportability.NotNeeded
|
||||
if (function.origin == IrDeclarationOrigin.BRIDGE ||
|
||||
function.origin == JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION ||
|
||||
function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER
|
||||
) {
|
||||
return Exportability.NotNeeded
|
||||
}
|
||||
|
||||
if (function.isFakeOverriddenFromAny())
|
||||
return Exportability.NotNeeded
|
||||
if (function.name.asString().endsWith("-impl"))
|
||||
return Exportability.NotNeeded
|
||||
|
||||
val name = function.getExportedIdentifier()
|
||||
// TODO: Use [] syntax instead of prohibiting
|
||||
if (name in allReservedWords)
|
||||
return Exportability.Prohibited("Name is a reserved word")
|
||||
|
||||
return Exportability.Allowed
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Exportability {
|
||||
object Allowed : Exportability()
|
||||
object NotNeeded : Exportability()
|
||||
class Prohibited(val reason: String) : Exportability()
|
||||
}
|
||||
|
||||
private val IrClassifierSymbol.isInterface
|
||||
get() = (owner as? IrClass)?.isInterface == true
|
||||
|
||||
private val reservedWords = setOf(
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"class",
|
||||
"const",
|
||||
"continue",
|
||||
"debugger",
|
||||
"default",
|
||||
"delete",
|
||||
"do",
|
||||
"else",
|
||||
"enum",
|
||||
"export",
|
||||
"extends",
|
||||
"false",
|
||||
"finally",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"import",
|
||||
"in",
|
||||
"instanceof",
|
||||
"new",
|
||||
"null",
|
||||
"return",
|
||||
"super",
|
||||
"switch",
|
||||
"this",
|
||||
"throw",
|
||||
"true",
|
||||
"try",
|
||||
"typeof",
|
||||
"var",
|
||||
"void",
|
||||
"while",
|
||||
"with"
|
||||
)
|
||||
|
||||
val strictModeReservedWords = setOf(
|
||||
"as",
|
||||
"implements",
|
||||
"interface",
|
||||
"let",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"static",
|
||||
"yield"
|
||||
)
|
||||
|
||||
private val allReservedWords = reservedWords + strictModeReservedWords
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsAstUtils
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.defineProperty
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
|
||||
class ExportModelToJsStatements(
|
||||
private val internalModuleName: JsName,
|
||||
private val nameTables: NameTables
|
||||
) {
|
||||
private val namespaceToRefMap = mutableMapOf<String, JsNameRef>()
|
||||
private val globalNames = NameTable<String>(nameTables.globalNames)
|
||||
|
||||
fun generateModuleExport(module: ExportedModule): List<JsStatement> {
|
||||
return module.declarations.flatMap { generateDeclarationExport(it, JsNameRef(internalModuleName)) }
|
||||
}
|
||||
|
||||
private fun generateDeclarationExport(declaration: ExportedDeclaration, namespace: JsNameRef): List<JsStatement> {
|
||||
return when (declaration) {
|
||||
is ExportedNamespace -> {
|
||||
val statements = mutableListOf<JsStatement>()
|
||||
val elements = declaration.name.split(".")
|
||||
var currentNamespace = ""
|
||||
var currentRef = namespace
|
||||
for (element in elements) {
|
||||
val newNamespace = "$currentNamespace$$element"
|
||||
val newNameSpaceRef = namespaceToRefMap.getOrPut(newNamespace) {
|
||||
val varName = globalNames.declareFreshName(newNamespace, newNamespace)
|
||||
val varRef = JsNameRef(varName)
|
||||
val namespaceRef = JsNameRef(element, currentRef)
|
||||
statements += JsVars(
|
||||
JsVars.JsVar(JsName(varName),
|
||||
JsAstUtils.or(
|
||||
namespaceRef,
|
||||
jsAssignment(
|
||||
namespaceRef,
|
||||
JsObjectLiteral()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
varRef
|
||||
}
|
||||
currentRef = newNameSpaceRef
|
||||
currentNamespace = newNamespace
|
||||
}
|
||||
statements + declaration.declarations.flatMap { generateDeclarationExport(it, currentRef) }
|
||||
}
|
||||
|
||||
is ExportedFunction -> {
|
||||
listOf(
|
||||
jsAssignment(
|
||||
JsNameRef(declaration.name, namespace),
|
||||
JsNameRef(nameTables.getNameForStaticDeclaration(declaration.ir))
|
||||
).makeStmt()
|
||||
)
|
||||
}
|
||||
|
||||
is ExportedConstructor -> emptyList()
|
||||
|
||||
is ExportedProperty -> {
|
||||
val getter = declaration.ir.getter?.let { JsNameRef(nameTables.getNameForStaticDeclaration(it)) }
|
||||
val setter = declaration.ir.setter?.let { JsNameRef(nameTables.getNameForStaticDeclaration(it)) }
|
||||
listOf(defineProperty(namespace, declaration.name, getter, setter).makeStmt())
|
||||
}
|
||||
|
||||
is ErrorDeclaration -> emptyList()
|
||||
|
||||
is ExportedClass -> {
|
||||
if (declaration.isInterface) return emptyList()
|
||||
val newNameSpace = JsNameRef(declaration.name, namespace)
|
||||
val klassExport = jsAssignment(
|
||||
newNameSpace,
|
||||
JsNameRef(
|
||||
nameTables.getNameForStaticDeclaration(
|
||||
declaration.ir
|
||||
)
|
||||
)
|
||||
).makeStmt()
|
||||
val staticsExport = declaration.statics.flatMap { generateDeclarationExport(it, newNameSpace) }
|
||||
listOf(klassExport) + staticsExport
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
// TODO: Support module kinds other than plain
|
||||
|
||||
fun ExportedModule.toTypeScript(): String {
|
||||
val prefix = " type Nullable<T> = T | null | undefined\n"
|
||||
val body = declarations.joinToString("\n") { it.toTypeScript(" ") }
|
||||
return "declare namespace $name {\n$prefix$body\n}\n"
|
||||
}
|
||||
|
||||
fun List<ExportedDeclaration>.toTypeScript(indent: String): String =
|
||||
joinToString("") { it.toTypeScript(indent) + "\n" }
|
||||
|
||||
fun ExportedDeclaration.toTypeScript(indent: String): String = indent + when (this) {
|
||||
is ErrorDeclaration -> "/* ErrorDeclaration: $message */"
|
||||
|
||||
is ExportedNamespace ->
|
||||
"namespace $name {\n" + declarations.toTypeScript("$indent ") + "$indent}"
|
||||
|
||||
is ExportedFunction -> {
|
||||
val keyword: String = when {
|
||||
isMember -> when {
|
||||
isStatic -> "static "
|
||||
isAbstract -> "abstract "
|
||||
else -> ""
|
||||
}
|
||||
else -> "function "
|
||||
}
|
||||
|
||||
val renderedParameters = parameters.joinToString(", ") { it.toTypeScript() }
|
||||
|
||||
val renderedTypeParameters =
|
||||
if (typeParameters.isNotEmpty())
|
||||
"<" + typeParameters.joinToString(", ") + ">"
|
||||
else
|
||||
""
|
||||
|
||||
val renderedReturnType = returnType.toTypeScript()
|
||||
|
||||
"$keyword$name$renderedTypeParameters($renderedParameters): $renderedReturnType"
|
||||
}
|
||||
is ExportedConstructor ->
|
||||
"constructor(${parameters.joinToString(", ") { it.toTypeScript() }})"
|
||||
|
||||
is ExportedProperty -> {
|
||||
val keyword = when {
|
||||
isMember -> (if (isAbstract) "abstract " else "") + (if (!mutable) "readonly " else "")
|
||||
else -> if (mutable) "let " else "const "
|
||||
}
|
||||
keyword + name + ": " + type.toTypeScript() + ";"
|
||||
}
|
||||
|
||||
is ExportedClass -> {
|
||||
val keyword = if (isInterface) "interface" else "class"
|
||||
val superInterfacesKeyword = if (isInterface) "extends" else "implements"
|
||||
|
||||
val superClassClause = superClass?.let { " extends ${it.toTypeScript()}" } ?: ""
|
||||
val superInterfacesClause = if (superInterfaces.isNotEmpty()) {
|
||||
" $superInterfacesKeyword " + superInterfaces.joinToString(", ") { it.toTypeScript() }
|
||||
} else ""
|
||||
|
||||
val membersString = members.joinToString("") { it.toTypeScript("$indent ") + "\n" }
|
||||
|
||||
val renderedTypeParameters =
|
||||
if (typeParameters.isNotEmpty())
|
||||
"<" + typeParameters.joinToString(", ") + ">"
|
||||
else
|
||||
""
|
||||
|
||||
val modifiers = if (isAbstract && !isInterface) "abstract " else ""
|
||||
|
||||
val klassExport = "$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$membersString$indent}"
|
||||
val staticsExport = if (statics.isNotEmpty()) "\n" + ExportedNamespace(name, statics).toTypeScript(indent) else ""
|
||||
klassExport + staticsExport
|
||||
}
|
||||
}
|
||||
|
||||
fun ExportedParameter.toTypeScript(): String =
|
||||
"$name: ${type.toTypeScript()}"
|
||||
|
||||
fun ExportedType.toTypeScript(): String = when (this) {
|
||||
is ExportedType.Primitive -> typescript
|
||||
is ExportedType.Array -> "Array<${elementType.toTypeScript()}>"
|
||||
is ExportedType.Function -> "(" + parameterTypes
|
||||
.withIndex()
|
||||
.joinToString(", ") { (index, type) ->
|
||||
"p$index: ${type.toTypeScript()}"
|
||||
} + ") => " + returnType.toTypeScript()
|
||||
|
||||
is ExportedType.ClassType ->
|
||||
name + if (arguments.isNotEmpty()) "<${arguments.joinToString(", ") { it.toTypeScript() }}>" else ""
|
||||
|
||||
is ExportedType.ErrorType -> "any /*$comment*/"
|
||||
is ExportedType.TypeParameter -> name
|
||||
is ExportedType.Nullable -> "Nullable<" + baseType.toTypeScript() + ">"
|
||||
}
|
||||
+22
-5
@@ -12,7 +12,9 @@ import org.jetbrains.kotlin.ir.backend.js.utils.getJsModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsQualifier
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrExternalPackageFragmentSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFileSymbol
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||
@@ -54,6 +56,22 @@ private class DescriptorlessExternalPackageFragmentSymbol : IrExternalPackageFra
|
||||
}
|
||||
}
|
||||
|
||||
private class DescriptorlessIrFileSymbol : IrFileSymbol {
|
||||
override fun bind(owner: IrFile) {
|
||||
_owner = owner
|
||||
}
|
||||
|
||||
override val descriptor: PackageFragmentDescriptor
|
||||
get() = error("Operation is unsupported")
|
||||
|
||||
private var _owner: IrFile? = null
|
||||
override val owner get() = _owner!!
|
||||
|
||||
override val isBound get() = _owner != null
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun moveBodilessDeclarationsToSeparatePlace(context: JsIrBackendContext, module: IrModuleFragment) {
|
||||
|
||||
val bodilessBuiltInsPackageFragment = IrExternalPackageFragmentImpl(
|
||||
@@ -81,11 +99,10 @@ fun moveBodilessDeclarationsToSeparatePlace(context: JsIrBackendContext, module:
|
||||
|
||||
fun lowerFile(irFile: IrFile): IrFile? {
|
||||
val externalPackageFragment by lazy {
|
||||
context.externalPackageFragment.getOrPut(irFile.fqName) {
|
||||
IrExternalPackageFragmentImpl(
|
||||
DescriptorlessExternalPackageFragmentSymbol(),
|
||||
irFile.fqName
|
||||
)
|
||||
context.externalPackageFragment.getOrPut(irFile.symbol) {
|
||||
IrFileImpl(fileEntry = irFile.fileEntry, fqName = irFile.fqName, symbol = DescriptorlessIrFileSymbol()).also {
|
||||
it.annotations += irFile.annotations
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-100
@@ -6,30 +6,32 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.CompilerResult
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.ExportModelGenerator
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.ExportModelToJsStatements
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.StaticMembersLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.toTypeScript
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.path
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.util.isObject
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class IrModuleToJsTransformer(
|
||||
private val backendContext: JsIrBackendContext,
|
||||
private val mainFunction: IrSimpleFunction?,
|
||||
private val mainArguments: List<String>?
|
||||
) : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
|
||||
|
||||
) {
|
||||
val moduleName = backendContext.configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||
|
||||
private fun generateModuleBody(module: IrModuleFragment, context: JsGenerationContext): List<JsStatement> {
|
||||
val statements = mutableListOf<JsStatement>(
|
||||
val statements = mutableListOf(
|
||||
JsStringLiteral("use strict").makeStmt()
|
||||
)
|
||||
|
||||
@@ -56,68 +58,7 @@ class IrModuleToJsTransformer(
|
||||
return statements
|
||||
}
|
||||
|
||||
private fun generateExportStatements(
|
||||
module: IrModuleFragment,
|
||||
context: JsGenerationContext,
|
||||
internalModuleName: JsName
|
||||
): List<JsStatement> {
|
||||
val exports = mutableListOf<JsExpressionStatement>()
|
||||
|
||||
for (file in module.files) {
|
||||
for (declaration in file.declarations) {
|
||||
exports.addIfNotNull(
|
||||
generateExportStatement(declaration, context, internalModuleName)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return exports
|
||||
}
|
||||
|
||||
private fun generateExportStatement(
|
||||
declaration: IrDeclaration,
|
||||
context: JsGenerationContext,
|
||||
internalModuleName: JsName
|
||||
): JsExpressionStatement? {
|
||||
if (declaration !is IrDeclarationWithVisibility ||
|
||||
declaration !is IrDeclarationWithName ||
|
||||
declaration.visibility != Visibilities.PUBLIC) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!declaration.isExported())
|
||||
return null
|
||||
|
||||
if (declaration.isEffectivelyExternal())
|
||||
return null
|
||||
|
||||
if (declaration is IrClass && declaration.isCompanion)
|
||||
return null
|
||||
|
||||
val name: JsName = when (declaration) {
|
||||
is IrSimpleFunction -> context.getNameForStaticFunction(declaration)
|
||||
is IrClass -> context.getNameForClass(declaration)
|
||||
// TODO: Fields must be exported as properties
|
||||
is IrField -> context.getNameForField(declaration)
|
||||
else -> return null
|
||||
}
|
||||
|
||||
val exportName = sanitizeName(declaration.getJsNameOrKotlinName().asString())
|
||||
|
||||
val expression =
|
||||
if (declaration is IrClass && declaration.isObject) {
|
||||
// TODO: Use export names for properties
|
||||
val instanceGetter = backendContext.objectToGetInstanceFunction[declaration.symbol]!!
|
||||
val instanceGetterName: JsName = context.getNameForStaticFunction(instanceGetter)
|
||||
defineProperty(internalModuleName.makeRef(), name.ident, getter = JsNameRef(instanceGetterName))
|
||||
} else {
|
||||
jsAssignment(JsNameRef(exportName, internalModuleName.makeRef()), name.makeRef())
|
||||
}
|
||||
|
||||
return JsExpressionStatement(expression)
|
||||
}
|
||||
|
||||
private fun generateModule(module: IrModuleFragment): JsProgram {
|
||||
fun generateModule(module: IrModuleFragment): CompilerResult {
|
||||
val additionalPackages = with(backendContext) {
|
||||
externalPackageFragment.values + listOf(
|
||||
bodilessBuiltInsPackageFragment,
|
||||
@@ -125,6 +66,11 @@ class IrModuleToJsTransformer(
|
||||
) + packageLevelJsModules
|
||||
}
|
||||
|
||||
val exportedModule = ExportModelGenerator(backendContext).generateExport(module)
|
||||
val dts = exportedModule.toTypeScript()
|
||||
|
||||
module.files.forEach { StaticMembersLowering(backendContext).lower(it) }
|
||||
|
||||
val namer = NameTables(module.files + additionalPackages)
|
||||
|
||||
val program = JsProgram()
|
||||
@@ -151,7 +97,8 @@ class IrModuleToJsTransformer(
|
||||
)
|
||||
|
||||
val moduleBody = generateModuleBody(module, rootContext)
|
||||
val exportStatements = generateExportStatements(module, rootContext, internalModuleName)
|
||||
val exportStatements = ExportModelToJsStatements(internalModuleName, namer)
|
||||
.generateModuleExport(exportedModule)
|
||||
|
||||
with(rootFunction) {
|
||||
parameters += JsParameter(internalModuleName)
|
||||
@@ -173,7 +120,7 @@ class IrModuleToJsTransformer(
|
||||
kind = moduleKind
|
||||
)
|
||||
|
||||
return program
|
||||
return CompilerResult(program.toString(), dts)
|
||||
}
|
||||
|
||||
private fun generateMainArguments(mainFunction: IrSimpleFunction, rootContext: JsGenerationContext): List<JsExpression> {
|
||||
@@ -247,9 +194,6 @@ class IrModuleToJsTransformer(
|
||||
return Pair(importStatements, importedJsModules)
|
||||
}
|
||||
|
||||
override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode =
|
||||
generateModule(declaration)
|
||||
|
||||
private fun processClassModels(
|
||||
classModelMap: Map<IrClassSymbol, JsIrClassModel>,
|
||||
preDeclarationBlock: JsBlock,
|
||||
@@ -271,28 +215,4 @@ class IrModuleToJsTransformer(
|
||||
declarationHandler
|
||||
)
|
||||
}
|
||||
|
||||
fun IrDeclarationWithName.isExported(): Boolean {
|
||||
if (fqNameWhenAvailable in backendContext.additionalExportedDeclarations)
|
||||
return true
|
||||
|
||||
// Hack to support properties
|
||||
val correspondingProperty = when {
|
||||
this is IrField -> correspondingPropertySymbol
|
||||
this is IrSimpleFunction -> correspondingPropertySymbol
|
||||
else -> null
|
||||
}
|
||||
correspondingProperty?.let {
|
||||
return it.owner.isExported()
|
||||
}
|
||||
|
||||
if (isJsExport())
|
||||
return true
|
||||
|
||||
return when (val parent = parent) {
|
||||
is IrDeclarationWithName -> parent.isExported()
|
||||
is IrAnnotationContainer -> parent.isJsExport()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||
|
||||
class NameTable<T>(
|
||||
val parent: NameTable<T>? = null,
|
||||
val parent: NameTable<*>? = null,
|
||||
private val reserved: MutableSet<String> = mutableSetOf(),
|
||||
val sanitizer: (String) -> String = ::sanitizeName
|
||||
) {
|
||||
@@ -135,7 +135,7 @@ fun functionSignature(declaration: IrFunction): Signature {
|
||||
}
|
||||
|
||||
class NameTables(packages: List<IrPackageFragment>) {
|
||||
private val globalNames: NameTable<IrDeclaration>
|
||||
val globalNames: NameTable<IrDeclaration>
|
||||
private val memberNames: NameTable<Signature>
|
||||
private val localNames = mutableMapOf<IrDeclaration, NameTable<IrDeclaration>>()
|
||||
private val loopNames = mutableMapOf<IrLoop, String>()
|
||||
@@ -297,7 +297,7 @@ class NameTables(packages: List<IrPackageFragment>) {
|
||||
}
|
||||
|
||||
inner class LocalNameGenerator(parentDeclaration: IrDeclaration) : IrElementVisitorVoid {
|
||||
val table = NameTable(globalNames)
|
||||
val table = NameTable<IrDeclaration>(globalNames)
|
||||
|
||||
init {
|
||||
localNames[parentDeclaration] = table
|
||||
@@ -348,4 +348,4 @@ fun sanitizeName(name: String): String {
|
||||
|
||||
val first = name.first().let { if (it.isES5IdentifierStart()) it else '_' }
|
||||
return first.toString() + name.drop(1).map { if (it.isES5IdentifierPart()) it else '_' }.joinToString("")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user