diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt index 0aacc9e7c43..c3dea0b1c17 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt @@ -210,8 +210,7 @@ object ConstructorCallCase : FunctionCallCase() { val arguments = argumentsInfo.getArguments() val constructorDescriptor = callableDescriptor as ConstructorDescriptor - val classDescriptor = constructorDescriptor.containingDeclaration - val closure = context.getLocalClassClosure(classDescriptor) + val closure = context.getLocalClassClosure(constructorDescriptor) var closureArgs = emptyList() if (closure != null) { closureArgs = closure.asSequence().map { context.getParameterNameRefForInvocation(it) }.toList() diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index 74d27862d95..403f31a215b 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -98,7 +98,7 @@ public final class StaticContext { private final Map scopeToFunction = Maps.newHashMap(); @NotNull - private final Map> localClassesClosure = Maps.newHashMap(); + private final Map> localClassesClosure = Maps.newHashMap(); @NotNull private final Config config; @@ -148,7 +148,7 @@ public final class StaticContext { } @NotNull - public JsScope getRootScope() { + private JsScope getRootScope() { return rootScope; } @@ -650,12 +650,12 @@ public final class StaticContext { } } - public void putLocalClassClosure(@NotNull ClassDescriptor localClass, @NotNull List closure) { + public void putLocalClassClosure(@NotNull MemberDescriptor localClass, @NotNull List closure) { localClassesClosure.put(localClass, Lists.newArrayList(closure)); } @Nullable - public List getLocalClassClosure(@NotNull ClassDescriptor localClass) { + public List getLocalClassClosure(@NotNull MemberDescriptor localClass) { List result = localClassesClosure.get(localClass); return result != null ? Lists.newArrayList(result) : null; } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index 4512f05a232..f69adee3c41 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -414,11 +414,6 @@ public class TranslationContext { return getDefinitionPlace().define(suggestedName, expression); } - @NotNull - public JsNameRef define(JsName name, JsExpression expression) { - return getDefinitionPlace().define(name, expression); - } - @Nullable private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) { if (usageTracker != null) { @@ -440,9 +435,10 @@ public class TranslationContext { private boolean shouldCaptureViaThis() { if (declarationDescriptor == null) return false; - if (DescriptorUtils.isDescriptorWithLocalVisibility(declarationDescriptor)) { - return !(declarationDescriptor instanceof FunctionDescriptor); - } + if (DescriptorUtils.isDescriptorWithLocalVisibility(declarationDescriptor)) return false; + if (declarationDescriptor instanceof ConstructorDescriptor && + DescriptorUtils.isDescriptorWithLocalVisibility(declarationDescriptor.getContainingDeclaration())) return false; + return true; } @@ -451,12 +447,12 @@ public class TranslationContext { return declarationDescriptor; } - public void putLocalClassClosure(@NotNull ClassDescriptor localClass, @NotNull List closure) { + public void putLocalClassClosure(@NotNull MemberDescriptor localClass, @NotNull List closure) { staticContext.putLocalClassClosure(localClass, closure); } @Nullable - public List getLocalClassClosure(@NotNull ClassDescriptor localClass) { + public List getLocalClassClosure(@NotNull MemberDescriptor localClass) { return staticContext.getLocalClassClosure(localClass); } @@ -468,11 +464,6 @@ public class TranslationContext { return getNameForDescriptor(descriptor).makeRef(); } - @NotNull - public JsScope getRootScope() { - return staticContext.getRootScope(); - } - @Nullable public JsName getOuterClassReference(ClassDescriptor descriptor) { DeclarationDescriptor container = descriptor.getContainingDeclaration(); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt index 1dcd94090f4..41645fe5a9b 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt @@ -22,6 +22,7 @@ import com.google.dart.compiler.backend.js.ast.JsName import org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName import com.google.dart.compiler.backend.js.ast.JsScope import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils.getParentOfType import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver private val CAPTURED_RECEIVER_NAME_PREFIX : String = "this$" @@ -38,6 +39,9 @@ class UsageTracker( val capturedDescriptorToJsName: Map get() = captured + val capturedDescriptors: Set + get() = captured.keys + fun used(descriptor: DeclarationDescriptor) { if (isCaptured(descriptor)) return @@ -61,13 +65,62 @@ class UsageTracker( private fun captureIfNeed(descriptor: DeclarationDescriptor?) { if (descriptor == null || isCaptured(descriptor) || isAncestor(containingDescriptor, descriptor, /* strict = */ true) || - isSingletonReceiver(descriptor)) return + isReceiverAncestor(descriptor) || isSingletonReceiver(descriptor)) return parent?.captureIfNeed(descriptor) captured[descriptor] = descriptor.getJsNameForCapturedDescriptor() } + /** + * We shouldn't capture current `this` or outer `this`. Assuming `C` is current translating class, + * we have `descriptor == A::this` in the following cases: + * * `A == C` + * * `C` in inner class of `A` + * * `A <: C` + * * among outer classes of `C` there is `T` such that `A <: T` + * + * If fact, the latter case is the generalization of all previous cases, assuming that `is inner class of` and `<:` relations + * are reflective. All this cases allow to refer to `this` directly or via sequence of `outer` fields. + * + * Note that the continuous sequence of inner classes may be interrupted by non-class descriptor. This means that + * the last class of the sequence if a local class. We stop there, since this means that the next class in the sequence + * is referred by closure variable rather than by dedicated `$outer` field. + * + * The nested classes are out of scope, since nested class can't refer to outer's class `this`, thus frontend will + * never generate ReceiverParameterDescriptor for this case. + */ + private fun isReceiverAncestor(descriptor: DeclarationDescriptor): Boolean { + if (descriptor !is ReceiverParameterDescriptor) return false + if (containingDescriptor !is ClassDescriptor && DescriptorUtils.isDescriptorWithLocalVisibility(containingDescriptor)) return false + + val currentClass = DescriptorUtils.getClassDescriptorForType(descriptor.type) + val containingClass = getParentOfType(containingDescriptor, ClassDescriptor::class.java, false) ?: return false + + for (outerDeclaration in generateSequence(containingClass) { it.containingDeclaration as? ClassDescriptor }) { + if (DescriptorUtils.isSubclass(outerDeclaration, currentClass)) return true + } + + return false + } + + /** + * Test for the case like this: + * + * ``` + * object A { + * var x: Int + * + * class B { + * fun foo() { + * { x } + * } + * } + * } + * ``` + * + * We don't want to capture `A::this`, since we always can refer A by its FQN + */ private fun isSingletonReceiver(descriptor: DeclarationDescriptor): Boolean { if (descriptor !is ReceiverParameterDescriptor) return false @@ -75,12 +128,15 @@ class UsageTracker( if (value !is ImplicitReceiver) return false if (!DescriptorUtils.isObject(value.declarationDescriptor)) return false + + // This is workaround, since sometimes translator generates wrong expression for `this` expressions. + // Presumably, it's related to KT-11823 + // TODO: remove when issue gets fixed. if (containingDescriptor == value.declarationDescriptor) return false if (containingDescriptor !is ClassDescriptor) { - val containingClass = generateSequence(containingDescriptor) { it.containingDeclaration as? MemberDescriptor } - .first { it is ClassDescriptor } as? ClassDescriptor - if (containingClass != null && containingClass == value.declarationDescriptor) return false + val containingClass = getParentOfType(containingDescriptor, ClassDescriptor::class.java, false); + if (containingClass == value.declarationDescriptor) return false } return true @@ -99,7 +155,7 @@ class UsageTracker( } } -fun UsageTracker.getNameForCapturedDescriptor(descriptor: DeclarationDescriptor): JsName? = capturedDescriptorToJsName.get(descriptor) +fun UsageTracker.getNameForCapturedDescriptor(descriptor: DeclarationDescriptor): JsName? = capturedDescriptorToJsName[descriptor] fun UsageTracker.hasCapturedExceptContaining(): Boolean { val hasNotCaptured = diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt index 334d81b0326..6ef2cabb7e8 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt @@ -48,7 +48,6 @@ import org.jetbrains.kotlin.types.CommonSupertypes.topologicallySortSuperclasses import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.utils.identity -import java.util.* /** * Generates a definition of a single class. @@ -59,16 +58,25 @@ class ClassTranslator private constructor( ) : AbstractTranslator(context) { private val descriptor = getClassDescriptor(context.bindingContext(), classDeclaration) + private val invocationArguments = mutableListOf() + private val secondaryConstructors = mutableListOf() + private val secondaryConstructorProperties = mutableListOf() + private var primaryConstructor: ConstructorInfo? = null - private fun translate(declarationContext: TranslationContext = context()): JsInvocation { - return JsInvocation(context().namer().classCreateInvocation(descriptor), getClassCreateInvocationArguments(declarationContext)) + private fun translate(): List { + invocationArguments.clear() + getClassCreateInvocationArguments() + + val classNameRef = context().getNameForDescriptor(descriptor).makeRef() + val classCreation = JsInvocation(context().namer().classCreateInvocation(descriptor), invocationArguments) + + return listOf(JsPropertyInitializer(classNameRef, classCreation)) + secondaryConstructorProperties } private fun isTrait(): Boolean = descriptor.kind == ClassKind.INTERFACE - private fun getClassCreateInvocationArguments(declarationContext: TranslationContext): List { - var context = declarationContext - val invocationArguments = ArrayList() + private fun getClassCreateInvocationArguments() { + var context = context() val properties = SmartList() val staticProperties = SmartList() @@ -76,43 +84,37 @@ class ClassTranslator private constructor( val qualifiedReference = context.getQualifiedReference(descriptor) val scope = context().getScopeForDescriptor(descriptor) val definitionPlace = DefinitionPlace(scope as JsObjectScope, qualifiedReference, staticProperties) - context = context.newDeclaration(descriptor, definitionPlace) - context = fixContextForCompanionObjectAccessing(context) invocationArguments.add(getSuperclassReferences(context)) - val delegationTranslator = DelegationTranslator(classDeclaration, context) - var initializer: JsFunction? = null - if (!isTrait()) { - initializer = ClassInitializerTranslator(classDeclaration, context).generateInitializeMethod(delegationTranslator) - invocationArguments.add(initializer) - } - translatePropertiesAsConstructorParameters(context, properties) + val nonConstructorContext = context.innerWithUsageTracker(scope, descriptor) + val delegationTranslator = DelegationTranslator(classDeclaration, nonConstructorContext) + translatePropertiesAsConstructorParameters(nonConstructorContext, properties) val bodyVisitor = DeclarationBodyVisitor(properties, staticProperties) - bodyVisitor.traverseContainer(classDeclaration, context) + bodyVisitor.traverseContainer(classDeclaration, nonConstructorContext) delegationTranslator.generateDelegated(properties) + var enumFunction: JsFunction? = null if (isEnumClass(descriptor)) { val enumEntries = JsObjectLiteral(bodyVisitor.enumEntryList, true) - val function = simpleReturnFunction(context.getScopeForDescriptor(descriptor), enumEntries) - invocationArguments.add(function) + enumFunction = simpleReturnFunction(nonConstructorContext.getScopeForDescriptor(descriptor), enumEntries) } + translatePrimaryConstructor(context, delegationTranslator) + classDeclaration.getSecondaryConstructors().forEach { generateSecondaryConstructor(context, it) } generatedBridgeMethods(properties) + enumFunction?.let { invocationArguments += it } val dataClassGenerator = JsDataClassGenerator(classDeclaration, context, properties) - val tracker = context.usageTracker() - if (tracker != null && initializer != null && tracker.hasCapturedExceptContaining()) { - val captured = tracker.capturedDescriptorToJsName - val keysAsList = captured.keys.toList() - for ((i, key) in keysAsList.withIndex()) { - val name = captured[key]!! - initializer.parameters.add(i, JsParameter(name)) - initializer.body.statements.add(i, JsAstUtils.defineSimpleProperty(name.ident, name.makeRef())) - dataClassGenerator.addClosureVariable(name) - } - context.putLocalClassClosure(descriptor, keysAsList) + + inflateClosure() + for (constructor in allConstructors) { + addClosureParameters(constructor, nonConstructorContext, dataClassGenerator) + } + + for (constructor in allConstructors) { + constructor.superCallGenerator() } if (descriptor.isData) { @@ -122,28 +124,206 @@ class ClassTranslator private constructor( val hasStaticProperties = !staticProperties.isEmpty() if (!properties.isEmpty() || hasStaticProperties) { if (properties.isEmpty()) { - invocationArguments.add(JsLiteral.NULL) + invocationArguments += JsLiteral.NULL } else { // about "prototype" - see http://code.google.com/p/jsdoc-toolkit/wiki/TagLends - invocationArguments.add(JsDocComment(JsAstUtils.LENDS_JS_DOC_TAG, JsNameRef("prototype", qualifiedReference))) - invocationArguments.add(JsObjectLiteral(properties, true)) + invocationArguments += JsDocComment(JsAstUtils.LENDS_JS_DOC_TAG, JsNameRef("prototype", qualifiedReference)) + invocationArguments += JsObjectLiteral(properties, true) } } if (hasStaticProperties) { - invocationArguments.add(JsDocComment(JsAstUtils.LENDS_JS_DOC_TAG, qualifiedReference)) - invocationArguments.add(JsObjectLiteral(staticProperties, true)) + invocationArguments += JsDocComment(JsAstUtils.LENDS_JS_DOC_TAG, qualifiedReference) + invocationArguments += JsObjectLiteral(staticProperties, true) } - if (initializer != null && initializer.body.isEmpty) { - invocationArguments.replaceAll { if (it == initializer) JsLiteral.NULL else it } + if (primaryConstructor != null && primaryConstructor!!.function.body.isEmpty) { + invocationArguments[invocationArguments.indexOf(primaryConstructor!!.function)] = JsLiteral.NULL } - - return invocationArguments } - private fun fixContextForCompanionObjectAccessing(context: TranslationContext): TranslationContext { - return context + private fun translatePrimaryConstructor(classContext: TranslationContext, delegationTranslator: DelegationTranslator) { + if (isTrait()) return + + var constructorContext = classContext.innerWithUsageTracker(classContext.scope(), descriptor) + val initializer = ClassInitializerTranslator(classDeclaration, constructorContext).generateInitializeMethod(delegationTranslator) + invocationArguments += initializer + + this.primaryConstructor = ConstructorInfo(initializer, constructorContext, descriptor) + } + + private fun generateSecondaryConstructor(classContext: TranslationContext, constructor: KtSecondaryConstructor) { + // Prepare + val constructorDescriptor = BindingUtils.getDescriptorForElement(classContext.bindingContext(), constructor) as ConstructorDescriptor + val classDescriptor = constructorDescriptor.containingDeclaration + + var context = classContext.innerWithUsageTracker(classContext.scope(), constructorDescriptor) + + val constructorScope = context.getScopeForDescriptor(constructorDescriptor) + val thisName = constructorScope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME) + val thisNameRef = thisName.makeRef() + val receiverDescriptor = classDescriptor.thisAsReceiverParameter + + context = context.newDeclaration(constructorDescriptor.containingDeclaration, context.definitionPlace) + context = context.newDeclaration(constructorDescriptor, context.definitionPlace) + context = context.innerContextWithAliased(receiverDescriptor, thisNameRef) + + val outerName = context.getOuterClassReference(classDescriptor); + val outerClass = DescriptorUtils.getContainingClass(classDescriptor) + if (outerClass != null && outerName != null) { + val outerClassRef = outerClass.thisAsReceiverParameter + context = context.innerContextWithAliased(outerClassRef, outerName.makeRef()) + } + + // Translate constructor body + val constructorInitializer = FunctionTranslator.newInstance(constructor, context).translateAsMethod() + val constructorFunction = constructorInitializer.valueExpr as JsFunction + + // Translate super/this call + val superCallGenerators = mutableListOf<(MutableList) -> Unit>() + val referenceToClass = context.getQualifiedReference(classDescriptor) + + superCallGenerators += { it += FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, context) } + + val createInstance = Namer.createObjectWithPrototypeFrom(referenceToClass) + val instanceVar = JsAstUtils.assignment(thisNameRef, JsAstUtils.or(thisNameRef, createInstance)).makeStmt() + superCallGenerators += { it += instanceVar } + + // Add parameter for outer instance + val leadingArgs = mutableListOf() + + if (outerName != null) { + constructorFunction.parameters.add(0, JsParameter(outerName)) + leadingArgs += outerName.makeRef() + } + + constructorFunction.parameters += JsParameter(thisName) + + // Generate super/this call to insert to beginning of the function + val resolvedCall = BindingContextUtils.getDelegationConstructorCall(context.bindingContext(), constructorDescriptor) + val delegationClassDescriptor = resolvedCall?.resultingDescriptor?.containingDeclaration + val superCall = if (resolvedCall != null && !KotlinBuiltIns.isAny(delegationClassDescriptor!!)) { + CallTranslator.translate(context, resolvedCall) + } + else { + null + } + + if (superCall != null) { + superCallGenerators += { + it += CallTranslator.translate(context, resolvedCall!!).toInvocationWith(leadingArgs, thisNameRef).makeStmt() + } + } + + val delegationCtorInTheSameClass = delegationClassDescriptor == classDescriptor + if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) { + superCallGenerators += { + val usageTracker = context.usageTracker()!! + val closure = context.getLocalClassClosure(classDescriptor).orEmpty().map { + usageTracker.capturedDescriptorToJsName[it]?.makeRef() as? JsExpression ?: JsLiteral.NULL + } + it += JsInvocation(Namer.getFunctionCallRef(referenceToClass), listOf(thisNameRef) + closure + leadingArgs).makeStmt() + } + } + + constructorFunction.body.statements += JsReturn(thisNameRef) + + val compositeSuperCallGenerator: () -> Unit = { + val additionalStatements = mutableListOf() + for (partGenerator in superCallGenerators) { + partGenerator(additionalStatements) + } + constructorFunction.body.statements.addAll(0, additionalStatements) + } + + secondaryConstructors += ConstructorInfo(constructorFunction, context, constructorDescriptor, compositeSuperCallGenerator) + secondaryConstructorProperties += constructorInitializer + } + + private val allConstructors: Sequence + get() { + val primary = primaryConstructor + return if (primary != null) sequenceOf(primary) + secondaryConstructors else secondaryConstructors.asSequence() + } + + private fun inflateClosure() { + // Build map that maps constructor to all constructors called via this() + val constructorMap = allConstructors.map { Pair(it.descriptor, it) }.toMap() + val primaryConstructor = this.primaryConstructor + + val thisCalls = allConstructors.map { + val set = mutableSetOf() + val descriptor = it.descriptor + if (descriptor is ConstructorDescriptor) { + val resolvedCall = BindingContextUtils.getDelegationConstructorCall(context().bindingContext(), descriptor) + if (resolvedCall != null) { + val callee = constructorMap[resolvedCall.resultingDescriptor] + if (callee != null) { + set += callee + } + } + } + if (primaryConstructor != null && primaryConstructor != it) { + set += primaryConstructor + } + Pair(it, set) + }.toMap() + + // Sort graph of constructors + val sortedConstructors = mutableListOf() + val visitedConstructors = mutableSetOf() + + fun sort(constructor: ConstructorInfo) { + if (!visitedConstructors.add(constructor)) return + + sortedConstructors += constructor + for (thisCall in thisCalls[constructor].orEmpty()) { + sort(thisCall) + } + } + + allConstructors.forEach(::sort) + + // Inflate closure + for (constructor in sortedConstructors) { + val usageTracker = constructor.context.usageTracker() ?: continue + for (thisCall in thisCalls[constructor].orEmpty()) { + val callUsageTracker = thisCall.context.usageTracker() ?: continue + callUsageTracker.capturedDescriptors.forEach { usageTracker.used(it) } + } + } + } + + private fun addClosureParameters(constructor: ConstructorInfo, nonConstructorContext: TranslationContext, + dataClassGenerator: JsDataClassGenerator) { + val usageTracker = constructor.context.usageTracker()!! + val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!! + + val nonConstructorCapturedVars = nonConstructorUsageTracker.capturedDescriptors + val constructorCapturedVars = usageTracker.capturedDescriptors + + val capturedVars = (nonConstructorCapturedVars.asSequence() + + constructorCapturedVars.asSequence().filter { it !in nonConstructorCapturedVars }).toList() + + val descriptor = constructor.descriptor + nonConstructorContext.putLocalClassClosure(descriptor, capturedVars) + if (descriptor is ClassDescriptor) { + val primaryConstructor = descriptor.constructors.find { it.isPrimary } + if (primaryConstructor != null) { + nonConstructorContext.putLocalClassClosure(primaryConstructor, capturedVars) + } + } + + val function = constructor.function + for ((i, capturedVar) in capturedVars.withIndex()) { + val name = usageTracker.capturedDescriptorToJsName[capturedVar] ?: + nonConstructorUsageTracker.capturedDescriptorToJsName[capturedVar]!! + function.parameters.add(i, JsParameter(name)) + if (capturedVar in nonConstructorUsageTracker.capturedDescriptors && constructor == primaryConstructor) { + function.body.statements.add(i, JsAstUtils.defineSimpleProperty(name.ident, name.makeRef())) + dataClassGenerator.addClosureVariable(name) + } + } } private fun getSuperclassReferences(declarationContext: TranslationContext): JsExpression { @@ -167,16 +347,20 @@ class ClassTranslator private constructor( return listOf(getClassReference(supertypeDescriptor)) } - val supertypeConstructors = HashSet() + val supertypeConstructors = mutableSetOf() for (type in supertypes) { - supertypeConstructors.add(type.constructor) + supertypeConstructors += type.constructor } - val sortedAllSuperTypes = topologicallySortSuperclassesAndRecordAllInstances(descriptor.defaultType, HashMap>(), HashSet()) - val supertypesRefs = ArrayList() + val sortedAllSuperTypes = topologicallySortSuperclassesAndRecordAllInstances( + descriptor.defaultType, + mutableMapOf>(), + mutableSetOf() + ) + val supertypesRefs = mutableListOf() for (typeConstructor in sortedAllSuperTypes) { if (supertypeConstructors.contains(typeConstructor)) { val supertypeDescriptor = getClassDescriptorForTypeConstructor(typeConstructor) - supertypesRefs.add(getClassReference(supertypeDescriptor)) + supertypesRefs += getClassReference(supertypeDescriptor) } } return supertypesRefs @@ -186,7 +370,8 @@ class ClassTranslator private constructor( return context().getQualifiedReference(superClassDescriptor) } - private fun translatePropertiesAsConstructorParameters(classDeclarationContext: TranslationContext, result: MutableList) { + private fun translatePropertiesAsConstructorParameters(classDeclarationContext: TranslationContext, + result: MutableList) { for (parameter in getPrimaryConstructorParameters(classDeclaration)) { val descriptor = getPropertyDescriptorForConstructorParameter(bindingContext(), parameter) if (descriptor != null) { @@ -206,7 +391,7 @@ class ClassTranslator private constructor( private fun generateBridgesToTraitImpl(properties: MutableList) { for (entry in CodegenUtil.getNonPrivateTraitMethods(descriptor).entries) { if (!areNamesEqual(entry.key, entry.value)) { - properties.add(generateDelegateCall(entry.value, entry.key, JsLiteral.THIS, context())) + properties += generateDelegateCall(entry.value, entry.key, JsLiteral.THIS, context()) } } } @@ -231,7 +416,7 @@ class ClassTranslator private constructor( if (fromDescriptor.kind.isReal && fromDescriptor.modality != Modality.ABSTRACT && !toDescriptor.kind.isReal) return - properties.add(generateDelegateCall(fromDescriptor, toDescriptor, JsLiteral.THIS, context())) + properties += generateDelegateCall(fromDescriptor, toDescriptor, JsLiteral.THIS, context()) } private fun areNamesEqual(first: FunctionDescriptor, second: FunctionDescriptor): Boolean { @@ -242,111 +427,14 @@ class ClassTranslator private constructor( companion object { fun translate(classDeclaration: KtClassOrObject, context: TranslationContext): List { - val result = arrayListOf() - - val classDescriptor = getClassDescriptor(context.bindingContext(), classDeclaration) - val classNameRef = context.getNameForDescriptor(classDescriptor).makeRef() - val classCreation = generateClassCreation(classDeclaration, context) - - result.add(JsPropertyInitializer(classNameRef, classCreation)) - - classDeclaration.getSecondaryConstructors().forEach { - result.add(generateSecondaryConstructor(it, context)) - } - - return result - } - - private fun generateClassCreation(classDeclaration: KtClassOrObject, context: TranslationContext): JsInvocation { return ClassTranslator(classDeclaration, context).translate() } - - private fun generateSecondaryConstructor(constructor: KtSecondaryConstructor, context: TranslationContext): JsPropertyInitializer { - // Prepare - val constructorDescriptor = BindingUtils.getDescriptorForElement(context.bindingContext(), constructor) as ConstructorDescriptor - val classDescriptor = constructorDescriptor.containingDeclaration - - val constructorScope = context.getScopeForDescriptor(constructorDescriptor) - val thisName = constructorScope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME) - val thisNameRef = thisName.makeRef() - val receiverDescriptor = classDescriptor.thisAsReceiverParameter - - var translationContext = context - translationContext = translationContext.newDeclaration(constructorDescriptor.containingDeclaration, - translationContext.definitionPlace) - translationContext = translationContext.newDeclaration(constructorDescriptor, translationContext.definitionPlace) - translationContext = translationContext.innerContextWithAliased(receiverDescriptor, thisNameRef) - - val closure = translationContext.getLocalClassClosure(classDescriptor) - val usageTracker = context.usageTracker() - - val outerName = translationContext.getOuterClassReference(classDescriptor); - val outerClass = DescriptorUtils.getContainingClass(classDescriptor) - if (outerClass != null && outerName != null) { - val outerClassRef = outerClass.thisAsReceiverParameter - translationContext = translationContext.innerContextWithAliased(outerClassRef, outerName.makeRef()) - } - - // Translate constructor body - val constructorInitializer = FunctionTranslator.newInstance(constructor, translationContext).translateAsMethod() - val constructorFunction = constructorInitializer.valueExpr as JsFunction - - // Translate super/this call - val forAddToBeginning = arrayListOf() - - val referenceToClass = translationContext.getQualifiedReference(classDescriptor) - - forAddToBeginning.addAll(FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, translationContext)) - - val createInstance = Namer.createObjectWithPrototypeFrom(referenceToClass) - val instanceVar = JsAstUtils.assignment(thisNameRef, JsAstUtils.or(thisNameRef, createInstance)).makeStmt() - forAddToBeginning.add(instanceVar) - - val resolvedCall = BindingContextUtils.getDelegationConstructorCall(translationContext.bindingContext(), - constructorDescriptor) - val delegationClassDescriptor = resolvedCall?.resultingDescriptor?.containingDeclaration - val superCall = if (resolvedCall != null && !KotlinBuiltIns.isAny(delegationClassDescriptor!!)) { - CallTranslator.translate(translationContext, resolvedCall) - } - else { - null - } - - // Add parameters for closure and outer instance - val leadingArgs = mutableListOf() - - var additionalParameterIndex = 0 - if (closure != null && usageTracker != null) { - for (capturedValue in closure) { - val capturedName = usageTracker.capturedDescriptorToJsName[capturedValue]!! - constructorFunction.parameters.add(additionalParameterIndex++, JsParameter(capturedName)) - leadingArgs.add(capturedName.makeRef()) - } - } - - if (outerName != null) { - constructorFunction.parameters.add(additionalParameterIndex, JsParameter(outerName)) - leadingArgs.add(outerName.makeRef()) - } - - constructorFunction.parameters.add(JsParameter(thisName)) - - // Insert super/this call to beginning of function - if (superCall != null) { - forAddToBeginning.add(superCall.toInvocationWith(leadingArgs, thisNameRef).makeStmt()) - } - - val delegationCtorInTheSameClass = delegationClassDescriptor == classDescriptor - if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) { - forAddToBeginning.add(JsInvocation(Namer.getFunctionCallRef(referenceToClass), listOf(thisNameRef) + leadingArgs).makeStmt()) - } - - with(constructorFunction.body.statements) { - addAll(0, forAddToBeginning) - add(JsReturn(thisNameRef)) - } - - return constructorInitializer - } } + + private class ConstructorInfo( + val function: JsFunction, + val context: TranslationContext, + val descriptor: MemberDescriptor, + val superCallGenerator: (() -> Unit) = { } + ) } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.java index 3c69e10914d..233d58d46d5 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.java @@ -104,7 +104,7 @@ public class DeclarationBodyVisitor extends TranslatorVisitor { @Override public Void visitProperty(@NotNull KtProperty expression, TranslationContext context) { PropertyDescriptor propertyDescriptor = BindingUtils.getPropertyDescriptor(context.bindingContext(), expression); - context.newDeclaration(propertyDescriptor, context.getDefinitionPlace()); + context = context.newDeclaration(propertyDescriptor, context.getDefinitionPlace()); PropertyTranslatorKt.translateAccessors(propertyDescriptor, expression, result, context); return null; } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt index 963e6063c14..1df0d52c951 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt @@ -35,7 +35,6 @@ import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtSuperTypeListEntry import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry import org.jetbrains.kotlin.resolve.DescriptorUtils -import java.util.* class DelegationTranslator( private val classDeclaration: KtClassOrObject, @@ -49,7 +48,7 @@ class DelegationTranslator( classDeclaration.getSuperTypeListEntries().filterIsInstance(); private class Field (val name: String, val generateField: Boolean) - private val fields = HashMap() + private val fields = mutableMapOf() init { for (specifier in delegationBySpecifiers) { @@ -59,20 +58,20 @@ class DelegationTranslator( val propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, classDescriptor, bindingContext()) if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext())) { - fields.put(specifier, Field(propertyDescriptor!!.name.asString(), false)) + fields[specifier] = Field(propertyDescriptor!!.name.asString(), false) } else { val classFqName = DescriptorUtils.getFqName(classDescriptor) val typeFqName = DescriptorUtils.getFqName(descriptor) val delegateName = getMangledMemberNameForExplicitDelegation(Namer.getDelegatePrefix(), classFqName, typeFqName) - fields.put(specifier, Field(delegateName, true)) + fields[specifier] = Field(delegateName, true) } } } fun addInitCode(statements: MutableList) { for (specifier in delegationBySpecifiers) { - val field = fields.get(specifier)!! + val field = fields[specifier]!! if (field.generateField) { val expression = specifier.delegateExpression!! val delegateInitExpr = Translation.translateAsExpression(expression, context()) @@ -83,7 +82,7 @@ class DelegationTranslator( fun generateDelegated(properties: MutableList) { for (specifier in delegationBySpecifiers) { - generateDelegates(getSuperClass(specifier), fields.get(specifier)!!, properties) + generateDelegates(getSuperClass(specifier), fields[specifier]!!, properties) } } @@ -98,7 +97,7 @@ class DelegationTranslator( is FunctionDescriptor -> generateDelegateCallForFunctionMember(descriptor, overriddenDescriptor as FunctionDescriptor, field.name, properties) else -> - throw IllegalArgumentException("Expected property or function ${descriptor}") + throw IllegalArgumentException("Expected property or function $descriptor") } } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt index c62e051b8a5..27f5ba0e3e3 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt @@ -108,11 +108,11 @@ private class PropertyTranslator( private fun getCustomGetterDeclaration(): KtPropertyAccessor = declaration?.getter ?: - throw IllegalStateException("declaration and getter should not be null descriptor=${descriptor} declaration=${declaration}") + throw IllegalStateException("declaration and getter should not be null descriptor=$descriptor declaration=$declaration") private fun getCustomSetterDeclaration(): KtPropertyAccessor = declaration?.setter ?: - throw IllegalStateException("declaration and setter should not be null descriptor=${descriptor} declaration=${declaration}") + throw IllegalStateException("declaration and setter should not be null descriptor=$descriptor declaration=$declaration") private fun generateDefaultGetter(): JsPropertyInitializer { val getterDescriptor = descriptor.getter ?: throw IllegalStateException("Getter descriptor should not be null") @@ -149,7 +149,7 @@ private class PropertyTranslator( if (getterDescriptor.isExtension) { val receiver = function.addParameter(getReceiverParameterName()).name val arguments = (delegatedJsCall as JsInvocation).arguments - arguments.set(0, receiver.makeRef()) + arguments[0] = receiver.makeRef() } val returnResult = JsReturn(delegatedJsCall) @@ -182,7 +182,7 @@ private class PropertyTranslator( assert(setterDescriptor.valueParameters.size == 1) { "Setter must have 1 parameter" } val correspondingPropertyName = setterDescriptor.correspondingProperty.name.asString() val valueParameter = function.addParameter(correspondingPropertyName).name - val withAliased = context().innerContextWithAliased(setterDescriptor.valueParameters.get(0), valueParameter.makeRef()) + val withAliased = context().innerContextWithAliased(setterDescriptor.valueParameters[0], valueParameter.makeRef()) val delegatedCall = context().bindingContext().get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, setterDescriptor) if (delegatedCall != null) { @@ -194,7 +194,7 @@ private class PropertyTranslator( if (setterDescriptor.isExtension) { val receiver = function.addParameter(getReceiverParameterName(), 0).name - (delegatedJsCall as JsInvocation).arguments.set(0, receiver.makeRef()) + (delegatedJsCall as JsInvocation).arguments[0] = receiver.makeRef() } } else {