KT-11030 Minor code improvements

This commit is contained in:
Alexey Andreev
2016-04-13 11:08:33 +03:00
parent 166b985fca
commit 87ea0bd0cb
14 changed files with 185 additions and 152 deletions
@@ -171,4 +171,18 @@ public final class ClosureTest extends SingleFileTranslationTest {
public void testLocalConstructorAndMethod() throws Exception { public void testLocalConstructorAndMethod() throws Exception {
checkFooBoxIsOk(); checkFooBoxIsOk();
} }
// TODO: tests UsageTracker but encounters issue somewhere else. Uncomment when issue with `this` generation gets fixed
// Presumably, KT-11823
/*public void testDeepInnerClassInLocalClass() throws Exception {
checkFooBoxIsOk();
}
public void testDeepInnerClassInLocalClassFromExtension() throws Exception {
checkFooBoxIsOk();
}*/
public void testImplicitGenericReceiverInExtensionInLocalClass() throws Exception {
checkFooBoxIsOk();
}
} }
@@ -210,11 +210,8 @@ object ConstructorCallCase : FunctionCallCase() {
val arguments = argumentsInfo.getArguments() val arguments = argumentsInfo.getArguments()
val constructorDescriptor = callableDescriptor as ConstructorDescriptor val constructorDescriptor = callableDescriptor as ConstructorDescriptor
val closure = context.getLocalClassClosure(constructorDescriptor) val closure = context.getClassOrConstructorClosure(constructorDescriptor)
var closureArgs = emptyList<JsExpression>() val closureArgs = closure?.map { context.getParameterNameRefForInvocation(it) } ?: emptyList()
if (closure != null) {
closureArgs = closure.asSequence().map { context.getParameterNameRefForInvocation(it) }.toList()
}
if (constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) { if (constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) {
return JsNew(functionRef, closureArgs + arguments) return JsNew(functionRef, closureArgs + arguments)
@@ -46,6 +46,7 @@ import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.fqnWithoutSideE
import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.*; import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.*;
import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getMangledName; import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getMangledName;
import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName; import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.getParentOfType;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isExtension; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isExtension;
import static org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt.isDynamic; import static org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt.isDynamic;
@@ -252,7 +253,7 @@ public final class StaticContext {
} }
}; };
Rule<JsName> localDeclarations = new Rule<JsName>() { Rule<JsName> localClasses = new Rule<JsName>() {
@Nullable @Nullable
@Override @Override
public JsName apply(@NotNull DeclarationDescriptor descriptor) { public JsName apply(@NotNull DeclarationDescriptor descriptor) {
@@ -263,9 +264,7 @@ public final class StaticContext {
String suggested = getSuggestedName(descriptor); String suggested = getSuggestedName(descriptor);
do { descriptor = getParentOfType(descriptor, ClassOrPackageFragmentDescriptor.class, true);
descriptor = descriptor.getContainingDeclaration();
} while (descriptor != null && !(descriptor instanceof ClassOrPackageFragmentDescriptor));
assert descriptor != null; assert descriptor != null;
JsScope scope = getScopeForDescriptor(descriptor); JsScope scope = getScopeForDescriptor(descriptor);
@@ -291,7 +290,7 @@ public final class StaticContext {
return scope.declareFreshName(getSuggestedName(descriptor)); return scope.declareFreshName(getSuggestedName(descriptor));
} }
}; };
Rule<JsName> constructorOrCompanionObjectHasTheSameNameAsTheClass = new Rule<JsName>() { Rule<JsName> constructorOrNativeCompanionObjectHasTheSameNameAsTheClass = new Rule<JsName>() {
@Override @Override
public JsName apply(@NotNull DeclarationDescriptor descriptor) { public JsName apply(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary() || if (descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary() ||
@@ -385,9 +384,9 @@ public final class StaticContext {
}; };
addRule(namesForDynamic); addRule(namesForDynamic);
addRule(localDeclarations); addRule(localClasses);
addRule(namesForStandardClasses); addRule(namesForStandardClasses);
addRule(constructorOrCompanionObjectHasTheSameNameAsTheClass); addRule(constructorOrNativeCompanionObjectHasTheSameNameAsTheClass);
addRule(propertyOrPropertyAccessor); addRule(propertyOrPropertyAccessor);
addRule(predefinedObjectsHasUnobfuscatableNames); addRule(predefinedObjectsHasUnobfuscatableNames);
addRule(overridingDescriptorsReferToOriginalName); addRule(overridingDescriptorsReferToOriginalName);
@@ -573,7 +572,7 @@ public final class StaticContext {
if (!(descriptor instanceof ClassDescriptor)) { if (!(descriptor instanceof ClassDescriptor)) {
return null; return null;
} }
DeclarationDescriptor container = getEnclosing(descriptor.getContainingDeclaration()); DeclarationDescriptor container = getParentOfType(descriptor, ClassDescriptor.class);
if (container == null) { if (container == null) {
return null; return null;
} }
@@ -593,12 +592,8 @@ public final class StaticContext {
return null; return null;
} }
descriptor = descriptor.getContainingDeclaration(); descriptor = getParentOfType(descriptor, PackageFragmentDescriptor.class, true);
while (descriptor != null && !(descriptor instanceof ClassOrPackageFragmentDescriptor)) { assert descriptor != null;
descriptor = descriptor.getContainingDeclaration();
}
if (!(descriptor instanceof PackageFragmentDescriptor)) return null;
return getQualifiedReference(descriptor); return getQualifiedReference(descriptor);
} }
}; };
@@ -614,14 +609,6 @@ public final class StaticContext {
} }
} }
@Nullable
private static ClassDescriptor getEnclosing(@Nullable DeclarationDescriptor descriptor) {
while (descriptor != null && !(descriptor instanceof ClassDescriptor)) {
descriptor = descriptor.getContainingDeclaration();
}
return (ClassDescriptor) descriptor;
}
private static JsExpression applySideEffects(JsExpression expression, DeclarationDescriptor descriptor) { private static JsExpression applySideEffects(JsExpression expression, DeclarationDescriptor descriptor) {
if (expression instanceof HasMetadata) { if (expression instanceof HasMetadata) {
if (descriptor instanceof FunctionDescriptor || if (descriptor instanceof FunctionDescriptor ||
@@ -650,13 +637,13 @@ public final class StaticContext {
} }
} }
public void putLocalClassClosure(@NotNull MemberDescriptor localClass, @NotNull List<DeclarationDescriptor> closure) { public void putClassOrConstructorClosure(@NotNull MemberDescriptor localClass, @NotNull List<DeclarationDescriptor> closure) {
localClassesClosure.put(localClass, Lists.newArrayList(closure)); localClassesClosure.put(localClass, Lists.newArrayList(closure));
} }
@Nullable @Nullable
public List<DeclarationDescriptor> getLocalClassClosure(@NotNull MemberDescriptor localClass) { public List<DeclarationDescriptor> getClassOrConstructorClosure(@NotNull MemberDescriptor descriptor) {
List<DeclarationDescriptor> result = localClassesClosure.get(localClass); List<DeclarationDescriptor> result = localClassesClosure.get(descriptor);
return result != null ? Lists.newArrayList(result) : null; return result != null ? Lists.newArrayList(result) : null;
} }
} }
@@ -356,11 +356,17 @@ public class TranslationContext {
if (descriptor.getValue() instanceof ExtensionReceiver) return JsLiteral.THIS; if (descriptor.getValue() instanceof ExtensionReceiver) return JsLiteral.THIS;
ClassifierDescriptor classifier = descriptor.getValue().getType().getConstructor().getDeclarationDescriptor(); ClassifierDescriptor classifier = descriptor.getValue().getType().getConstructor().getDeclarationDescriptor();
if (!(classifier instanceof ClassDescriptor)) return JsLiteral.THIS;
// TODO: can't tell why this assertion is valid, revisit this code later
assert classifier instanceof ClassDescriptor;
ClassDescriptor cls = (ClassDescriptor) classifier; ClassDescriptor cls = (ClassDescriptor) classifier;
JsExpression receiver = getAliasForDescriptor(cls.getThisAsReceiverParameter()); JsExpression receiver = getAliasForDescriptor(cls.getThisAsReceiverParameter());
// I could reproduce situation when receiver == null, although there's no test to reproduce it. Have no time for investigation
// Hint: try generating field with getter/setter, see deepInnerClassInLocalClass.kt
// TODO: revisit this code later
if (receiver == null) { if (receiver == null) {
receiver = JsLiteral.THIS; receiver = JsLiteral.THIS;
} }
@@ -447,15 +453,15 @@ public class TranslationContext {
return declarationDescriptor; return declarationDescriptor;
} }
public void putLocalClassClosure(@NotNull MemberDescriptor localClass, @NotNull List<DeclarationDescriptor> closure) { public void putClassOrConstructorClosure(@NotNull MemberDescriptor descriptor, @NotNull List<DeclarationDescriptor> closure) {
staticContext.putLocalClassClosure(localClass, closure); staticContext.putClassOrConstructorClosure(descriptor, closure);
} }
@Nullable @Nullable
public List<DeclarationDescriptor> getLocalClassClosure(@NotNull MemberDescriptor localClass) { public List<DeclarationDescriptor> getClassOrConstructorClosure(@NotNull MemberDescriptor localClass) {
List<DeclarationDescriptor> result = staticContext.getLocalClassClosure(localClass); List<DeclarationDescriptor> result = staticContext.getClassOrConstructorClosure(localClass);
if (result == null && localClass instanceof ConstructorDescriptor && ((ConstructorDescriptor) localClass).isPrimary()) { if (result == null && localClass instanceof ConstructorDescriptor && ((ConstructorDescriptor) localClass).isPrimary()) {
result = staticContext.getLocalClassClosure((ClassDescriptor) localClass.getContainingDeclaration()); result = staticContext.getClassOrConstructorClosure((ClassDescriptor) localClass.getContainingDeclaration());
} }
return result; return result;
} }
@@ -17,13 +17,11 @@
package org.jetbrains.kotlin.js.translate.context package org.jetbrains.kotlin.js.translate.context
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.DescriptorUtils.isAncestor
import com.google.dart.compiler.backend.js.ast.JsName import com.google.dart.compiler.backend.js.ast.JsName
import org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName import org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName
import com.google.dart.compiler.backend.js.ast.JsScope import com.google.dart.compiler.backend.js.ast.JsScope
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.getParentOfType import org.jetbrains.kotlin.resolve.DescriptorUtils.*
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
private val CAPTURED_RECEIVER_NAME_PREFIX : String = "this$" private val CAPTURED_RECEIVER_NAME_PREFIX : String = "this$"
@@ -64,14 +62,23 @@ class UsageTracker(
} }
private fun captureIfNeed(descriptor: DeclarationDescriptor?) { private fun captureIfNeed(descriptor: DeclarationDescriptor?) {
if (descriptor == null || isCaptured(descriptor) || isAncestor(containingDescriptor, descriptor, /* strict = */ true) || if (descriptor == null || isCaptured(descriptor) || !isInLocalDeclaration() ||
isReceiverAncestor(descriptor) || isSingletonReceiver(descriptor)) return isAncestor(containingDescriptor, descriptor, /* strict = */ true) ||
isReceiverAncestor(descriptor) || isSingletonReceiver(descriptor)
) {
return
}
parent?.captureIfNeed(descriptor) parent?.captureIfNeed(descriptor)
captured[descriptor] = descriptor.getJsNameForCapturedDescriptor() captured[descriptor] = descriptor.getJsNameForCapturedDescriptor()
} }
private fun isInLocalDeclaration(): Boolean {
val container = containingDescriptor
return isDescriptorWithLocalVisibility(if (container is ConstructorDescriptor) container.containingDeclaration else container)
}
/** /**
* We shouldn't capture current `this` or outer `this`. Assuming `C` is current translating class, * We shouldn't capture current `this` or outer `this`. Assuming `C` is current translating class,
* we have `descriptor == A::this` in the following cases: * we have `descriptor == A::this` in the following cases:
@@ -92,10 +99,10 @@ class UsageTracker(
*/ */
private fun isReceiverAncestor(descriptor: DeclarationDescriptor): Boolean { private fun isReceiverAncestor(descriptor: DeclarationDescriptor): Boolean {
if (descriptor !is ReceiverParameterDescriptor) return false if (descriptor !is ReceiverParameterDescriptor) return false
if (containingDescriptor !is ClassDescriptor && DescriptorUtils.isDescriptorWithLocalVisibility(containingDescriptor)) return false if (containingDescriptor !is ClassDescriptor && containingDescriptor !is ConstructorDescriptor) return false
val currentClass = DescriptorUtils.getClassDescriptorForType(descriptor.type)
val containingClass = getParentOfType(containingDescriptor, ClassDescriptor::class.java, false) ?: return false val containingClass = getParentOfType(containingDescriptor, ClassDescriptor::class.java, false) ?: return false
val currentClass = descriptor.containingDeclaration as? ClassDescriptor ?: return false
for (outerDeclaration in generateSequence(containingClass) { it.containingDeclaration as? ClassDescriptor }) { for (outerDeclaration in generateSequence(containingClass) { it.containingDeclaration as? ClassDescriptor }) {
if (DescriptorUtils.isSubclass(outerDeclaration, currentClass)) return true if (DescriptorUtils.isSubclass(outerDeclaration, currentClass)) return true
@@ -124,19 +131,17 @@ class UsageTracker(
private fun isSingletonReceiver(descriptor: DeclarationDescriptor): Boolean { private fun isSingletonReceiver(descriptor: DeclarationDescriptor): Boolean {
if (descriptor !is ReceiverParameterDescriptor) return false if (descriptor !is ReceiverParameterDescriptor) return false
val value = descriptor.value val container = descriptor.containingDeclaration
if (value !is ImplicitReceiver) return false if (!DescriptorUtils.isObject(container)) return false
if (!DescriptorUtils.isObject(value.declarationDescriptor)) return false
// This is workaround, since sometimes translator generates wrong expression for `this` expressions. // This is workaround, since sometimes translator generates wrong expression for `this` expressions.
// Presumably, it's related to KT-11823 // Presumably, it's related to KT-11823
// TODO: remove when issue gets fixed. // TODO: remove when issue gets fixed.
if (containingDescriptor == value.declarationDescriptor) return false if (containingDescriptor == container) return false
if (containingDescriptor !is ClassDescriptor) { if (containingDescriptor !is ClassDescriptor) {
val containingClass = getParentOfType(containingDescriptor, ClassDescriptor::class.java, false); val containingClass = getParentOfType(containingDescriptor, ClassDescriptor::class.java, false);
if (containingClass == value.declarationDescriptor) return false if (containingClass == container) return false
} }
return true return true
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils.*
import org.jetbrains.kotlin.types.CommonSupertypes.topologicallySortSuperclassesAndRecordAllInstances import org.jetbrains.kotlin.types.CommonSupertypes.topologicallySortSuperclassesAndRecordAllInstances
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.identity import org.jetbrains.kotlin.utils.identity
/** /**
@@ -63,9 +64,8 @@ class ClassTranslator private constructor(
private var primaryConstructor: ConstructorInfo? = null private var primaryConstructor: ConstructorInfo? = null
private lateinit var definitionPlace: DefinitionPlace private lateinit var definitionPlace: DefinitionPlace
fun translate(): TranslationResult { private fun translate(): TranslationResult {
invocationArguments.clear() generateClassCreateInvocationArguments()
getClassCreateInvocationArguments()
val classNameRef = context().getNameForDescriptor(descriptor).makeRef() val classNameRef = context().getNameForDescriptor(descriptor).makeRef()
val classCreation = JsInvocation(context().namer().classCreateInvocation(descriptor), invocationArguments) val classCreation = JsInvocation(context().namer().classCreateInvocation(descriptor), invocationArguments)
@@ -76,16 +76,14 @@ class ClassTranslator private constructor(
private fun isTrait(): Boolean = descriptor.kind == ClassKind.INTERFACE private fun isTrait(): Boolean = descriptor.kind == ClassKind.INTERFACE
private fun getClassCreateInvocationArguments() { private fun generateClassCreateInvocationArguments() {
var context = context()
val properties = SmartList<JsPropertyInitializer>() val properties = SmartList<JsPropertyInitializer>()
val staticProperties = SmartList<JsPropertyInitializer>() val staticProperties = SmartList<JsPropertyInitializer>()
val qualifiedReference = context.getQualifiedReference(descriptor) val qualifiedReference = context().getQualifiedReference(descriptor)
val scope = context().getScopeForDescriptor(descriptor) val scope = context().getScopeForDescriptor(descriptor)
val definitionPlace = DefinitionPlace(scope as JsObjectScope, qualifiedReference, staticProperties) val definitionPlace = DefinitionPlace(scope as JsObjectScope, qualifiedReference, staticProperties)
context = context.newDeclaration(descriptor, definitionPlace) val context = context().newDeclaration(descriptor, definitionPlace)
invocationArguments += getSuperclassReferences(context) invocationArguments += getSuperclassReferences(context)
@@ -96,16 +94,14 @@ class ClassTranslator private constructor(
bodyVisitor.traverseContainer(classDeclaration, nonConstructorContext) bodyVisitor.traverseContainer(classDeclaration, nonConstructorContext)
delegationTranslator.generateDelegated(properties) delegationTranslator.generateDelegated(properties)
var enumFunction: JsFunction? = null
if (isEnumClass(descriptor)) {
val enumEntries = JsObjectLiteral(bodyVisitor.enumEntryList, true)
enumFunction = simpleReturnFunction(nonConstructorContext.getScopeForDescriptor(descriptor), enumEntries)
}
translatePrimaryConstructor(context, delegationTranslator) translatePrimaryConstructor(context, delegationTranslator)
classDeclaration.getSecondaryConstructors().forEach { generateSecondaryConstructor(context, it) } classDeclaration.getSecondaryConstructors().forEach { generateSecondaryConstructor(context, it) }
generatedBridgeMethods(properties) generatedBridgeMethods(properties)
enumFunction?.let { invocationArguments += it }
if (isEnumClass(descriptor)) {
val enumEntries = JsObjectLiteral(bodyVisitor.enumEntryList, true)
invocationArguments += simpleReturnFunction(nonConstructorContext.getScopeForDescriptor(descriptor), enumEntries)
}
val dataClassGenerator = JsDataClassGenerator(classDeclaration, context, properties) val dataClassGenerator = JsDataClassGenerator(classDeclaration, context, properties)
@@ -118,6 +114,12 @@ class ClassTranslator private constructor(
dataClassGenerator.generate() dataClassGenerator.generate()
} }
// ExpressionVisitor.visitObjectLiteralExpression uses DefinitionPlace of the translated class to generate call to
// super constructor. Sometimes, when generating super call, we may translate another anonymous class passed as an
// argument. This class will be declared in the DefinitionPlace and put it static properties. See full explanation
// in ExpressionVisitor.visitObjectLiteralExpression
// TODO: It's a hack, we should think how to generate staticProperties lazily, whenever somebody tries to put
// definition into DefinitionPlace.
val hasStaticProperties = !staticProperties.isEmpty() || DescriptorUtils.isAnonymousObject(descriptor) val hasStaticProperties = !staticProperties.isEmpty() || DescriptorUtils.isAnonymousObject(descriptor)
if (!properties.isEmpty() || hasStaticProperties) { if (!properties.isEmpty() || hasStaticProperties) {
if (properties.isEmpty()) { if (properties.isEmpty()) {
@@ -144,7 +146,7 @@ class ClassTranslator private constructor(
if (isTrait()) return if (isTrait()) return
val scope = JsFunctionScope(classContext.scope(), "$descriptor: primary constructor") val scope = JsFunctionScope(classContext.scope(), "$descriptor: primary constructor")
var constructorContext = classContext.innerWithUsageTracker(scope, descriptor) val constructorContext = classContext.innerWithUsageTracker(scope, descriptor)
val initializer = ClassInitializerTranslator(classDeclaration, constructorContext).generateInitializeMethod(delegationTranslator) val initializer = ClassInitializerTranslator(classDeclaration, constructorContext).generateInitializeMethod(delegationTranslator)
invocationArguments += initializer invocationArguments += initializer
@@ -157,21 +159,20 @@ class ClassTranslator private constructor(
val classDescriptor = constructorDescriptor.containingDeclaration val classDescriptor = constructorDescriptor.containingDeclaration
val constructorScope = classContext.getScopeForDescriptor(constructorDescriptor) val constructorScope = classContext.getScopeForDescriptor(constructorDescriptor)
var context = classContext.innerWithUsageTracker(constructorScope, constructorDescriptor)
val thisName = constructorScope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME) val thisName = constructorScope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME)
val thisNameRef = thisName.makeRef() val thisNameRef = thisName.makeRef()
val receiverDescriptor = classDescriptor.thisAsReceiverParameter val receiverDescriptor = classDescriptor.thisAsReceiverParameter
context = context.newDeclaration(constructorDescriptor.containingDeclaration, context.definitionPlace) var context = classContext
context = context.newDeclaration(constructorDescriptor, context.definitionPlace) .innerWithUsageTracker(constructorScope, constructorDescriptor)
context = context.innerContextWithAliased(receiverDescriptor, thisNameRef) .innerContextWithAliased(receiverDescriptor, thisNameRef)
val outerName = context.getOuterClassReference(classDescriptor); val outerClassName = context.getOuterClassReference(classDescriptor);
val outerClass = DescriptorUtils.getContainingClass(classDescriptor) val outerClass = DescriptorUtils.getContainingClass(classDescriptor)
if (outerClass != null && outerName != null) { if (outerClassName != null) {
val outerClassRef = outerClass.thisAsReceiverParameter val outerClassReceiver = outerClass!!.thisAsReceiverParameter
context = context.innerContextWithAliased(outerClassRef, outerName.makeRef()) context = context.innerContextWithAliased(outerClassReceiver, outerClassName.makeRef())
} }
// Translate constructor body // Translate constructor body
@@ -191,9 +192,9 @@ class ClassTranslator private constructor(
// Add parameter for outer instance // Add parameter for outer instance
val leadingArgs = mutableListOf<JsExpression>() val leadingArgs = mutableListOf<JsExpression>()
if (outerName != null) { if (outerClassName != null) {
constructorFunction.parameters.add(0, JsParameter(outerName)) constructorFunction.parameters.add(0, JsParameter(outerClassName))
leadingArgs += outerName.makeRef() leadingArgs += outerClassName.makeRef()
} }
constructorFunction.parameters += JsParameter(thisName) constructorFunction.parameters += JsParameter(thisName)
@@ -212,8 +213,8 @@ class ClassTranslator private constructor(
if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) { if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) {
superCallGenerators += { superCallGenerators += {
val usageTracker = context.usageTracker()!! val usageTracker = context.usageTracker()!!
val closure = context.getLocalClassClosure(classDescriptor).orEmpty().map { val closure = context.getClassOrConstructorClosure(classDescriptor).orEmpty().map {
usageTracker.capturedDescriptorToJsName[it]?.makeRef() as? JsExpression ?: JsLiteral.NULL usageTracker.getNameForCapturedDescriptor(it)!!.makeRef()
} }
it += JsInvocation(Namer.getFunctionCallRef(referenceToClass), listOf(thisNameRef) + closure + leadingArgs).makeStmt() it += JsInvocation(Namer.getFunctionCallRef(referenceToClass), listOf(thisNameRef) + closure + leadingArgs).makeStmt()
} }
@@ -242,9 +243,8 @@ class ClassTranslator private constructor(
private fun emitConstructors(nonConstructorContext: TranslationContext) { private fun emitConstructors(nonConstructorContext: TranslationContext) {
// Build map that maps constructor to all constructors called via this() // Build map that maps constructor to all constructors called via this()
val constructorMap = allConstructors.map { it.descriptor to it }.toMap() val constructorMap = allConstructors.map { it.descriptor to it }.toMap()
val primaryConstructor = this.primaryConstructor
val thisCalls = allConstructors.map { val thisCalls = secondaryConstructors.map {
val set = mutableSetOf<ConstructorInfo>() val set = mutableSetOf<ConstructorInfo>()
val descriptor = it.descriptor val descriptor = it.descriptor
if (descriptor is ConstructorDescriptor) { if (descriptor is ConstructorDescriptor) {
@@ -256,72 +256,46 @@ class ClassTranslator private constructor(
} }
} }
} }
if (primaryConstructor != null && primaryConstructor != it) {
set += primaryConstructor
}
Pair(it, set) Pair(it, set)
}.toMap() }.toMap()
// Sort graph of constructors val sortedConstructors = DFS.topologicalOrder(allConstructors.asIterable()) { thisCalls[it].orEmpty() }.reversed()
val sortedConstructors = mutableListOf<ConstructorInfo>()
val visitedConstructors = mutableSetOf<ConstructorInfo>()
fun sort(constructor: ConstructorInfo) {
if (!visitedConstructors.add(constructor)) return
sortedConstructors += constructor
for (thisCall in thisCalls[constructor].orEmpty()) {
sort(thisCall)
}
}
allConstructors.forEach(::sort)
// Emit constructors
for (constructor in sortedConstructors) { for (constructor in sortedConstructors) {
constructor.superCallGenerator() constructor.superCallGenerator()
val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!! val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!!
val usageTracker = constructor.context.usageTracker() ?: continue val usageTracker = constructor.context.usageTracker()!!
val nonConstructorCapturedVars = nonConstructorUsageTracker.capturedDescriptors val nonConstructorCapturedVars = nonConstructorUsageTracker.capturedDescriptors
val constructorCapturedVars = usageTracker.capturedDescriptors val constructorCapturedVars = usageTracker.capturedDescriptors
val capturedVars = (nonConstructorCapturedVars.asSequence() + val capturedVars = (nonConstructorCapturedVars + constructorCapturedVars).distinct()
constructorCapturedVars.asSequence().filter { it !in nonConstructorCapturedVars }).toList()
val descriptor = constructor.descriptor val descriptor = constructor.descriptor
nonConstructorContext.putLocalClassClosure(descriptor, capturedVars) nonConstructorContext.putClassOrConstructorClosure(descriptor, capturedVars)
if (descriptor is ClassDescriptor) {
if (primaryConstructor != null) {
nonConstructorContext.putLocalClassClosure(primaryConstructor.descriptor, capturedVars)
}
}
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, private fun addClosureParameters(constructor: ConstructorInfo, nonConstructorContext: TranslationContext,
dataClassGenerator: JsDataClassGenerator) { dataClassGenerator: JsDataClassGenerator) {
val usageTracker = constructor.context.usageTracker()!! val usageTracker = constructor.context.usageTracker()!!
val capturedVars = context().getLocalClassClosure(constructor.descriptor) ?: return val capturedVars = context().getClassOrConstructorClosure(constructor.descriptor) ?: return
val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!! val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!!
val function = constructor.function val function = constructor.function
val additionalStatements = mutableListOf<JsStatement>()
for ((i, capturedVar) in capturedVars.withIndex()) { for ((i, capturedVar) in capturedVars.withIndex()) {
val fieldName = nonConstructorUsageTracker.capturedDescriptorToJsName[capturedVar] val fieldName = nonConstructorUsageTracker.capturedDescriptorToJsName[capturedVar]
val name = usageTracker.capturedDescriptorToJsName[capturedVar] ?: fieldName!! val name = usageTracker.capturedDescriptorToJsName[capturedVar] ?: fieldName!!
function.parameters.add(i, JsParameter(name)) function.parameters.add(i, JsParameter(name))
if (fieldName != null && constructor == primaryConstructor) { if (fieldName != null && constructor == primaryConstructor) {
function.body.statements.add(i, JsAstUtils.defineSimpleProperty(fieldName.ident, name.makeRef())) additionalStatements += JsAstUtils.defineSimpleProperty(fieldName.ident, name.makeRef())
dataClassGenerator.addClosureVariable(fieldName) dataClassGenerator.addClosureVariable(fieldName)
} }
} }
function.body.statements.addAll(0, additionalStatements);
} }
private fun getSuperclassReferences(declarationContext: TranslationContext): JsExpression { private fun getSuperclassReferences(declarationContext: TranslationContext): JsExpression {
@@ -62,7 +62,7 @@ public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
} }
@Override @Override
public Void visitClass(@NotNull KtClass declaration, TranslationContext context) { public Void visitClassOrObject(@NotNull KtClassOrObject declaration, TranslationContext context) {
staticResult.addAll(ClassTranslator.translate(declaration, context).getProperties()); staticResult.addAll(ClassTranslator.translate(declaration, context).getProperties());
return null; return null;
} }
@@ -82,12 +82,6 @@ public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
return null; return null;
} }
@Override
public Void visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, TranslationContext context) {
staticResult.addAll(ClassTranslator.translate(declaration, context).getProperties());
return null;
}
@Override @Override
public Void visitNamedFunction(@NotNull KtNamedFunction expression, TranslationContext context) { public Void visitNamedFunction(@NotNull KtNamedFunction expression, TranslationContext context) {
FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression); FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
@@ -26,10 +26,7 @@ import org.jetbrains.kotlin.js.translate.initializer.InitializerUtils.generateIn
import org.jetbrains.kotlin.js.translate.initializer.InitializerVisitor import org.jetbrains.kotlin.js.translate.initializer.InitializerVisitor
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getPropertyDescriptor import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getPropertyDescriptor
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtProperty
class FileDeclarationVisitor( class FileDeclarationVisitor(
val context: TranslationContext, val context: TranslationContext,
@@ -49,20 +46,11 @@ class FileDeclarationVisitor(
} }
} }
override fun visitClass(declaration: KtClass, context: TranslationContext?): Void? { override fun visitClassOrObject(declaration: KtClassOrObject, context: TranslationContext?): Void? {
result += ClassTranslator.translate(declaration, context!!).properties result += ClassTranslator.translate(declaration, context!!).properties
return null return null
} }
override fun visitObjectDeclaration(declaration: KtObjectDeclaration, context: TranslationContext?): Void? {
context!!
// TODO: avoid duplication with superclass
result += ClassTranslator.translate(declaration, context).properties
return null
}
override fun visitProperty(expression: KtProperty, context: TranslationContext?): Void? { override fun visitProperty(expression: KtProperty, context: TranslationContext?): Void? {
context!! // hack context!! // hack
@@ -522,15 +522,12 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull @NotNull
public JsNode visitObjectLiteralExpression(@NotNull KtObjectLiteralExpression expression, @NotNull TranslationContext context) { public JsNode visitObjectLiteralExpression(@NotNull KtObjectLiteralExpression expression, @NotNull TranslationContext context) {
ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), expression.getObjectDeclaration()); ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), expression.getObjectDeclaration());
JsScope scope = context.getScopeForDescriptor(descriptor); ClassTranslator.TranslationResult result = translateClassOrObject(expression.getObjectDeclaration(), descriptor, context);
TranslationContext classContext = context.innerWithUsageTracker(scope, descriptor);
ClassTranslator.TranslationResult result = ClassTranslator.translate(expression.getObjectDeclaration(), classContext);
List<JsPropertyInitializer> properties = result.getProperties(); List<JsPropertyInitializer> properties = result.getProperties();
context.getDefinitionPlace().getProperties().addAll(properties); context.getDefinitionPlace().getProperties().addAll(properties);
JsExpression constructor = context.getQualifiedReference(descriptor); JsExpression constructor = context.getQualifiedReference(descriptor);
List<DeclarationDescriptor> closure = context.getLocalClassClosure(descriptor); List<DeclarationDescriptor> closure = context.getClassOrConstructorClosure(descriptor);
List<JsExpression> closureArgs = new ArrayList<JsExpression>(); List<JsExpression> closureArgs = new ArrayList<JsExpression>();
if (closure != null) { if (closure != null) {
for (DeclarationDescriptor capturedValue : closure) { for (DeclarationDescriptor capturedValue : closure) {
@@ -601,11 +598,17 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override @Override
public JsNode visitClass(@NotNull KtClass klass, TranslationContext context) { public JsNode visitClass(@NotNull KtClass klass, TranslationContext context) {
ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), klass); ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), klass);
JsScope scope = context.getScopeForDescriptor(descriptor); context.getDefinitionPlace().getProperties().addAll(translateClassOrObject(klass, descriptor, context).getProperties());
TranslationContext classContext = context.innerWithUsageTracker(scope, descriptor);
context.getDefinitionPlace().getProperties().addAll(ClassTranslator.translate(klass, classContext).getProperties());
return JsEmpty.INSTANCE; return JsEmpty.INSTANCE;
} }
private static ClassTranslator.TranslationResult translateClassOrObject(
@NotNull KtClassOrObject declaration,
@NotNull ClassDescriptor descriptor,
@NotNull TranslationContext context
) {
JsScope scope = context.getScopeForDescriptor(descriptor);
TranslationContext classContext = context.innerWithUsageTracker(scope, descriptor);
return ClassTranslator.translate(declaration, classContext);
}
} }
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.js.translate.context.UsageTracker;
import org.jetbrains.kotlin.js.translate.declaration.DelegationTranslator; import org.jetbrains.kotlin.js.translate.declaration.DelegationTranslator;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator; import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.AstUtilsKt; import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.AstUtilsKt;
import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.lexer.KtTokens;
@@ -149,7 +148,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
return; return;
} }
if (hasAncestorClass(bindingContext(), classDeclaration)) { if (hasAncestorClass(bindingContext(), classDeclaration)) {
ResolvedCall<FunctionDescriptor> superCall = BindingUtils.getSuperCall(bindingContext(), classDeclaration); ResolvedCall<FunctionDescriptor> superCall = getSuperCall(bindingContext(), classDeclaration);
if (superCall == null) return; if (superCall == null) return;
if (classDeclaration instanceof KtEnumEntry) { if (classDeclaration instanceof KtEnumEntry) {
@@ -163,9 +162,11 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
ConstructorDescriptor superDescriptor = (ConstructorDescriptor) superCall.getResultingDescriptor(); ConstructorDescriptor superDescriptor = (ConstructorDescriptor) superCall.getResultingDescriptor();
List<DeclarationDescriptor> superclassClosure = context.getLocalClassClosure(superDescriptor); List<DeclarationDescriptor> superclassClosure = context.getClassOrConstructorClosure(superDescriptor);
UsageTracker tracker = context.usageTracker(); if (superclassClosure != null) {
if (superclassClosure != null && tracker != null) { UsageTracker tracker = context.usageTracker();
assert tracker != null : "Closure exists, therefore UsageTracker must exist too. Translating constructor of " +
descriptor;
for (DeclarationDescriptor capturedValue : superclassClosure) { for (DeclarationDescriptor capturedValue : superclassClosure) {
tracker.used(capturedValue); tracker.used(capturedValue);
arguments.add(tracker.getCapturedDescriptorToJsName().get(capturedValue).makeRef()); arguments.add(tracker.getCapturedDescriptorToJsName().get(capturedValue).makeRef());
@@ -85,7 +85,8 @@ public class ManglingUtils {
} }
Collections.reverse(parts); Collections.reverse(parts);
return StringUtil.join(parts, "$") + "$"; String result = StringUtil.join(parts, "$");
return !result.isEmpty() ? result + "$" : "";
} }
@NotNull @NotNull
@@ -0,0 +1,23 @@
package foo
class A() {
fun test(): Int {
open class B(open val x: Int) {
inner class C(override val x: Int) : B(x * 10) {
inner class D() {
var baz: () -> Int = { 0 }
constructor(b: Boolean) : this() {
baz = { x + this@B.x }
}
fun bar() = { x + this@B.x }
}
}
}
return B(3).C(2).D().bar()() + B(5).C(4).D(true).baz()
}
}
fun box(): String {
assertEquals(2345, A().test())
return "OK"
}
@@ -0,0 +1,23 @@
package foo
class A() {
fun test(): Int {
open class B(open val x: Int) {
inner class C(override val x: Int) : B(x * 10) {
inner class D() {
fun baz() = bar()
}
fun D.bar() = { x + this@B.x }
}
}
return B(3).C(2).D().baz()()
}
}
fun box(): String {
assertEquals(23, A().test())
return "OK"
}
@@ -0,0 +1,17 @@
package foo
class A {
fun test() = 23
}
fun <T : A> T.foo(): Int {
class B {
fun foo() = test()
}
return B().foo()
}
fun box(): String {
assertEquals(23, A().foo())
return "OK"
}