[JS IR BE] New name generator

This commit is contained in:
Svyatoslav Kuzmich
2019-04-04 15:07:00 +03:00
parent a16ca5e66c
commit 977d3ef840
34 changed files with 836 additions and 529 deletions
@@ -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?
@@ -55,9 +55,12 @@ class JsIrBackendContext(
override var inVerbosePhase: Boolean = false
lateinit var externalPackageFragment: IrPackageFragment
lateinit var bodilessBuiltInsPackageFragment: IrPackageFragment
val externalNestedClasses = mutableListOf<IrClass>()
val packageLevelJsModules = mutableListOf<IrFile>()
val declarationLevelJsModules = mutableListOf<IrDeclaration>()
val declarationLevelJsModules = mutableListOf<IrDeclarationWithName>()
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<IrFunction>().atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
continuationClass.owner.declarations.filterIsInstance<IrSimpleFunction>().atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
?: continuationClass.owner.declarations.filterIsInstance<IrProperty>().atMostOne { it.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!!
return contextGetter.symbol
}
@@ -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()
@@ -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
}
}
}
@@ -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<JsStatement, JsGenerationContext> {
override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsStatement {
@@ -33,7 +34,7 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatemen
}
override fun visitField(declaration: IrField, context: JsGenerationContext): JsStatement {
val fieldName = context.getNameForSymbol(declaration.symbol)
val fieldName = context.getNameForField(declaration)
if (declaration.isExternal) return JsEmpty
@@ -6,10 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
@@ -64,26 +61,38 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
}
override fun visitGetField(expression: IrGetField, context: JsGenerationContext): JsExpression {
if (expression.symbol.isBound) {
val fieldParent = expression.symbol.owner.parent
if (fieldParent is IrClass && fieldParent.isInline) {
return expression.receiver!!.accept(this, context)
}
val symbol = expression.symbol
val field = symbol.owner
val fieldParent = field.parent
if (fieldParent is IrClass && field.isEffectivelyExternal()) {
// External fields are only allowed in external enums
assert(fieldParent.isEnumClass)
return JsNameRef(
field.getJsNameOrKotlinName().identifier,
context.getNameForClass(fieldParent).makeRef()
)
}
val fieldName = context.getNameForSymbol(expression.symbol)
if (fieldParent is IrClass && fieldParent.isInline) {
return expression.receiver!!.accept(this, context)
}
val fieldName = context.getNameForField(field)
return JsNameRef(fieldName, expression.receiver?.accept(this, context))
}
override fun visitGetValue(expression: IrGetValue, context: JsGenerationContext): JsExpression =
context.getNameForSymbol(expression.symbol).makeRef()
context.getNameForValueDeclaration(expression.symbol.owner).makeRef()
override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext) = when (expression.symbol.owner.kind) {
ClassKind.OBJECT -> {
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<JsEx
override fun visitSetField(expression: IrSetField, context: JsGenerationContext): JsExpression {
val fieldName = context.getNameForSymbol(expression.symbol)
val fieldName = context.getNameForField(expression.symbol.owner)
val dest = JsNameRef(fieldName, expression.receiver?.accept(this, context))
val source = expression.value.accept(this, context)
return jsAssignment(dest, source)
}
override fun visitSetVariable(expression: IrSetVariable, context: JsGenerationContext): JsExpression {
val ref = JsNameRef(context.getNameForSymbol(expression.symbol))
val ref = JsNameRef(context.getNameForValueDeclaration(expression.symbol.owner))
val value = expression.value.accept(this, context)
return JsBinaryOperation(JsBinaryOperator.ASG, ref, value)
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
val classNameRef = context.getNameForSymbol(expression.symbol).makeRef()
val classNameRef = context.getNameForConstructor(expression.symbol.owner).makeRef()
val callFuncRef = JsNameRef(Namer.CALL_FUNCTION, classNameRef)
val fromPrimary = context.currentFunction is IrConstructor
val thisRef =
if (fromPrimary) JsThisRef() else context.getNameForSymbol(context.currentFunction!!.valueParameters.last().symbol).makeRef()
if (fromPrimary) JsThisRef() else context.getNameForValueDeclaration(context.currentFunction!!.valueParameters.last()).makeRef()
val arguments = translateCallArguments(expression, context)
val constructor = expression.symbol.owner
@@ -139,9 +148,9 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
// Transform external property accessor call
// @JsName-annotated external property accessors are translated as function calls
if (function is IrSimpleFunction && function.getJsName() == null) {
val property = function.correspondingProperty
val property = function.correspondingPropertySymbol?.owner
if (property != null && property.isEffectivelyExternal()) {
val nameRef = JsNameRef(context.getNameForDeclaration(property), jsDispatchReceiver)
val nameRef = JsNameRef(context.getNameForProperty(property), jsDispatchReceiver)
return when (function) {
property.getter -> nameRef
property.setter -> jsAssignment(nameRef, arguments.single())
@@ -154,13 +163,18 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
return JsInvocation(jsDispatchReceiver!!, arguments)
}
expression.superQualifierSymbol?.let {
val (target, owner) = if (it.owner.isInterface) {
val impl = (symbol.owner as IrSimpleFunction).resolveFakeOverride()!!
expression.superQualifierSymbol?.let { superQualifier ->
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<JsEx
val varargParameterIndex = function.valueParameters.indexOfFirst { it.varargElementType != null }
val isExternalVararg = function.isEffectivelyExternal() && varargParameterIndex != -1
val symbolName = context.getNameForSymbol(symbol)
val ref = if (function is IrSimpleFunction && jsDispatchReceiver != null) JsNameRef(symbolName, jsDispatchReceiver) else JsNameRef(
symbolName
)
return if (function is IrConstructor) {
if (function is IrConstructor) {
// Inline class primary constructor takes a single value of to
// initialize underlying property.
// TODO: Support initialization block
val klass = function.parentAsClass
if (klass.isInline) {
return if (klass.isInline) {
assert(function.isPrimary) {
"Inline class secondary constructors must be lowered into static methods"
}
// Argument value constructs unboxed inline class instance
arguments.single()
} else {
val ref = when {
klass.isEffectivelyExternal() ->
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<JsEx
}
)
if (function is IrSimpleFunction && jsDispatchReceiver != null) {
if (jsDispatchReceiver != null) {
// TODO: Do not create IIFE when receiver expression is simple or has no side effects
// TODO: Do not create IIFE at all? (Currently there is no reliable way to create temporary variable in current scope)
val receiverName = context.currentScope.declareFreshName("\$externalVarargReceiverTmp")
@@ -43,11 +43,11 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
}
override fun visitBreak(jump: IrBreak, context: JsGenerationContext): JsStatement {
return JsBreak(context.getNameForLoop(jump.loop)?.makeRef())
return JsBreak(context.getNameForLoop(jump.loop)?.let { JsNameRef(it) })
}
override fun visitContinue(jump: IrContinue, context: JsGenerationContext): JsStatement {
return JsContinue(context.getNameForLoop(jump.loop)?.makeRef())
return JsContinue(context.getNameForLoop(jump.loop)?.let { JsNameRef(it) })
}
override fun visitReturn(expression: IrReturn, context: JsGenerationContext): JsStatement {
@@ -59,7 +59,7 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
}
override fun visitVariable(declaration: IrVariable, context: JsGenerationContext): JsStatement {
val varName = context.getNameForSymbol(declaration.symbol)
val varName = context.getNameForValueDeclaration(declaration)
return jsVar(varName, declaration.initializer, context)
}
@@ -81,7 +81,7 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
val jsTryBlock = aTry.tryResult.accept(this, context).asBlock()
val jsCatch = aTry.catches.singleOrNull()?.let {
val name = context.getNameForSymbol(it.catchParameter.symbol)
val name = context.getNameForValueDeclaration(it.catchParameter)
val jsCatchBlock = it.result.accept(this, context)
JsCatch(context.currentScope, name.ident, jsCatchBlock)
}
@@ -128,14 +128,14 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
override fun visitWhileLoop(loop: IrWhileLoop, context: JsGenerationContext): JsStatement {
//TODO what if body null?
val label = context.getNameForLoop(loop)
val label = context.getNameForLoop(loop)?.let { context.staticContext.rootScope.declareName(it) }
val loopStatement = JsWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
return label?.let { JsLabel(it, loopStatement) } ?: loopStatement
}
override fun visitDoWhileLoop(loop: IrDoWhileLoop, context: JsGenerationContext): JsStatement {
//TODO what if body null?
val label = context.getNameForLoop(loop)
val label = context.getNameForLoop(loop)?.let { context.staticContext.rootScope.declareName(it) }
val loopStatement =
JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
return label?.let { JsLabel(it, loopStatement) } ?: loopStatement
@@ -12,15 +12,20 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.util.isObject
import org.jetbrains.kotlin.js.backend.ast.JsFunction
open class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer<JsFunction, JsGenerationContext> {
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer<JsFunction, JsGenerationContext> {
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)
}
@@ -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<JsNode, Nothing?> {
class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
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<JsStatement>()
// 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<JsStatement> {
return module.files
.flatMap { it.declarations }
.asSequence()
.filterIsInstance<IrDeclarationWithVisibility>()
.filter { it.visibility == Visibilities.PUBLIC }
.filter { !it.isEffectivelyExternal() }
.filterIsInstance<IrSymbolOwner>()
.map { declaration ->
val name = context.getNameForSymbol(declaration.symbol)
val exports = mutableListOf<JsExpressionStatement>()
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<MutableList<JsStatement>, List<JsImportedModule>> {
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<IrDeclarationWithName>()
.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 =
@@ -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
}
)
@@ -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<IrConstructor>().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))
}
}
}
@@ -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 {
@@ -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?
}
@@ -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")
}
}
}
@@ -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
@@ -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()
}
}
@@ -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<JsName, JsClassModel>()
@@ -31,8 +28,4 @@ class JsStaticContext(
.filterIsInstance<IrSimpleFunction>().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)
}
@@ -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?
}
@@ -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<T>(
val parent: NameTable<T>? = null,
private val reserved: MutableSet<String> = mutableSetOf()
) {
var finished = false
val names = mutableMapOf<T, String>()
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<IrDeclaration>.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<IrPackageFragment>) {
private val globalNames: NameTable<IrDeclaration>
private val memberNames: NameTable<IrDeclaration>
private val localNames = mutableMapOf<IrDeclaration, NameTable<IrDeclaration>>()
private val loopNames = mutableMapOf<IrLoop, String>()
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<IrLoop>()
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<IrField, JsName>()
private val functionCache = mutableMapOf<IrFunction, JsName>()
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("")
}
@@ -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 = "<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 = "<this>"
val SET_SPECIAL_NAME = "<set-?>"
}
@@ -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<IrDeclaration, JsName>()
private val loopCache = mutableMapOf<IrLoop, JsName>()
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("")
}
@@ -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<String>()
val memberNames = mutableSetOf<String>()
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"
)
@@ -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
}
@@ -1,4 +1,5 @@
// EXPECTED_REACHABLE_NODES: 1288
// IGNORE_BACKEND: JS_IR
// FILE: foo.kt
package foo
+1
View File
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1294
@JsName("AA") object A {
@@ -1,4 +1,5 @@
// EXPECTED_REACHABLE_NODES: 1285
// IGNORE_BACKEND: JS_IR
package foo
external class A {
@@ -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"
}
@@ -1,3 +1,4 @@
// KJS_WITH_FULL_RUNTIME
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1805
// MODULE: lib
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1284
package foo
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1282
package test
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1282
package test
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1285
// MODULE: lib
// FILE: lib.kt
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1221
external class A
@@ -19,11 +19,11 @@ internal interface InternalMap<K, V> : MutableIterable<MutableMap.MutableEntry<K
fun clear(): Unit
fun createJsMap(): dynamic {
val result = js("Object.create(null)")
val newJsMap = js("Object.create(null)")
// force to switch object representation to dictionary mode
// Using js-function due to JS_IR limitations
js("result[\"foo\"] = 1")
js("delete result[\"foo\"]")
return result
js("newJsMap[\"foo\"] = 1")
js("delete newJsMap[\"foo\"]")
return newJsMap
}
}