From 977d3ef840604959c90d950f94d2fc6069eb556c Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Thu, 4 Apr 2019 15:07:00 +0300 Subject: [PATCH] [JS IR BE] New name generator --- .../kotlin/ir/backend/js/JsIntrinsics.kt | 2 +- .../ir/backend/js/JsIrBackendContext.kt | 13 +- ...MoveBodilessDeclarationsToSeparatePlace.kt | 6 +- .../backend/js/lower/StaticMembersLowering.kt | 12 +- .../irToJs/IrDeclarationToJsTransformer.kt | 3 +- .../IrElementToJsExpressionTransformer.kt | 97 ++++-- .../IrElementToJsStatementTransformer.kt | 12 +- .../irToJs/IrFunctionToJsTransformer.kt | 11 +- .../irToJs/IrModuleToJsTransformer.kt | 215 ++++++++----- .../transformers/irToJs/JsClassGenerator.kt | 26 +- .../irToJs/JsIntrinsicTransformers.kt | 35 ++- .../js/transformers/irToJs/jsAstUtils.kt | 5 +- .../kotlin/ir/backend/js/utils/IrNamer.kt | 25 ++ .../kotlin/ir/backend/js/utils/IrNamerImpl.kt | 81 +++++ .../kotlin/ir/backend/js/utils/IrTypeUtils.kt | 2 +- .../backend/js/utils/JsGenerationContext.kt | 61 +--- .../ir/backend/js/utils/JsStaticContext.kt | 15 +- .../ir/backend/js/utils/NameGenerator.kt | 17 - .../kotlin/ir/backend/js/utils/NameTables.kt | 294 ++++++++++++++++++ .../kotlin/ir/backend/js/utils/Namer.kt | 58 ---- .../backend/js/utils/SimpleNameGenerator.kt | 228 -------------- .../backend/js/utils/StableNamesCollector.kt | 112 +++++++ .../kotlin/ir/backend/js/utils/misc.kt | 10 +- .../box/inheritance/fromNestedNativeClass.kt | 1 + .../testData/box/jsName/classes.kt | 1 + .../box/jsName/jsNamePropertyAccessors.kt | 1 + .../multiModule/privateInterfaceNameClash.kt | 8 +- .../box/multiModule/privateNameClash.kt | 1 + .../box/nameClashes/jsNameAndPrivate.kt | 1 - .../nameClashes/nativeAndTopLevelFunction.kt | 1 - .../nativeDeclarationAndLocalVar.kt | 1 - .../nameClashes/propertyAndNativeMethod.kt | 1 - .../testData/box/reflection/external.kt | 1 - .../js/src/kotlin/collections/InternalMap.kt | 8 +- 34 files changed, 836 insertions(+), 529 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamer.kt create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt delete mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameGenerator.kt create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt delete mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index 57c9d5fb725..48c7afbb321 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -26,7 +26,7 @@ import java.util.* class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendContext) { private val externalPackageFragmentSymbol = IrExternalPackageFragmentSymbolImpl(context.internalPackageFragmentDescriptor) - private val externalPackageFragment = IrExternalPackageFragmentImpl(externalPackageFragmentSymbol) + val externalPackageFragment = IrExternalPackageFragmentImpl(externalPackageFragmentSymbol) // TODO: Should we drop operator intrinsics in favor of IrDynamicOperatorExpression? diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index e29d7b0cf75..d5a73edef18 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -55,9 +55,12 @@ class JsIrBackendContext( override var inVerbosePhase: Boolean = false + lateinit var externalPackageFragment: IrPackageFragment + lateinit var bodilessBuiltInsPackageFragment: IrPackageFragment + val externalNestedClasses = mutableListOf() val packageLevelJsModules = mutableListOf() - val declarationLevelJsModules = mutableListOf() + val declarationLevelJsModules = mutableListOf() val internalPackageFragmentDescriptor = EmptyPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.js.internal")) val implicitDeclarationFile by lazy { @@ -87,7 +90,7 @@ class JsIrBackendContext( val hasTests get() = testContainerField != null - val testContainer + val testContainer: IrSimpleFunction get() = testContainerField ?: JsIrBuilder.buildFunction("test fun", irBuiltIns.unitType, implicitDeclarationFile).apply { body = JsIrBuilder.buildBlockBody(emptyList()) testContainerField = this @@ -117,7 +120,7 @@ class JsIrBackendContext( private val COROUTINE_PACKAGE_FQNAME = COROUTINE_PACKAGE_FQNAME_13 private val COROUTINE_INTRINSICS_PACKAGE_FQNAME = COROUTINE_PACKAGE_FQNAME.child(INTRINSICS_PACKAGE_NAME) - // TODO: due to name clash those weird suffix is required, remove it once `NameGenerator` is implemented + // TODO: due to name clash those weird suffix is required, remove it once `MemberNameGenerator` is implemented private val COROUTINE_SUSPEND_OR_RETURN_JS_NAME = "suspendCoroutineUninterceptedOrReturnJS" private val GET_COROUTINE_CONTEXT_NAME = "getCoroutineContext" @@ -212,10 +215,10 @@ class JsIrBackendContext( val coroutineSuspendOrReturn = symbolTable.referenceSimpleFunction(getJsInternalFunction(COROUTINE_SUSPEND_OR_RETURN_JS_NAME)) val coroutineSuspendGetter = ir.symbols.coroutineSuspendedGetter - val coroutineGetContext: IrFunctionSymbol + val coroutineGetContext: IrSimpleFunctionSymbol get() { val contextGetter = - continuationClass.owner.declarations.filterIsInstance().atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME } + continuationClass.owner.declarations.filterIsInstance().atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME } ?: continuationClass.owner.declarations.filterIsInstance().atMostOne { it.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!! return contextGetter.symbol } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt index d3a1e69aadc..9ebc6d108bc 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt @@ -60,11 +60,15 @@ fun moveBodilessDeclarationsToSeparatePlace(context: JsIrBackendContext, module: FqName.ROOT ) + context.externalPackageFragment = externalPackageFragment + val bodilessBuiltInsPackageFragment = IrExternalPackageFragmentImpl( DescriptorlessExternalPackageFragmentSymbol(), FqName("kotlin") ) + context.bodilessBuiltInsPackageFragment = bodilessBuiltInsPackageFragment + fun isBuiltInClass(declaration: IrDeclaration): Boolean = declaration is IrClass && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES @@ -92,7 +96,7 @@ fun moveBodilessDeclarationsToSeparatePlace(context: JsIrBackendContext, module: val it = irFile.declarations.iterator() while (it.hasNext()) { - val d = it.next() + val d = it.next() as? IrDeclarationWithName ?: continue if (isBuiltInClass(d)) { it.remove() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/StaticMembersLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/StaticMembersLowering.kt index 17143b87ac1..6ee7e3c71b6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/StaticMembersLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/StaticMembersLowering.kt @@ -37,14 +37,20 @@ class StaticMembersLowering(val context: JsIrBackendContext) : FileLoweringPass staticDeclarationsInClasses.add(declaration) super.visitSimpleFunction(declaration) } + + override fun visitVariable(declaration: IrVariable) { + // TODO: Don't generate variables inside classes + if (declaration.parent is IrClass) + staticDeclarationsInClasses.add(declaration) + super.visitVariable(declaration) + } }) for (declaration in staticDeclarationsInClasses) { val klass = declaration.parentAsClass - val fragment = klass.getPackageFragment()!! klass.declarations.remove(declaration) - fragment.addChild(declaration) - declaration.parent = fragment + irFile.addChild(declaration) + declaration.parent = irFile } } } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt index aee92bd1cb0..18a73e0422e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.js.backend.ast.* +@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer { override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsStatement { @@ -33,7 +34,7 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer { val obj = expression.symbol.owner - val className = context.getNameForSymbol(expression.symbol) if (obj.isEffectivelyExternal()) { - className.makeRef() + context.getRefForExternalClass(obj) } else { + val className = context.getNameForClass(expression.symbol.owner) + // TODO: Don't use implicit naming val getInstanceName = className.ident + "_getInstance" JsInvocation(JsNameRef(getInstanceName)) } @@ -93,24 +102,24 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer nameRef property.setter -> jsAssignment(nameRef, arguments.single()) @@ -154,13 +163,18 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer + require(function is IrSimpleFunction) + + val (target, klass) = if (superQualifier.owner.isInterface) { + val impl = function.resolveFakeOverride()!! Pair(impl, impl.parentAsClass) - } else Pair(symbol.owner, it.owner) - val qualifierName = context.getNameForSymbol(owner.symbol).makeRef() - val targetName = context.getNameForSymbol(target.symbol) + } else { + Pair(function, superQualifier.owner) + } + + val qualifierName = context.getNameForClass(klass).makeRef() + val targetName = context.getNameForMemberFunction(target) val qPrototype = JsNameRef(targetName, prototypeOf(qualifierName)) val callRef = JsNameRef(Namer.CALL_FUNCTION, qPrototype) return JsInvocation(callRef, jsDispatchReceiver?.let { receiver -> listOf(receiver) + arguments } ?: arguments) @@ -169,26 +183,43 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer + context.getRefForExternalClass(klass) + + else -> + context.getNameForClass(klass).makeRef() + } JsNew(ref, arguments) } - } else if (isExternalVararg) { + } + + require(function is IrSimpleFunction) + + val symbolName = when (jsDispatchReceiver) { + null -> context.getNameForStaticFunction(function) + else -> context.getNameForMemberFunction(function) + } + + val ref = when (jsDispatchReceiver) { + null -> JsNameRef(symbolName) + else -> JsNameRef(symbolName, jsDispatchReceiver) + } + + return if (isExternalVararg) { // External vararg arguments should be represented in JS as multiple "plain" arguments (opposed to arrays in Kotlin) // We are using `Function.prototype.apply` function to pass all arguments as a single array. @@ -213,7 +244,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer { +@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") +class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer { override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsFunction { - val funcName = context.getNameForSymbol(declaration.symbol) + val funcName = if (declaration.dispatchReceiverParameter == null) { + context.getNameForStaticFunction(declaration) + } else { + context.getNameForMemberFunction(declaration) + } return translateFunction(declaration, funcName, false, context) } override fun visitConstructor(declaration: IrConstructor, context: JsGenerationContext): JsFunction { assert(declaration.isPrimary) - val funcName = context.getNameForSymbol(declaration.symbol) + val funcName = context.getNameForConstructor(declaration) val constructedClass = declaration.parent as IrClass return translateFunction(declaration, funcName, constructedClass.isObject, context) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index fd4f11d741e..846bdb227bf 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -9,18 +9,18 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.utils.* -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithVisibility -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.util.isEffectivelyExternal import org.jetbrains.kotlin.ir.util.isObject -import org.jetbrains.kotlin.ir.util.parentAsClass 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 +) : BaseIrElementToJsNodeTransformer { -class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer { val moduleName = backendContext.configuration[CommonConfigurationKeys.MODULE_NAME]!! private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!! @@ -28,11 +28,13 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) : val statements = mutableListOf() // TODO: fix it up with new name generator - val anyName = context.getNameForSymbol(backendContext.irBuiltIns.anyClass) - val throwableName = context.getNameForSymbol(backendContext.irBuiltIns.throwableClass) + val anyName = context.getNameForClass(backendContext.irBuiltIns.anyClass.owner) + val throwableName = context.getNameForClass(backendContext.irBuiltIns.throwableClass.owner) + val stringName = context.getNameForClass(backendContext.irBuiltIns.stringClass.owner) statements += JsVars(JsVars.JsVar(anyName, Namer.JS_OBJECT)) statements += JsVars(JsVars.JsVar(throwableName, Namer.JS_ERROR)) + statements += JsVars(JsVars.JsVar(stringName, JsNameRef("String"))) val preDeclarationBlock = JsBlock() val postDeclarationBlock = JsBlock() @@ -50,7 +52,7 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) : statements += context.staticContext.initializerBlock if (backendContext.hasTests) { - statements += JsInvocation(context.getNameForSymbol(backendContext.testContainer.symbol).makeRef()).makeStmt() + statements += JsInvocation(context.getNameForStaticFunction(backendContext.testContainer).makeRef()).makeStmt() } return statements @@ -61,40 +63,132 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) : context: JsGenerationContext, internalModuleName: JsName ): List { - return module.files - .flatMap { it.declarations } - .asSequence() - .filterIsInstance() - .filter { it.visibility == Visibilities.PUBLIC } - .filter { !it.isEffectivelyExternal() } - .filterIsInstance() - .map { declaration -> - val name = context.getNameForSymbol(declaration.symbol) + val exports = mutableListOf() - JsExpressionStatement( - if (declaration is IrClass && declaration.isObject) { - defineProperty(internalModuleName.makeRef(), name.ident, getter = JsNameRef("${name.ident}_getInstance")) - } else { - jsAssignment(JsNameRef(name, internalModuleName.makeRef()), name.makeRef()) - } + for (file in module.files) { + for (declaration in file.declarations) { + exports.addIfNotNull( + generateExportStatement(declaration, context, internalModuleName) ) } - .toList() + } + + 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.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 + defineProperty(internalModuleName.makeRef(), name.ident, getter = JsNameRef("${name.ident}_getInstance")) + } else { + jsAssignment(JsNameRef(exportName, internalModuleName.makeRef()), name.makeRef()) + } + + return JsExpressionStatement(expression) + } + + + private fun generateModule(module: IrModuleFragment): JsProgram { + val additionalPackages = with(backendContext) { + listOf( + externalPackageFragment, + bodilessBuiltInsPackageFragment, + intrinsics.externalPackageFragment + ) + packageLevelJsModules + } + + val namer = NameTables(module.files + additionalPackages) + val program = JsProgram() - val rootContext = JsGenerationContext(JsRootScope(program), backendContext) + + val nameGenerator = IrNamerImpl( + memberNameGenerator = LegacyMemberNameGenerator(program.rootScope), + newNameTables = namer, + rootScope = program.rootScope + ) + val staticContext = JsStaticContext( + backendContext = backendContext, + irNamer = nameGenerator, + rootScope = program.rootScope + ) + val rootContext = JsGenerationContext( + parent = null, + currentBlock = program.globalBlock, + currentFunction = null, + currentScope = program.rootScope, + staticContext = staticContext + ) val rootFunction = JsFunction(program.rootScope, JsBlock(), "root function") val internalModuleName = rootFunction.scope.declareName("_") - rootFunction.parameters += JsParameter(internalModuleName) + val (importStatements, importedJsModules) = + generateImportStatements( + getNameForExternalDeclaration = { rootContext.getNameForStaticDeclaration(it) }, + declareFreshGlobal = { rootFunction.scope.declareFreshName(sanitizeName(it)) } + ) + val moduleBody = generateModuleBody(module, rootContext) + val exportStatements = generateExportStatements(module, rootContext, internalModuleName) + + with(rootFunction) { + parameters += JsParameter(internalModuleName) + parameters += importedJsModules.map { JsParameter(it.internalName) } + with(body) { + statements += importStatements + statements += moduleBody + statements += exportStatements + statements += JsReturn(internalModuleName.makeRef()) + } + } + + program.globalBlock.statements += ModuleWrapperTranslation.wrap( + moduleName, + rootFunction, + importedJsModules, + program, + kind = moduleKind + ) + + return program + } + + private fun generateImportStatements( + getNameForExternalDeclaration: (IrDeclarationWithName) -> JsName, + declareFreshGlobal: (String) -> JsName + ): Pair, List> { val declarationLevelJsModules = backendContext.declarationLevelJsModules.map { externalDeclaration -> val jsModule = externalDeclaration.getJsModule()!! - val name = rootContext.getNameForDeclaration(externalDeclaration) + val name = getNameForExternalDeclaration(externalDeclaration) JsImportedModule(jsModule, name, name.makeRef()) } @@ -110,7 +204,7 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) : val qualifiedReference: JsNameRef if (jsModule != null) { - val internalName = rootFunction.scope.declareFreshName(sanitizeName("\$module\$$jsModule")) + val internalName = declareFreshGlobal("\$module\$$jsModule") packageLevelJsModules += JsImportedModule(jsModule, internalName, null) qualifiedReference = @@ -122,61 +216,24 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) : qualifiedReference = JsNameRef(jsQualifier!!) } - file.declarations.forEach { declaration -> - val declName = rootContext.getNameForDeclaration(declaration) - importStatements.add( - JsExpressionStatement( - jsAssignment( - declName.makeRef(), - JsNameRef(declName, qualifiedReference) + file.declarations + .asSequence() + .filterIsInstance() + .forEach { declaration -> + val declName = getNameForExternalDeclaration(declaration) + importStatements.add( + JsExpressionStatement( + jsAssignment( + declName.makeRef(), + JsNameRef(declName, qualifiedReference) + ) ) ) - ) - } - } - - for (externalClass in backendContext.externalNestedClasses) { - - // External companions are not "nested". Instead they use parent class name. - if (externalClass.isCompanion) - continue - - val declName = rootContext.getNameForDeclaration(externalClass) - val parentName = rootContext.getNameForDeclaration(externalClass.parentAsClass) - importStatements.add( - JsExpressionStatement( - jsAssignment( - declName.makeRef(), - JsNameRef(externalClass.getJsNameOrKotlinName().identifier, parentName.makeRef()) - ) - ) - ) + } } val importedJsModules = declarationLevelJsModules + packageLevelJsModules - rootFunction.parameters += importedJsModules.map { JsParameter(it.internalName) } - - rootFunction.body.statements += importStatements - - val moduleBody = generateModuleBody(module, rootContext) - - val exportStatements = generateExportStatements(module, rootContext, internalModuleName) - - rootFunction.body.statements += moduleBody - - rootFunction.body.statements += exportStatements - - rootFunction.body.statements += JsReturn(internalModuleName.makeRef()) - - program.globalBlock.statements += ModuleWrapperTranslation.wrap( - moduleName, - rootFunction, - importedJsModules, - program, - kind = moduleKind - ) - - return program + return Pair(importStatements, importedJsModules) } override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode = diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt index e70fb0009ac..c2498af16bc 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt @@ -26,10 +26,12 @@ import org.jetbrains.kotlin.utils.addIfNotNull class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) { - private val className = context.getNameForSymbol(irClass.symbol) + private val className = context.getNameForClass(irClass) private val classNameRef = className.makeRef() - private val baseClass = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface } - private val baseClassName = baseClass?.let { context.getNameForType(it) } + private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface } + private val baseClassName = baseClass?.let { + context.getNameForClass(baseClass.classifierOrFail.owner as IrClass) + } private val classPrototypeRef = prototypeOf(classNameRef) private val classBlock = JsGlobalBlock() private val classModel = JsClassModel(className, baseClassName) @@ -87,9 +89,9 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo classBlock.statements += JsExpressionStatement( defineProperty( classPrototypeRef, - context.getNameForDeclaration(property).ident, - getter = property.getter?.let { context.getNameForDeclaration(it) }?.let { JsNameRef(it, classPrototypeRef) }, - setter = property.setter?.let { context.getNameForDeclaration(it) }?.let { JsNameRef(it, classPrototypeRef) } + context.getNameForProperty(property).ident, + getter = property.getter?.let { JsNameRef(context.getNameForMemberFunction(it), classPrototypeRef) }, + setter = property.setter?.let { JsNameRef(context.getNameForMemberFunction(it), classPrototypeRef) } ) ) } @@ -115,7 +117,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo } private fun buildGetterFunction(delegate: IrSimpleFunction): JsFunction { - val getterName = context.getNameForSymbol(delegate.symbol) + val getterName = context.getNameForMemberFunction(delegate) val returnStatement = JsReturn(JsInvocation(JsNameRef(getterName, JsThisRef()))) return JsFunction(JsFunctionScope(context.currentScope, ""), JsBlock(returnStatement), "") @@ -128,7 +130,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo return translatedFunction?.makeStmt() } - val memberName = context.getNameForSymbol(declaration.realOverrideTarget.symbol) + val memberName = context.getNameForMemberFunction(declaration.realOverrideTarget) val memberRef = JsNameRef(memberName, classPrototypeRef) translatedFunction?.let { return jsAssignment(memberRef, it.apply { name = null }).makeStmt() } @@ -147,8 +149,8 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo } if (!implClassDeclaration.defaultType.isAny() && !it.isEffectivelyExternal()) { - val implMethodName = context.getNameForSymbol(it.symbol) - val implClassName = context.getNameForSymbol(implClassDeclaration.symbol) + val implMethodName = context.getNameForMemberFunction(it) + val implClassName = context.getNameForClass(implClassDeclaration) val implClassPrototype = prototypeOf(implClassName.makeRef()) val implMemberRef = JsNameRef(implMethodName, implClassPrototype) @@ -247,10 +249,10 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo JsNameRef(Namer.METADATA_INTERFACES), JsArrayLiteral( irClass.superTypes.mapNotNull { - val symbol = it.classifierOrFail + val symbol = it.classifierOrFail as IrClassSymbol // TODO: make sure that there is a test which breaks when isExternal is used here instead of isEffectivelyExternal if (symbol.isInterface && !functionTypeOrSubtype && !symbol.isEffectivelyExternal) { - JsNameRef(context.getNameForSymbol(symbol)) + JsNameRef(context.getNameForClass(symbol.owner)) } else null } ) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt index 341219ac4f0..f3551aeff12 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt @@ -8,15 +8,16 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext import org.jetbrains.kotlin.ir.backend.js.utils.Namer -import org.jetbrains.kotlin.ir.declarations.IrConstructor -import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrFunctionReference +import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.util.getInlineClassBackingField import org.jetbrains.kotlin.ir.util.getInlinedClass +import org.jetbrains.kotlin.ir.util.isEffectivelyExternal import org.jetbrains.kotlin.js.backend.ast.* typealias IrCallTransformer = (IrCall, context: JsGenerationContext) -> JsExpression @@ -78,8 +79,8 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { prefixOp(intrinsics.jsTypeOf, JsUnaryOperator.TYPEOF) add(intrinsics.jsObjectCreate) { call, context -> - val classToCreate = call.getTypeArgument(0)!! - val className = context.getNameForSymbol(classToCreate.classifierOrFail) + val classToCreate = call.getTypeArgument(0)!!.classifierOrFail.owner as IrClass + val className = context.getNameForClass(classToCreate) val prototype = prototypeOf(className.makeRef()) JsInvocation(Namer.JS_OBJECT_CREATE_FUNCTION, prototype) } @@ -106,8 +107,16 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { } add(intrinsics.jsClass) { call, context -> - val typeName = context.getNameForSymbol(call.getTypeArgument(0)!!.classifierOrFail) - typeName.makeRef() + val classifier: IrClassifierSymbol = call.getTypeArgument(0)!!.classifierOrFail + val owner = classifier.owner + + when { + owner is IrClass && owner.isEffectivelyExternal() -> + context.getRefForExternalClass(owner) + + else -> + context.getNameForStaticDeclaration(owner as IrDeclarationWithName).makeRef() + } } addIfNotNull(intrinsics.jsCode) { call, context -> @@ -147,7 +156,7 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { add(intrinsics.jsCoroutineContext) { _, context: JsGenerationContext -> val contextGetter = backendContext.coroutineGetContext - val getterName = context.getNameForSymbol(contextGetter) + val getterName = context.getNameForStaticFunction(contextGetter.owner) val continuation = context.continuation JsInvocation(JsNameRef(getterName, continuation)) } @@ -193,14 +202,14 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { val arg = translateCallArguments(call, context).single() val inlineClass = call.getTypeArgument(0)!!.getInlinedClass()!! val constructor = inlineClass.declarations.filterIsInstance().single { it.isPrimary } - JsNew(context.getNameForSymbol(constructor.symbol).makeRef(), listOf(arg)) + JsNew(context.getNameForConstructor(constructor).makeRef(), listOf(arg)) } add(intrinsics.jsUnboxIntrinsic) { call: IrCall, context -> val arg = translateCallArguments(call, context).single() val inlineClass = call.getTypeArgument(1)!!.getInlinedClass()!! val field = getInlineClassBackingField(inlineClass) - val fieldName = context.getNameForSymbol(field.symbol) + val fieldName = context.getNameForField(field) JsNameRef(fieldName, arg) } @@ -210,13 +219,17 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { val superClass = call.superQualifierSymbol!! val jsReceiver = receiver.accept(IrElementToJsExpressionTransformer(), context) - val functionName = context.getNameForSymbol(reference.symbol) - val superName = context.getNameForSymbol(superClass).makeRef() + val functionName = context.getNameForMemberFunction(reference.symbol.owner as IrSimpleFunction) + val superName = context.getNameForClass(superClass.owner).makeRef() val qPrototype = JsNameRef(functionName, prototypeOf(superName)) val bindRef = JsNameRef(Namer.BIND_FUNCTION, qPrototype) JsInvocation(bindRef, jsReceiver) } + + add(intrinsics.unreachable) { _, _ -> + JsInvocation(JsNameRef(Namer.UNREACHABLE_NAME)) + } } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index f798c06638e..cc6b467e993 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -39,7 +39,7 @@ fun prototypeOf(classNameRef: JsExpression) = JsNameRef(Namer.PROTOTYPE_NAME, cl fun translateFunction(declaration: IrFunction, name: JsName?, isObjectConstructor: Boolean, context: JsGenerationContext): JsFunction { val functionScope = JsFunctionScope(context.currentScope, "scope for ${name ?: "annon"}") val functionContext = context.newDeclaration(functionScope, declaration) - val functionParams = declaration.valueParameters.map { functionContext.getNameForSymbol(it.symbol) } + val functionParams = declaration.valueParameters.map { functionContext.getNameForValueDeclaration(it) } val body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock() val functionBody = if (isObjectConstructor) { @@ -56,7 +56,7 @@ fun translateFunction(declaration: IrFunction, name: JsName?, isObjectConstructo parameters.add(JsParameter(parameter)) } - declaration.extensionReceiverParameter?.let { function.addParameter(functionContext.getNameForSymbol(it.symbol)) } + declaration.extensionReceiverParameter?.let { function.addParameter(functionContext.getNameForValueDeclaration(it)) } functionParams.forEach { function.addParameter(it) } if (declaration.descriptor.isSuspend) { function.addParameter(context.currentScope.declareName(Namer.CONTINUATION)) @@ -82,6 +82,7 @@ fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGene fun JsStatement.asBlock() = this as? JsBlock ?: JsBlock(this) +// TODO: Don't use implicit name conventions fun JsName.objectInstanceName() = "${ident}_instance" fun defineProperty(receiver: JsExpression, name: String, value: () -> JsExpression): JsInvocation { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamer.kt new file mode 100644 index 00000000000..0b0efce5036 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamer.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.utils + +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrLoop +import org.jetbrains.kotlin.js.backend.ast.JsName +import org.jetbrains.kotlin.js.backend.ast.JsNameRef + +interface IrNamer { + fun getNameForConstructor(constructor: IrConstructor): JsName + fun getNameForMemberFunction(function: IrSimpleFunction): JsName + fun getNameForMemberField(field: IrField): JsName + fun getNameForField(field: IrField): JsName + fun getNameForValueDeclaration(declaration: IrValueDeclaration): JsName + fun getNameForClass(klass: IrClass): JsName + fun getNameForStaticFunction(function: IrSimpleFunction): JsName + fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): JsName + fun getNameForProperty(property: IrProperty): JsName + fun getRefForExternalClass(klass: IrClass): JsNameRef + fun getNameForLoop(loop: IrLoop): String? +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt new file mode 100644 index 00000000000..198a129afcd --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.utils + +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrLoop +import org.jetbrains.kotlin.ir.util.parentAsClass +import org.jetbrains.kotlin.js.backend.ast.JsName +import org.jetbrains.kotlin.js.backend.ast.JsNameRef +import org.jetbrains.kotlin.js.backend.ast.JsRootScope + +class IrNamerImpl( + private val memberNameGenerator: LegacyMemberNameGenerator, + private val newNameTables: NameTables, + private val rootScope: JsRootScope // TODO: Don't use scopes +) : IrNamer { + + override fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): JsName { + val name = newNameTables.getNameForStaticDeclaration(declaration) + return rootScope.declareName(name) + } + + override fun getNameForLoop(loop: IrLoop): String? = + newNameTables.getNameForLoop(loop) + + override fun getNameForConstructor(constructor: IrConstructor): JsName { + return getNameForStaticDeclaration(constructor.parentAsClass) + } + + override fun getNameForMemberFunction(function: IrSimpleFunction): JsName { + require(function.dispatchReceiverParameter != null) + return memberNameGenerator.getNameForMemberFunction(function) + } + + override fun getNameForMemberField(field: IrField): JsName { + require(!field.isStatic) + return memberNameGenerator.getNameForMemberField(field) + } + + override fun getNameForField(field: IrField): JsName { + return if (field.isStatic) { + getNameForStaticDeclaration(field) + } else { + getNameForMemberField(field) + } + } + + override fun getNameForValueDeclaration(declaration: IrValueDeclaration): JsName = + getNameForStaticDeclaration(declaration) + + override fun getNameForClass(klass: IrClass): JsName = + getNameForStaticDeclaration(klass) + + override fun getNameForStaticFunction(function: IrSimpleFunction): JsName = + getNameForStaticDeclaration(function) + + override fun getNameForProperty(property: IrProperty): JsName { + return rootScope.declareName(property.getJsNameOrKotlinName().asString()) + } + + override fun getRefForExternalClass(klass: IrClass): JsNameRef { + val parent = klass.parent + if (klass.isCompanion) + return getRefForExternalClass(parent as IrClass) + + val currentClassName = klass.getJsNameOrKotlinName().identifier + return when (parent) { + is IrClass -> + JsNameRef(currentClassName, getRefForExternalClass(parent)) + + is IrPackageFragment -> + JsNameRef(currentClassName) + + else -> + error("Unsupported external class parent $parent") + } + } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrTypeUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrTypeUtils.kt index 5b91132ad3c..f7331be29cf 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrTypeUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrTypeUtils.kt @@ -5,11 +5,11 @@ package org.jetbrains.kotlin.ir.backend.js.utils -import org.jetbrains.kotlin.backend.common.serialization.fqNameSafe 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.fqNameSafe import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt index ff10e30baf6..430397340d0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt @@ -5,54 +5,27 @@ package org.jetbrains.kotlin.ir.backend.js.utils -import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrLoop -import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.js.backend.ast.* -class JsGenerationContext { - fun newDeclaration(scope: JsScope, func: IrFunction? = null): JsGenerationContext { - return JsGenerationContext(this, if (func != null) JsBlock() else JsGlobalBlock(), scope, func) - } - - val currentBlock: JsBlock - val currentScope: JsScope - val currentFunction: IrFunction? - val parent: JsGenerationContext? +class JsGenerationContext( + val parent: JsGenerationContext?, + val currentBlock: JsBlock, + val currentScope: JsScope, + val currentFunction: IrFunction?, val staticContext: JsStaticContext - private val program: JsProgram - - constructor(rootScope: JsRootScope, backendContext: JsIrBackendContext) { - - this.parent = null - this.program = rootScope.program - this.staticContext = JsStaticContext(rootScope, program.globalBlock, SimpleNameGenerator(), backendContext) - this.currentScope = rootScope - this.currentBlock = program.globalBlock - this.currentFunction = null +): IrNamer by staticContext { + fun newDeclaration(scope: JsScope, func: IrFunction? = null): JsGenerationContext { + return JsGenerationContext( + parent = this, + currentBlock = if (func != null) JsBlock() else JsGlobalBlock(), + currentScope = scope, + currentFunction = func, + staticContext = staticContext + ) } - constructor(parent: JsGenerationContext, block: JsBlock, scope: JsScope, func: IrFunction?) { - this.parent = parent - this.program = parent.program - this.staticContext = parent.staticContext - this.currentBlock = block - this.currentScope = scope - this.currentFunction = func - } - - fun getNameForDeclaration(declaration: IrDeclaration): JsName = - if (declaration is IrSymbolOwner) - getNameForSymbol(declaration.symbol) - else - error("Unsupported") - - fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol, this) - fun getNameForType(type: IrType): JsName = staticContext.getNameForType(type, this) - fun getNameForLoop(loop: IrLoop): JsName? = staticContext.getNameForLoop(loop, this) - val continuation get() = if (isCoroutineDoResume()) { JsThisRef() @@ -60,7 +33,7 @@ class JsGenerationContext { if (currentFunction!!.descriptor.isSuspend) { JsNameRef(currentScope.declareName(Namer.CONTINUATION)) } else { - getNameForSymbol(currentFunction.valueParameters.last().symbol).makeRef() + getNameForValueDeclaration(currentFunction.valueParameters.last()).makeRef() } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt index 77abbed9b3c..1cbfec37372 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt @@ -8,9 +8,6 @@ package org.jetbrains.kotlin.ir.backend.js.utils import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction -import org.jetbrains.kotlin.ir.expressions.IrLoop -import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.js.backend.ast.JsClassModel import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock import org.jetbrains.kotlin.js.backend.ast.JsName @@ -19,10 +16,10 @@ import org.jetbrains.kotlin.js.backend.ast.JsRootScope class JsStaticContext( val rootScope: JsRootScope, - private val globalBlock: JsGlobalBlock, - private val nameGenerator: NameGenerator, - val backendContext: JsIrBackendContext -) { + val backendContext: JsIrBackendContext, + private val irNamer: IrNamer +) : IrNamer by irNamer { + val intrinsics = JsIntrinsicTransformers(backendContext) // TODO: use IrSymbol instead of JsName val classModels = mutableMapOf() @@ -31,8 +28,4 @@ class JsStaticContext( .filterIsInstance().single { it.name.asString() == "doResume" }.symbol val initializerBlock = JsGlobalBlock() - - fun getNameForSymbol(irSymbol: IrSymbol, context: JsGenerationContext) = nameGenerator.getNameForSymbol(irSymbol, context) - fun getNameForType(type: IrType, context: JsGenerationContext) = nameGenerator.getNameForType(type, context) - fun getNameForLoop(loop: IrLoop, context: JsGenerationContext) = nameGenerator.getNameForLoop(loop, context) } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameGenerator.kt deleted file mode 100644 index 0b7d219cbbc..00000000000 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameGenerator.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. 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.utils - -import org.jetbrains.kotlin.ir.expressions.IrLoop -import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.js.backend.ast.JsName - -interface NameGenerator { - fun getNameForSymbol(symbol: IrSymbol, context: JsGenerationContext): JsName - fun getNameForType(type: IrType, context: JsGenerationContext): JsName - fun getNameForLoop(loop: IrLoop, context: JsGenerationContext): JsName? -} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt new file mode 100644 index 00000000000..98b95955f22 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt @@ -0,0 +1,294 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.utils + +import org.jetbrains.kotlin.backend.common.ir.isTopLevel +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrLoop +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.ir.util.isEffectivelyExternal +import org.jetbrains.kotlin.ir.util.isInlined +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.js.backend.ast.JsName +import org.jetbrains.kotlin.js.backend.ast.JsScope +import org.jetbrains.kotlin.js.naming.isES5IdentifierPart +import org.jetbrains.kotlin.js.naming.isES5IdentifierStart +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty + +class NameTable( + val parent: NameTable? = null, + private val reserved: MutableSet = mutableSetOf() +) { + var finished = false + val names = mutableMapOf() + + private fun isReserved(name: String): Boolean { + if (parent != null && parent.isReserved(name)) + return true + return name in reserved + } + + fun declareStableName(declaration: T, name: String) { + if (parent != null) assert(parent.finished) + assert(!finished) + names[declaration] = name + reserved.add(name) + } + + fun declareFreshName(declaration: T, suggestedName: String) { + val freshName = findFreshName(sanitizeName(suggestedName)) + declareStableName(declaration, freshName) + } + + private fun findFreshName(suggestedName: String): String { + if (!isReserved(suggestedName)) + return suggestedName + + var i = 0 + + fun freshName() = + suggestedName + "_" + i + + while (isReserved(freshName())) { + i++ + } + return freshName() + } +} + +fun NameTable.dump(): String = + "Names: \n" + names.toList().joinToString("\n") { (declaration, name) -> + val decl: FqName? = (declaration as IrDeclarationWithName).fqNameWhenAvailable + val declRef = decl ?: declaration + "--- $declRef => $name" + } + + +class NameTables(packages: List) { + private val globalNames: NameTable + private val memberNames: NameTable + private val localNames = mutableMapOf>() + private val loopNames = mutableMapOf() + + init { + val stableNamesCollector = StableNamesCollector() + packages.forEach { it.acceptChildrenVoid(stableNamesCollector) } + + globalNames = NameTable(reserved = stableNamesCollector.staticNames) + memberNames = NameTable(reserved = stableNamesCollector.memberNames) + + for (p in packages) { + for (declaration in p.declarations) { + generateNamesForTopLevelDecl(declaration) + } + } + + globalNames.finished = true + + for (p in packages) { + for (declaration in p.declarations) { + if (declaration.isEffectivelyExternal()) + continue + + val localNameGenerator = LocalNameGenerator(declaration) + + if (declaration is IrClass) { + declaration.thisReceiver!!.acceptVoid(localNameGenerator) + for (memberDecl in declaration.declarations) { + memberDecl.acceptChildrenVoid(LocalNameGenerator(memberDecl)) + } + } else { + declaration.acceptChildrenVoid(localNameGenerator) + } + } + } + } + + @Suppress("unused") + fun dump(): String { + val local = localNames.toList().joinToString("\n") { (decl, table) -> + val declRef = (decl as? IrDeclarationWithName)?.fqNameWhenAvailable ?: decl + "\nLocal names for $declRef:\n${table.dump()}\n" + } + return "Global names:\n${globalNames.dump()}" + + "\nMember names:\n${memberNames.dump()}" + + "\nLocal names:\n$local\n" + } + + fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): String { + val global: String? = globalNames.names[declaration] + if (global != null) return global + + if (declaration is IrTypeParameter) { + // TODO: Fix type parameters + return declaration.name.identifier + } + + var parent: IrDeclarationParent = declaration.parent + while (parent is IrDeclaration) { + val parentLocalNames = localNames[parent] + if (parentLocalNames != null) { + val localName = parentLocalNames.names[declaration] + if (localName != null) + return localName + } + parent = parent.parent + } + + error("Can't find name for declaration $declaration") + } + + + private fun generateNamesForTopLevelDecl(declaration: IrDeclaration) { + when { + declaration !is IrDeclarationWithName -> + return + + declaration.isEffectivelyExternal() -> + globalNames.declareStableName(declaration, declaration.getJsNameOrKotlinName().identifier) + + else -> + globalNames.declareFreshName(declaration, declaration.name.asString()) + } + } + + inner class LocalNameGenerator(parentDeclaration: IrDeclaration) : IrElementVisitorVoid { + val table = NameTable(globalNames) + + init { + localNames[parentDeclaration] = table + } + + private val localLoopNames = NameTable() + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitValueParameter(declaration: IrValueParameter) { + val parentFunction = declaration.parent as? IrFunction + if ((declaration.origin == IrDeclarationOrigin.INSTANCE_RECEIVER && declaration.name.isSpecial) || + (parentFunction != null && declaration == parentFunction.dispatchReceiverParameter) + ) + table.declareStableName(declaration, Namer.IMPLICIT_RECEIVER_NAME) + else + super.visitValueParameter(declaration) + } + + override fun visitDeclaration(declaration: IrDeclaration) { + if (declaration is IrDeclarationWithName && declaration is IrSymbolOwner) { + table.declareFreshName(declaration, declaration.name.asString()) + } + super.visitDeclaration(declaration) + } + + override fun visitLoop(loop: IrLoop) { + val label = loop.label + if (label != null) { + localLoopNames.declareFreshName(loop, label) + loopNames[loop] = localLoopNames.names[loop]!! + } + super.visitLoop(loop) + } + } + + fun getNameForLoop(loop: IrLoop): String? = + if (loop.label == null) + null + else + loopNames[loop]!! +} + +// TODO: implement without JsScope +class LegacyMemberNameGenerator(val scope: JsScope) { + + private val fieldCache = mutableMapOf() + private val functionCache = mutableMapOf() + + fun getNameForMemberField(field: IrField): JsName { + return fieldCache.getOrPut(field) { getNewNameForField(field) } + } + + fun getNameForMemberFunction(function: IrSimpleFunction): JsName { + return functionCache.getOrPut(function) { getNewNameForFunction(function) } + } + + private fun getNewNameForField(f: IrField): JsName { + require(!f.isTopLevel) + require(!f.isStatic) + + if (f.isEffectivelyExternal()) { + return scope.declareName(f.name.identifier) + } + + val parentName = (f.parent as IrDeclarationWithName).name.asString() + val name = "${f.name.asString()}_$parentName" + + return scope.declareFreshName(sanitizeName(name)) + } + + private fun getNewNameForFunction(declaration: IrSimpleFunction): JsName { + require(!declaration.isStaticMethodOfClass) + require(declaration.dispatchReceiverParameter != null) + + val declarationName = declaration.getJsNameOrKotlinName().asString() + + if (declaration.origin == JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION) { + return scope.declareName(declarationName) + } + + if (declaration.isEffectivelyExternal()) { + return scope.declareName(declarationName) + } + declaration.getJsName()?.let { jsName -> + return scope.declareName(jsName) + } + + val nameBuilder = StringBuilder() + + // Handle names for special functions + if (declaration.isEqualsInheritedFromAny()) { + return scope.declareName("equals") + } + + nameBuilder.append(declarationName) + + // TODO should we skip type parameters and use upper bound of type parameter when print type of value parameters? + declaration.typeParameters.ifNotEmpty { + nameBuilder.append("_\$t") + joinTo(nameBuilder, "") { "_${it.name.asString()}" } + } + declaration.extensionReceiverParameter?.let { + nameBuilder.append("_r$${it.type.asString()}") + } + declaration.valueParameters.ifNotEmpty { + joinTo(nameBuilder, "") { "_${it.type.asString()}" } + } + declaration.returnType.let { + // Return type is only used in signature for inline class types because + // they are binary incompatible with supertypes. + if (it.isInlined()) { + nameBuilder.append("_ret$${it.asString()}") + } + } + + // TODO: Check reserved names + + return scope.declareName(sanitizeName(nameBuilder.toString())) + } +} + + +fun sanitizeName(name: String): String { + if (name.isEmpty()) return "_" + + val first = name.first().let { if (it.isES5IdentifierStart()) it else '_' } + return first.toString() + name.drop(1).map { if (it.isES5IdentifierPart()) it else '_' }.joinToString("") +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt index ce663715aa5..1d9473a712e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt @@ -9,57 +9,20 @@ import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.js.backend.ast.JsNameRef object Namer { - val KOTLIN_NAME = KotlinLanguage.NAME - val KOTLIN_LOWER_NAME = KOTLIN_NAME.toLowerCase() - -// val EQUALS_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getAny(), "equals") -// val COMPARE_TO_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getComparable(), "compareTo") - val LONG_FROM_NUMBER = "fromNumber" - val LONG_TO_NUMBER = "toNumber" - val LONG_FROM_INT = "fromInt" - val LONG_ZERO = "ZERO" - val LONG_ONE = "ONE" - val LONG_NEG_ONE = "NEG_ONE" - val LONG_MAX_VALUE = "MAX_VALUE" - val LONG_MIN_VALUE = "MIN_VALUE" - val PRIMITIVE_COMPARE_TO = "primitiveCompareTo" - val IS_CHAR = "isChar" - val IS_NUMBER = "isNumber" - val IS_CHAR_SEQUENCE = "isCharSequence" - val GET_KCLASS = "getKClass" - val GET_KCLASS_FROM_EXPRESSION = "getKClassFromExpression" - - val CALLEE_NAME = "\$fun" - val CALL_FUNCTION = "call" - val APPLY_FUNCTION = "apply" val BIND_FUNCTION = "bind" val SLICE_FUNCTION = "slice" - val CONCAT_FUNCTION = "concat" val OUTER_NAME = "\$outer" val UNREACHABLE_NAME = "\$unreachable" val DELEGATE = "\$delegate" - val ROOT_PACKAGE = "_" - - val EXTENSION_RECEIVER_NAME = "\$receiver" val IMPLICIT_RECEIVER_NAME = "this" - val ANOTHER_THIS_PARAMETER_NAME = "\$this" - val THROW_CLASS_CAST_EXCEPTION_FUN_NAME = "throwCCE" - val THROW_ILLEGAL_STATE_EXCEPTION_FUN_NAME = "throwISE" - val THROW_UNINITIALIZED_PROPERTY_ACCESS_EXCEPTION = "throwUPAE" - val NULL_CHECK_INTRINSIC_NAME = "ensureNotNull" val PROTOTYPE_NAME = "prototype" val CONSTRUCTOR_NAME = "constructor" - val CAPTURED_VAR_FIELD = "v" - - val IS_ARRAY_FUN_REF = JsNameRef("isArray", "Array") - val DEFINE_INLINE_FUNCTION = "defineInlineFunction" - val DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "\$default" val CONTINUATION = "\$cont" @@ -68,35 +31,14 @@ object Namer { val JS_OBJECT = JsNameRef("Object") val JS_OBJECT_CREATE_FUNCTION = JsNameRef("create", JS_OBJECT) - val LOCAL_MODULE_PREFIX = "\$module\$" val METADATA = "\$metadata\$" val METADATA_INTERFACES = "interfaces" val METADATA_SIMPLE_NAME = "simpleName" val METADATA_CLASS_KIND = "kind" - val CLASS_KIND_ENUM = "Kind" - val CLASS_KIND_CLASS = "CLASS" - val CLASS_KIND_INTERFACE = "INTERFACE" - val CLASS_KIND_OBJECT = "OBJECT" - - val OBJECT_INSTANCE_VAR_SUFFIX = "_instance" - val OBJECT_INSTANCE_FUNCTION_SUFFIX = "_getInstance" - - val ENUM_NAME_FIELD = "name\$" - val ENUM_ORDINAL_FIELD = "ordinal\$" - - val IMPORTS_FOR_INLINE_PROPERTY = "\$\$importsForInline\$\$" - - val GETTER_PREFIX = "get_" - val SETTER_PREFIX = "set_" val KCALLABLE_GET_NAME = "" val KCALLABLE_NAME = "callableName" val KPROPERTY_GET = "get" val KPROPERTY_SET = "set" val KCALLABLE_CACHE_SUFFIX = "\$cache" - - val SETTER_ARGUMENT = "\$setValue" - - val THIS_SPECIAL_NAME = "" - val SET_SPECIAL_NAME = "" } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt deleted file mode 100644 index 502d52a4656..00000000000 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. 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.utils - -import org.jetbrains.kotlin.backend.common.ir.isTopLevel -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrLoop -import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.classifierOrFail -import org.jetbrains.kotlin.ir.util.isEffectivelyExternal -import org.jetbrains.kotlin.ir.util.isInlined -import org.jetbrains.kotlin.ir.util.parentAsClass -import org.jetbrains.kotlin.js.backend.ast.JsName -import org.jetbrains.kotlin.js.naming.isES5IdentifierPart -import org.jetbrains.kotlin.js.naming.isES5IdentifierStart -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor -import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty - -// TODO: this class has to be reimplemented soon -class SimpleNameGenerator : NameGenerator { - - private val nameCache = mutableMapOf() - private val loopCache = mutableMapOf() - - override fun getNameForSymbol(symbol: IrSymbol, context: JsGenerationContext): JsName = - getNameForDeclaration(symbol.owner as IrDeclarationWithName, context) - - override fun getNameForLoop(loop: IrLoop, context: JsGenerationContext): JsName? = loop.label?.let { - loopCache.getOrPut(loop) { context.currentScope.declareFreshName(sanitizeName(loop.label!!)) } - } - - override fun getNameForType(type: IrType, context: JsGenerationContext) = - getNameForDeclaration(type.classifierOrFail.owner as IrDeclarationWithName, context) - - private fun getNameForDeclaration(declaration: IrDeclarationWithName, context: JsGenerationContext): JsName { - return nameCache.getOrPut(declaration) { getNewNameForDeclaration(declaration, context) } - } - - private fun getNewNameForDeclaration(declaration: IrDeclarationWithName, context: JsGenerationContext): JsName { - var nameDeclarator: (String) -> JsName = context.currentScope::declareName - - val declarationName = declaration.getJsNameOrKotlinName().asString() - - if (declaration is IrProperty) { - return context.currentScope.declareName(declaration.getJsNameOrKotlinName().asString()) - } - - if (declaration is IrSimpleFunction && declaration.origin == JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION) { - return nameDeclarator(declarationName) - } - - if (declaration.isEffectivelyExternal()) { - if (declaration is IrConstructor) - return getNameForDeclaration(declaration.parentAsClass, context) - - if (declaration is IrClass && declaration.parent is IrClass) { - val parentName = getNameForDeclaration(declaration.parentAsClass, context) - if (declaration.isCompanion) { - // External companions are class references - return parentName - } - return context.currentScope.declareFreshName(parentName.ident + "$" + declarationName) - } - return nameDeclarator(declarationName) - } - - val jsName = declaration.getJsName() - if (jsName != null) { - return context.currentScope.declareName(jsName) - } - - val nameBuilder = StringBuilder() - when (declaration) { - is IrValueParameter -> { - if ((context.currentFunction is IrConstructor && declaration.origin == IrDeclarationOrigin.INSTANCE_RECEIVER && declaration.name.isSpecial) || - declaration == context.currentFunction?.dispatchReceiverParameter - ) - nameBuilder.append(Namer.IMPLICIT_RECEIVER_NAME) - else if (declaration == context.currentFunction?.extensionReceiverParameter) { - nameBuilder.append(Namer.EXTENSION_RECEIVER_NAME) - } else { - val declaredName = declarationName - nameBuilder.append(declaredName) - if (declaredName.startsWith("\$")) { - nameBuilder.append('.') - nameBuilder.append(declaration.index) - } - nameDeclarator = context.currentScope::declareFreshName - } - } - is IrField -> { - nameBuilder.append(declarationName) - if (declaration.isTopLevel) { - nameDeclarator = context.staticContext.rootScope::declareFreshName - } else { - nameBuilder.append('.') - nameBuilder.append(getNameForDeclaration(declaration.parent as IrDeclarationWithName, context)) - if (declaration.visibility == Visibilities.PRIVATE) nameDeclarator = context.currentScope::declareFreshName - } - } - is IrClass -> { - if (declaration.isCompanion) { - nameBuilder.append(getNameForDeclaration(declaration.parent as IrDeclarationWithName, context)) - nameBuilder.append('.') - } - - nameBuilder.append(declarationName) - - (declaration.parent as? IrClass)?.let { - nameBuilder.append("$") - nameBuilder.append(getNameForDeclaration(it, context)) - } - - - nameDeclarator = context.staticContext.rootScope::declareFreshName - if (declaration.kind == ClassKind.OBJECT || declaration.name.isSpecial || declaration.visibility == Visibilities.LOCAL) { - val parent = declaration.parent - when (parent) { - is IrDeclarationWithName -> nameBuilder.append(getNameForDeclaration(parent, context)) - is IrPackageFragment -> nameBuilder.append(parent.fqName.asString()) - } - } - - // TODO: remove asap `NameGenerator` is implemented - (declaration.parent as? IrPackageFragment)?.let { - if (declaration.isInline && it.fqName.asString() != "kotlin") { - nameBuilder.append("_FIX") - } - } - - } - is IrConstructor -> { - nameBuilder.append(getNameForDeclaration(declaration.parent as IrClass, context)) - } - is IrVariable -> { - nameBuilder.append(declaration.name.identifier) - nameDeclarator = context.currentScope::declareFreshName - } - is IrSimpleFunction -> { - - // Handle names for special functions - if (declaration.isEqualsInheritedFromAny()) { - return context.staticContext.rootScope.declareName("equals") - } - - if (declaration.isStaticMethodOfClass) { - nameBuilder.append(getNameForDeclaration(declaration.parent as IrClass, context)) - nameBuilder.append('.') - } - if (declaration.dispatchReceiverParameter == null) { - nameDeclarator = context.staticContext.rootScope::declareFreshName - } - - nameBuilder.append(declarationName) - // TODO should we skip type parameters and use upper bound of type parameter when print type of value parameters? - declaration.typeParameters.ifNotEmpty { - nameBuilder.append("_\$t") - joinTo(nameBuilder, "") { "_${it.name.asString()}" } - } - declaration.extensionReceiverParameter?.let { - nameBuilder.append("_r$${it.type.asString()}") - } - declaration.valueParameters.ifNotEmpty { - joinTo(nameBuilder, "") { "_${it.type.asString()}" } - } - declaration.returnType.let { - // Return type is only used in signature for inline class types because - // they are binary incompatible with supertypes. - if (it.isInlined()) { - nameBuilder.append("_ret$${it.asString()}") - } - } - } - } - - if (nameBuilder.toString() in RESERVED_IDENTIFIERS) { - nameBuilder.append(0) - nameDeclarator = context.currentScope::declareFreshName - } - - return nameDeclarator(sanitizeName(nameBuilder.toString())) - } -} - -private val RESERVED_IDENTIFIERS = setOf( - // keywords - "await", "break", "case", "catch", "continue", "debugger", "default", "delete", "do", "else", "finally", "for", "function", "if", - "in", "instanceof", "new", "return", "switch", "throw", "try", "typeof", "var", "void", "while", "with", - - // future reserved words - "class", "const", "enum", "export", "extends", "import", "super", - - // as future reserved words in strict mode - "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield", - - // additional reserved words - // "null", "true", "false", - - // disallowed as variable names in strict mode - "eval", "arguments", - - // global identifiers usually declared in a typical JS interpreter - "NaN", "isNaN", "Infinity", "undefined", - - "Error", "Object", "Number", - - // "Math", "String", "Boolean", "Date", "Array", "RegExp", "JSON", - - // global identifiers usually declared in know environments (node.js, browser, require.js, WebWorkers, etc) - // "require", "define", "module", "window", "self", - - // the special Kotlin object - "Kotlin" -) - - -fun sanitizeName(name: String): String { - if (name.isEmpty()) return "_" - - val first = name.first().let { if (it.isES5IdentifierStart()) it else '_' } - return first.toString() + name.drop(1).map { if (it.isES5IdentifierPart()) it else '_' }.joinToString("") -} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt new file mode 100644 index 00000000000..1ed9e5cc6cb --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.utils + +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName +import org.jetbrains.kotlin.ir.util.isEffectivelyExternal +import org.jetbrains.kotlin.ir.util.isPropertyAccessor +import org.jetbrains.kotlin.ir.util.isPropertyField +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.utils.addIfNotNull + +class StableNamesCollector : IrElementVisitorVoid { + val staticNames = mutableSetOf() + val memberNames = mutableSetOf() + + init { + staticNames.addAll(RESERVED_IDENTIFIERS) + staticNames.add(Namer.IMPLICIT_RECEIVER_NAME) + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitDeclaration(declaration: IrDeclaration) { + super.visitDeclaration(declaration) + + if (declaration !is IrDeclarationWithName) + return + + val scope = + if (declaration.hasStaticDispatch()) + staticNames + else + memberNames + + val stableName = + if (declaration.isEffectivelyExternal()) + stableNameForExternalDeclaration(declaration) + else + stableNameForNonExternalDeclaration(declaration) + + scope.addIfNotNull(stableName) + } + + private fun stableNameForNonExternalDeclaration(declaration: IrDeclarationWithName): String? = + declaration.getJsName() + + private fun stableNameForExternalDeclaration(declaration: IrDeclarationWithName): String? { + if (declaration.isPropertyAccessor || + declaration.isPropertyField || + declaration is IrConstructor + ) { + return null + } + + val importedFromModuleOnly = + declaration.getJsModule() != null && !declaration.isJsNonModule() + + val jsName = declaration.getJsName() + val jsQualifier = declaration.getJsQualifier() + + return when { + importedFromModuleOnly -> + null + + jsQualifier != null -> + jsQualifier.split('1')[0] + + jsName != null -> + jsName + + else -> + declaration.name.identifier + } + } +} + +private val RESERVED_IDENTIFIERS = setOf( + // keywords + "await", "break", "case", "catch", "continue", "debugger", "default", "delete", "do", "else", "finally", "for", "function", "if", + "in", "instanceof", "new", "return", "switch", "throw", "try", "typeof", "var", "void", "while", "with", + + // future reserved words + "class", "const", "enum", "export", "extends", "import", "super", + + // as future reserved words in strict mode + "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield", + + // additional reserved words + "null", "true", "false", + + // disallowed as variable names in strict mode + "eval", "arguments", + + // global identifiers usually declared in a typical JS interpreter + "NaN", "isNaN", "Infinity", "undefined", + + "Error", "Object", "Number", + + "Math", "String", "Boolean", "Date", "Array", "RegExp", "JSON", + + // global identifiers usually declared in know environments (node.js, browser, require.js, WebWorkers, etc) + "require", "define", "module", "window", "self" +) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt index 2834e8f6cae..1da0f2da9b8 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt @@ -6,8 +6,9 @@ package org.jetbrains.kotlin.ir.backend.js.utils import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.types.isNullableAny +import org.jetbrains.kotlin.ir.util.isTopLevelDeclaration import org.jetbrains.kotlin.name.Name fun TODO(element: IrElement): Nothing = TODO(element::class.java.simpleName + " is not supported yet here") @@ -17,3 +18,10 @@ fun IrFunction.isEqualsInheritedFromAny() = dispatchReceiverParameter != null && valueParameters.size == 1 && valueParameters[0].type.isNullableAny() + +fun IrDeclaration.hasStaticDispatch() = when (this) { + is IrSimpleFunction -> dispatchReceiverParameter == null + is IrProperty -> isTopLevelDeclaration + is IrField -> isStatic + else -> true +} \ No newline at end of file diff --git a/js/js.translator/testData/box/inheritance/fromNestedNativeClass.kt b/js/js.translator/testData/box/inheritance/fromNestedNativeClass.kt index 13eb4d566e5..4ec0ca765ce 100644 --- a/js/js.translator/testData/box/inheritance/fromNestedNativeClass.kt +++ b/js/js.translator/testData/box/inheritance/fromNestedNativeClass.kt @@ -1,4 +1,5 @@ // EXPECTED_REACHABLE_NODES: 1288 +// IGNORE_BACKEND: JS_IR // FILE: foo.kt package foo diff --git a/js/js.translator/testData/box/jsName/classes.kt b/js/js.translator/testData/box/jsName/classes.kt index 408cf43816e..fbca5b4c75d 100644 --- a/js/js.translator/testData/box/jsName/classes.kt +++ b/js/js.translator/testData/box/jsName/classes.kt @@ -1,3 +1,4 @@ +// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1294 @JsName("AA") object A { diff --git a/js/js.translator/testData/box/jsName/jsNamePropertyAccessors.kt b/js/js.translator/testData/box/jsName/jsNamePropertyAccessors.kt index c39b36e0501..f0fc75d4fba 100644 --- a/js/js.translator/testData/box/jsName/jsNamePropertyAccessors.kt +++ b/js/js.translator/testData/box/jsName/jsNamePropertyAccessors.kt @@ -1,4 +1,5 @@ // EXPECTED_REACHABLE_NODES: 1285 +// IGNORE_BACKEND: JS_IR package foo external class A { diff --git a/js/js.translator/testData/box/multiModule/privateInterfaceNameClash.kt b/js/js.translator/testData/box/multiModule/privateInterfaceNameClash.kt index 6a0514bc950..92878a92510 100644 --- a/js/js.translator/testData/box/multiModule/privateInterfaceNameClash.kt +++ b/js/js.translator/testData/box/multiModule/privateInterfaceNameClash.kt @@ -1,4 +1,4 @@ -// IGNORE_BACKEND: JS_IR +// KJS_WITH_FULL_RUNTIME // EXPECTED_REACHABLE_NODES: 1819 // MODULE: lib1 // FILE: lib1.kt @@ -43,8 +43,10 @@ fun box(): String { val b = Derived2() if (b.bar() != "B.foo") return "fail2: ${b.bar()}" - checkJsNames("foo", a) - checkJsNames("foo", b) + if (testUtils.isLegacyBackend()) { + checkJsNames("foo", a) + checkJsNames("foo", b) + } return "OK" } \ No newline at end of file diff --git a/js/js.translator/testData/box/multiModule/privateNameClash.kt b/js/js.translator/testData/box/multiModule/privateNameClash.kt index 5c862dfe9f5..2c7b3b9f3eb 100644 --- a/js/js.translator/testData/box/multiModule/privateNameClash.kt +++ b/js/js.translator/testData/box/multiModule/privateNameClash.kt @@ -1,3 +1,4 @@ +// KJS_WITH_FULL_RUNTIME // IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1805 // MODULE: lib diff --git a/js/js.translator/testData/box/nameClashes/jsNameAndPrivate.kt b/js/js.translator/testData/box/nameClashes/jsNameAndPrivate.kt index 5316a9e331f..4e96c4de862 100644 --- a/js/js.translator/testData/box/nameClashes/jsNameAndPrivate.kt +++ b/js/js.translator/testData/box/nameClashes/jsNameAndPrivate.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1284 package foo diff --git a/js/js.translator/testData/box/nameClashes/nativeAndTopLevelFunction.kt b/js/js.translator/testData/box/nameClashes/nativeAndTopLevelFunction.kt index ea17e991aad..48c80f5fadb 100644 --- a/js/js.translator/testData/box/nameClashes/nativeAndTopLevelFunction.kt +++ b/js/js.translator/testData/box/nameClashes/nativeAndTopLevelFunction.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1282 package test diff --git a/js/js.translator/testData/box/nameClashes/nativeDeclarationAndLocalVar.kt b/js/js.translator/testData/box/nameClashes/nativeDeclarationAndLocalVar.kt index 375171ca058..241166eac60 100644 --- a/js/js.translator/testData/box/nameClashes/nativeDeclarationAndLocalVar.kt +++ b/js/js.translator/testData/box/nameClashes/nativeDeclarationAndLocalVar.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1282 package test diff --git a/js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt b/js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt index b51dbdae568..a3503a3ca37 100644 --- a/js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt +++ b/js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1285 // MODULE: lib // FILE: lib.kt diff --git a/js/js.translator/testData/box/reflection/external.kt b/js/js.translator/testData/box/reflection/external.kt index a842405f13b..6035b58fb3d 100644 --- a/js/js.translator/testData/box/reflection/external.kt +++ b/js/js.translator/testData/box/reflection/external.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1221 external class A diff --git a/libraries/stdlib/js/src/kotlin/collections/InternalMap.kt b/libraries/stdlib/js/src/kotlin/collections/InternalMap.kt index 84747566456..6d20050ea66 100644 --- a/libraries/stdlib/js/src/kotlin/collections/InternalMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/InternalMap.kt @@ -19,11 +19,11 @@ internal interface InternalMap : MutableIterable