diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClosureTest.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClosureTest.java index ca5677a3215..53c638ecf6a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClosureTest.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClosureTest.java @@ -171,4 +171,18 @@ public final class ClosureTest extends SingleFileTranslationTest { public void testLocalConstructorAndMethod() throws Exception { 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(); + } } 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 c3dea0b1c17..d3adb34e230 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,11 +210,8 @@ object ConstructorCallCase : FunctionCallCase() { val arguments = argumentsInfo.getArguments() val constructorDescriptor = callableDescriptor as ConstructorDescriptor - val closure = context.getLocalClassClosure(constructorDescriptor) - var closureArgs = emptyList() - if (closure != null) { - closureArgs = closure.asSequence().map { context.getParameterNameRefForInvocation(it) }.toList() - } + val closure = context.getClassOrConstructorClosure(constructorDescriptor) + val closureArgs = closure?.map { context.getParameterNameRefForInvocation(it) } ?: emptyList() if (constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) { return JsNew(functionRef, closureArgs + arguments) 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 403f31a215b..8414bf52f95 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 @@ -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.ManglingUtils.getMangledName; 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.isExtension; import static org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt.isDynamic; @@ -252,7 +253,7 @@ public final class StaticContext { } }; - Rule localDeclarations = new Rule() { + Rule localClasses = new Rule() { @Nullable @Override public JsName apply(@NotNull DeclarationDescriptor descriptor) { @@ -263,9 +264,7 @@ public final class StaticContext { String suggested = getSuggestedName(descriptor); - do { - descriptor = descriptor.getContainingDeclaration(); - } while (descriptor != null && !(descriptor instanceof ClassOrPackageFragmentDescriptor)); + descriptor = getParentOfType(descriptor, ClassOrPackageFragmentDescriptor.class, true); assert descriptor != null; JsScope scope = getScopeForDescriptor(descriptor); @@ -291,7 +290,7 @@ public final class StaticContext { return scope.declareFreshName(getSuggestedName(descriptor)); } }; - Rule constructorOrCompanionObjectHasTheSameNameAsTheClass = new Rule() { + Rule constructorOrNativeCompanionObjectHasTheSameNameAsTheClass = new Rule() { @Override public JsName apply(@NotNull DeclarationDescriptor descriptor) { if (descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary() || @@ -385,9 +384,9 @@ public final class StaticContext { }; addRule(namesForDynamic); - addRule(localDeclarations); + addRule(localClasses); addRule(namesForStandardClasses); - addRule(constructorOrCompanionObjectHasTheSameNameAsTheClass); + addRule(constructorOrNativeCompanionObjectHasTheSameNameAsTheClass); addRule(propertyOrPropertyAccessor); addRule(predefinedObjectsHasUnobfuscatableNames); addRule(overridingDescriptorsReferToOriginalName); @@ -573,7 +572,7 @@ public final class StaticContext { if (!(descriptor instanceof ClassDescriptor)) { return null; } - DeclarationDescriptor container = getEnclosing(descriptor.getContainingDeclaration()); + DeclarationDescriptor container = getParentOfType(descriptor, ClassDescriptor.class); if (container == null) { return null; } @@ -593,12 +592,8 @@ public final class StaticContext { return null; } - descriptor = descriptor.getContainingDeclaration(); - while (descriptor != null && !(descriptor instanceof ClassOrPackageFragmentDescriptor)) { - descriptor = descriptor.getContainingDeclaration(); - } - if (!(descriptor instanceof PackageFragmentDescriptor)) return null; - + descriptor = getParentOfType(descriptor, PackageFragmentDescriptor.class, true); + assert descriptor != null; 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) { if (expression instanceof HasMetadata) { if (descriptor instanceof FunctionDescriptor || @@ -650,13 +637,13 @@ public final class StaticContext { } } - public void putLocalClassClosure(@NotNull MemberDescriptor localClass, @NotNull List closure) { + public void putClassOrConstructorClosure(@NotNull MemberDescriptor localClass, @NotNull List closure) { localClassesClosure.put(localClass, Lists.newArrayList(closure)); } @Nullable - public List getLocalClassClosure(@NotNull MemberDescriptor localClass) { - List result = localClassesClosure.get(localClass); + public List getClassOrConstructorClosure(@NotNull MemberDescriptor descriptor) { + List result = localClassesClosure.get(descriptor); 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 5941d093d38..e2077055b01 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 @@ -356,11 +356,17 @@ public class TranslationContext { if (descriptor.getValue() instanceof ExtensionReceiver) return JsLiteral.THIS; 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; 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) { receiver = JsLiteral.THIS; } @@ -447,15 +453,15 @@ public class TranslationContext { return declarationDescriptor; } - public void putLocalClassClosure(@NotNull MemberDescriptor localClass, @NotNull List closure) { - staticContext.putLocalClassClosure(localClass, closure); + public void putClassOrConstructorClosure(@NotNull MemberDescriptor descriptor, @NotNull List closure) { + staticContext.putClassOrConstructorClosure(descriptor, closure); } @Nullable - public List getLocalClassClosure(@NotNull MemberDescriptor localClass) { - List result = staticContext.getLocalClassClosure(localClass); + public List getClassOrConstructorClosure(@NotNull MemberDescriptor localClass) { + List result = staticContext.getClassOrConstructorClosure(localClass); if (result == null && localClass instanceof ConstructorDescriptor && ((ConstructorDescriptor) localClass).isPrimary()) { - result = staticContext.getLocalClassClosure((ClassDescriptor) localClass.getContainingDeclaration()); + result = staticContext.getClassOrConstructorClosure((ClassDescriptor) localClass.getContainingDeclaration()); } return result; } 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 41645fe5a9b..efef0fc3756 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 @@ -17,13 +17,11 @@ package org.jetbrains.kotlin.js.translate.context import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.resolve.DescriptorUtils.isAncestor 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 +import org.jetbrains.kotlin.resolve.DescriptorUtils.* private val CAPTURED_RECEIVER_NAME_PREFIX : String = "this$" @@ -64,14 +62,23 @@ class UsageTracker( } private fun captureIfNeed(descriptor: DeclarationDescriptor?) { - if (descriptor == null || isCaptured(descriptor) || isAncestor(containingDescriptor, descriptor, /* strict = */ true) || - isReceiverAncestor(descriptor) || isSingletonReceiver(descriptor)) return + if (descriptor == null || isCaptured(descriptor) || !isInLocalDeclaration() || + isAncestor(containingDescriptor, descriptor, /* strict = */ true) || + isReceiverAncestor(descriptor) || isSingletonReceiver(descriptor) + ) { + return + } parent?.captureIfNeed(descriptor) 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 have `descriptor == A::this` in the following cases: @@ -92,10 +99,10 @@ class UsageTracker( */ private fun isReceiverAncestor(descriptor: DeclarationDescriptor): Boolean { 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 currentClass = descriptor.containingDeclaration as? ClassDescriptor ?: return false for (outerDeclaration in generateSequence(containingClass) { it.containingDeclaration as? ClassDescriptor }) { if (DescriptorUtils.isSubclass(outerDeclaration, currentClass)) return true @@ -124,19 +131,17 @@ class UsageTracker( private fun isSingletonReceiver(descriptor: DeclarationDescriptor): Boolean { if (descriptor !is ReceiverParameterDescriptor) return false - val value = descriptor.value - if (value !is ImplicitReceiver) return false - - if (!DescriptorUtils.isObject(value.declarationDescriptor)) return false + val container = descriptor.containingDeclaration + if (!DescriptorUtils.isObject(container)) 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 == container) return false if (containingDescriptor !is ClassDescriptor) { val containingClass = getParentOfType(containingDescriptor, ClassDescriptor::class.java, false); - if (containingClass == value.declarationDescriptor) return false + if (containingClass == container) return false } return true 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 fa9b74db261..b2a458a1b27 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 @@ -46,6 +46,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils.* import org.jetbrains.kotlin.types.CommonSupertypes.topologicallySortSuperclassesAndRecordAllInstances import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.identity /** @@ -63,9 +64,8 @@ class ClassTranslator private constructor( private var primaryConstructor: ConstructorInfo? = null private lateinit var definitionPlace: DefinitionPlace - fun translate(): TranslationResult { - invocationArguments.clear() - getClassCreateInvocationArguments() + private fun translate(): TranslationResult { + generateClassCreateInvocationArguments() val classNameRef = context().getNameForDescriptor(descriptor).makeRef() 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 getClassCreateInvocationArguments() { - var context = context() - + private fun generateClassCreateInvocationArguments() { val properties = SmartList() val staticProperties = SmartList() - val qualifiedReference = context.getQualifiedReference(descriptor) + val qualifiedReference = context().getQualifiedReference(descriptor) val scope = context().getScopeForDescriptor(descriptor) val definitionPlace = DefinitionPlace(scope as JsObjectScope, qualifiedReference, staticProperties) - context = context.newDeclaration(descriptor, definitionPlace) + val context = context().newDeclaration(descriptor, definitionPlace) invocationArguments += getSuperclassReferences(context) @@ -96,16 +94,14 @@ class ClassTranslator private constructor( bodyVisitor.traverseContainer(classDeclaration, nonConstructorContext) 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) classDeclaration.getSecondaryConstructors().forEach { generateSecondaryConstructor(context, it) } 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) @@ -118,6 +114,12 @@ class ClassTranslator private constructor( 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) if (!properties.isEmpty() || hasStaticProperties) { if (properties.isEmpty()) { @@ -144,7 +146,7 @@ class ClassTranslator private constructor( if (isTrait()) return 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) invocationArguments += initializer @@ -157,21 +159,20 @@ class ClassTranslator private constructor( val classDescriptor = constructorDescriptor.containingDeclaration val constructorScope = classContext.getScopeForDescriptor(constructorDescriptor) - var context = classContext.innerWithUsageTracker(constructorScope, 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) + var context = classContext + .innerWithUsageTracker(constructorScope, constructorDescriptor) + .innerContextWithAliased(receiverDescriptor, thisNameRef) - val outerName = context.getOuterClassReference(classDescriptor); + val outerClassName = context.getOuterClassReference(classDescriptor); val outerClass = DescriptorUtils.getContainingClass(classDescriptor) - if (outerClass != null && outerName != null) { - val outerClassRef = outerClass.thisAsReceiverParameter - context = context.innerContextWithAliased(outerClassRef, outerName.makeRef()) + if (outerClassName != null) { + val outerClassReceiver = outerClass!!.thisAsReceiverParameter + context = context.innerContextWithAliased(outerClassReceiver, outerClassName.makeRef()) } // Translate constructor body @@ -191,9 +192,9 @@ class ClassTranslator private constructor( // Add parameter for outer instance val leadingArgs = mutableListOf() - if (outerName != null) { - constructorFunction.parameters.add(0, JsParameter(outerName)) - leadingArgs += outerName.makeRef() + if (outerClassName != null) { + constructorFunction.parameters.add(0, JsParameter(outerClassName)) + leadingArgs += outerClassName.makeRef() } constructorFunction.parameters += JsParameter(thisName) @@ -212,8 +213,8 @@ class ClassTranslator private constructor( if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) { superCallGenerators += { val usageTracker = context.usageTracker()!! - val closure = context.getLocalClassClosure(classDescriptor).orEmpty().map { - usageTracker.capturedDescriptorToJsName[it]?.makeRef() as? JsExpression ?: JsLiteral.NULL + val closure = context.getClassOrConstructorClosure(classDescriptor).orEmpty().map { + usageTracker.getNameForCapturedDescriptor(it)!!.makeRef() } it += JsInvocation(Namer.getFunctionCallRef(referenceToClass), listOf(thisNameRef) + closure + leadingArgs).makeStmt() } @@ -242,9 +243,8 @@ class ClassTranslator private constructor( private fun emitConstructors(nonConstructorContext: TranslationContext) { // Build map that maps constructor to all constructors called via this() val constructorMap = allConstructors.map { it.descriptor to it }.toMap() - val primaryConstructor = this.primaryConstructor - val thisCalls = allConstructors.map { + val thisCalls = secondaryConstructors.map { val set = mutableSetOf() val descriptor = it.descriptor if (descriptor is ConstructorDescriptor) { @@ -256,72 +256,46 @@ class ClassTranslator private constructor( } } } - 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) - - // Emit constructors + val sortedConstructors = DFS.topologicalOrder(allConstructors.asIterable()) { thisCalls[it].orEmpty() }.reversed() for (constructor in sortedConstructors) { constructor.superCallGenerator() val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!! - val usageTracker = constructor.context.usageTracker() ?: continue + val usageTracker = constructor.context.usageTracker()!! val nonConstructorCapturedVars = nonConstructorUsageTracker.capturedDescriptors val constructorCapturedVars = usageTracker.capturedDescriptors - val capturedVars = (nonConstructorCapturedVars.asSequence() + - constructorCapturedVars.asSequence().filter { it !in nonConstructorCapturedVars }).toList() + val capturedVars = (nonConstructorCapturedVars + constructorCapturedVars).distinct() val descriptor = constructor.descriptor - nonConstructorContext.putLocalClassClosure(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) } - } + nonConstructorContext.putClassOrConstructorClosure(descriptor, capturedVars) } } private fun addClosureParameters(constructor: ConstructorInfo, nonConstructorContext: TranslationContext, dataClassGenerator: JsDataClassGenerator) { val usageTracker = constructor.context.usageTracker()!! - val capturedVars = context().getLocalClassClosure(constructor.descriptor) ?: return + val capturedVars = context().getClassOrConstructorClosure(constructor.descriptor) ?: return val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!! val function = constructor.function + val additionalStatements = mutableListOf() for ((i, capturedVar) in capturedVars.withIndex()) { val fieldName = nonConstructorUsageTracker.capturedDescriptorToJsName[capturedVar] val name = usageTracker.capturedDescriptorToJsName[capturedVar] ?: fieldName!! function.parameters.add(i, JsParameter(name)) 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) } } + + function.body.statements.addAll(0, additionalStatements); } private fun getSuperclassReferences(declarationContext: TranslationContext): JsExpression { 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 09dc637f9e9..cfa31655b8e 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 @@ -62,7 +62,7 @@ public class DeclarationBodyVisitor extends TranslatorVisitor { } @Override - public Void visitClass(@NotNull KtClass declaration, TranslationContext context) { + public Void visitClassOrObject(@NotNull KtClassOrObject declaration, TranslationContext context) { staticResult.addAll(ClassTranslator.translate(declaration, context).getProperties()); return null; } @@ -82,12 +82,6 @@ public class DeclarationBodyVisitor extends TranslatorVisitor { return null; } - @Override - public Void visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, TranslationContext context) { - staticResult.addAll(ClassTranslator.translate(declaration, context).getProperties()); - return null; - } - @Override public Void visitNamedFunction(@NotNull KtNamedFunction expression, TranslationContext context) { FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt index efa274bc0cb..d4489cc5a2d 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt @@ -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.utils.BindingUtils.getPropertyDescriptor import org.jetbrains.kotlin.js.translate.utils.JsAstUtils -import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.psi.KtAnonymousInitializer -import org.jetbrains.kotlin.psi.KtObjectDeclaration -import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.* class FileDeclarationVisitor( 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 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? { context!! // hack diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java index 382659fa143..8425bcdbff6 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java @@ -522,15 +522,12 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull public JsNode visitObjectLiteralExpression(@NotNull KtObjectLiteralExpression expression, @NotNull TranslationContext context) { ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), expression.getObjectDeclaration()); - JsScope scope = context.getScopeForDescriptor(descriptor); - TranslationContext classContext = context.innerWithUsageTracker(scope, descriptor); - - ClassTranslator.TranslationResult result = ClassTranslator.translate(expression.getObjectDeclaration(), classContext); + ClassTranslator.TranslationResult result = translateClassOrObject(expression.getObjectDeclaration(), descriptor, context); List properties = result.getProperties(); context.getDefinitionPlace().getProperties().addAll(properties); JsExpression constructor = context.getQualifiedReference(descriptor); - List closure = context.getLocalClassClosure(descriptor); + List closure = context.getClassOrConstructorClosure(descriptor); List closureArgs = new ArrayList(); if (closure != null) { for (DeclarationDescriptor capturedValue : closure) { @@ -601,11 +598,17 @@ public final class ExpressionVisitor extends TranslatorVisitor { @Override public JsNode visitClass(@NotNull KtClass klass, TranslationContext context) { ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), klass); - JsScope scope = context.getScopeForDescriptor(descriptor); - TranslationContext classContext = context.innerWithUsageTracker(scope, descriptor); - - context.getDefinitionPlace().getProperties().addAll(ClassTranslator.translate(klass, classContext).getProperties()); - + context.getDefinitionPlace().getProperties().addAll(translateClassOrObject(klass, descriptor, context).getProperties()); 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); + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java index 70f558a218c..5910bd49952 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java @@ -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.general.AbstractTranslator; 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.AstUtilsKt; import org.jetbrains.kotlin.lexer.KtTokens; @@ -149,7 +148,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator { return; } if (hasAncestorClass(bindingContext(), classDeclaration)) { - ResolvedCall superCall = BindingUtils.getSuperCall(bindingContext(), classDeclaration); + ResolvedCall superCall = getSuperCall(bindingContext(), classDeclaration); if (superCall == null) return; if (classDeclaration instanceof KtEnumEntry) { @@ -163,9 +162,11 @@ public final class ClassInitializerTranslator extends AbstractTranslator { ConstructorDescriptor superDescriptor = (ConstructorDescriptor) superCall.getResultingDescriptor(); - List superclassClosure = context.getLocalClassClosure(superDescriptor); - UsageTracker tracker = context.usageTracker(); - if (superclassClosure != null && tracker != null) { + List superclassClosure = context.getClassOrConstructorClosure(superDescriptor); + if (superclassClosure != null) { + UsageTracker tracker = context.usageTracker(); + assert tracker != null : "Closure exists, therefore UsageTracker must exist too. Translating constructor of " + + descriptor; for (DeclarationDescriptor capturedValue : superclassClosure) { tracker.used(capturedValue); arguments.add(tracker.getCapturedDescriptorToJsName().get(capturedValue).makeRef()); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/ManglingUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/ManglingUtils.java index 8af1e2af610..77bca0822dd 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/ManglingUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/ManglingUtils.java @@ -85,7 +85,8 @@ public class ManglingUtils { } Collections.reverse(parts); - return StringUtil.join(parts, "$") + "$"; + String result = StringUtil.join(parts, "$"); + return !result.isEmpty() ? result + "$" : ""; } @NotNull diff --git a/js/js.translator/testData/closure/cases/deepInnerClassInLocalClass.kt b/js/js.translator/testData/closure/cases/deepInnerClassInLocalClass.kt new file mode 100644 index 00000000000..967870deac9 --- /dev/null +++ b/js/js.translator/testData/closure/cases/deepInnerClassInLocalClass.kt @@ -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" +} \ No newline at end of file diff --git a/js/js.translator/testData/closure/cases/deepInnerClassInLocalClassFromExtension.kt b/js/js.translator/testData/closure/cases/deepInnerClassInLocalClassFromExtension.kt new file mode 100644 index 00000000000..bd168d1fdce --- /dev/null +++ b/js/js.translator/testData/closure/cases/deepInnerClassInLocalClassFromExtension.kt @@ -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" +} \ No newline at end of file diff --git a/js/js.translator/testData/closure/cases/implicitGenericReceiverInExtensionInLocalClass.kt b/js/js.translator/testData/closure/cases/implicitGenericReceiverInExtensionInLocalClass.kt new file mode 100644 index 00000000000..747d0f8c066 --- /dev/null +++ b/js/js.translator/testData/closure/cases/implicitGenericReceiverInExtensionInLocalClass.kt @@ -0,0 +1,17 @@ +package foo + +class A { + fun test() = 23 +} + +fun T.foo(): Int { + class B { + fun foo() = test() + } + return B().foo() +} + +fun box(): String { + assertEquals(23, A().foo()) + return "OK" +} \ No newline at end of file