KT-11030 Refactor translation of class to support secondary constructors of local classes
This commit is contained in:
+1
-2
@@ -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<JsExpression>()
|
||||
if (closure != null) {
|
||||
closureArgs = closure.asSequence().map { context.getParameterNameRefForInvocation(it) }.toList()
|
||||
|
||||
@@ -98,7 +98,7 @@ public final class StaticContext {
|
||||
private final Map<JsScope, JsFunction> scopeToFunction = Maps.newHashMap();
|
||||
|
||||
@NotNull
|
||||
private final Map<ClassDescriptor, List<DeclarationDescriptor>> localClassesClosure = Maps.newHashMap();
|
||||
private final Map<MemberDescriptor, List<DeclarationDescriptor>> 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<DeclarationDescriptor> closure) {
|
||||
public void putLocalClassClosure(@NotNull MemberDescriptor localClass, @NotNull List<DeclarationDescriptor> closure) {
|
||||
localClassesClosure.put(localClass, Lists.newArrayList(closure));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<DeclarationDescriptor> getLocalClassClosure(@NotNull ClassDescriptor localClass) {
|
||||
public List<DeclarationDescriptor> getLocalClassClosure(@NotNull MemberDescriptor localClass) {
|
||||
List<DeclarationDescriptor> result = localClassesClosure.get(localClass);
|
||||
return result != null ? Lists.newArrayList(result) : null;
|
||||
}
|
||||
|
||||
+6
-15
@@ -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<DeclarationDescriptor> closure) {
|
||||
public void putLocalClassClosure(@NotNull MemberDescriptor localClass, @NotNull List<DeclarationDescriptor> closure) {
|
||||
staticContext.putLocalClassClosure(localClass, closure);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<DeclarationDescriptor> getLocalClassClosure(@NotNull ClassDescriptor localClass) {
|
||||
public List<DeclarationDescriptor> 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();
|
||||
|
||||
@@ -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<DeclarationDescriptor, JsName>
|
||||
get() = captured
|
||||
|
||||
val capturedDescriptors: Set<DeclarationDescriptor>
|
||||
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 =
|
||||
|
||||
+240
-152
@@ -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<JsExpression>()
|
||||
private val secondaryConstructors = mutableListOf<ConstructorInfo>()
|
||||
private val secondaryConstructorProperties = mutableListOf<JsPropertyInitializer>()
|
||||
private var primaryConstructor: ConstructorInfo? = null
|
||||
|
||||
private fun translate(declarationContext: TranslationContext = context()): JsInvocation {
|
||||
return JsInvocation(context().namer().classCreateInvocation(descriptor), getClassCreateInvocationArguments(declarationContext))
|
||||
private fun translate(): List<JsPropertyInitializer> {
|
||||
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<JsExpression> {
|
||||
var context = declarationContext
|
||||
val invocationArguments = ArrayList<JsExpression>()
|
||||
private fun getClassCreateInvocationArguments() {
|
||||
var context = context()
|
||||
|
||||
val properties = SmartList<JsPropertyInitializer>()
|
||||
val staticProperties = SmartList<JsPropertyInitializer>()
|
||||
@@ -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<JsStatement>) -> 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<JsExpression>()
|
||||
|
||||
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<JsStatement>()
|
||||
for (partGenerator in superCallGenerators) {
|
||||
partGenerator(additionalStatements)
|
||||
}
|
||||
constructorFunction.body.statements.addAll(0, additionalStatements)
|
||||
}
|
||||
|
||||
secondaryConstructors += ConstructorInfo(constructorFunction, context, constructorDescriptor, compositeSuperCallGenerator)
|
||||
secondaryConstructorProperties += constructorInitializer
|
||||
}
|
||||
|
||||
private val allConstructors: Sequence<ConstructorInfo>
|
||||
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<ConstructorInfo>()
|
||||
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<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)
|
||||
|
||||
// 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<JsExpression>(getClassReference(supertypeDescriptor))
|
||||
}
|
||||
|
||||
val supertypeConstructors = HashSet<TypeConstructor>()
|
||||
val supertypeConstructors = mutableSetOf<TypeConstructor>()
|
||||
for (type in supertypes) {
|
||||
supertypeConstructors.add(type.constructor)
|
||||
supertypeConstructors += type.constructor
|
||||
}
|
||||
val sortedAllSuperTypes = topologicallySortSuperclassesAndRecordAllInstances(descriptor.defaultType, HashMap<TypeConstructor, Set<KotlinType>>(), HashSet<TypeConstructor>())
|
||||
val supertypesRefs = ArrayList<JsExpression>()
|
||||
val sortedAllSuperTypes = topologicallySortSuperclassesAndRecordAllInstances(
|
||||
descriptor.defaultType,
|
||||
mutableMapOf<TypeConstructor, Set<KotlinType>>(),
|
||||
mutableSetOf<TypeConstructor>()
|
||||
)
|
||||
val supertypesRefs = mutableListOf<JsExpression>()
|
||||
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<JsPropertyInitializer>) {
|
||||
private fun translatePropertiesAsConstructorParameters(classDeclarationContext: TranslationContext,
|
||||
result: MutableList<JsPropertyInitializer>) {
|
||||
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<JsPropertyInitializer>) {
|
||||
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<JsPropertyInitializer> {
|
||||
val result = arrayListOf<JsPropertyInitializer>()
|
||||
|
||||
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<JsStatement>()
|
||||
|
||||
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<JsExpression>()
|
||||
|
||||
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) = { }
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
|
||||
@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;
|
||||
}
|
||||
|
||||
+6
-7
@@ -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<KtDelegatedSuperTypeEntry>();
|
||||
|
||||
private class Field (val name: String, val generateField: Boolean)
|
||||
private val fields = HashMap<KtDelegatedSuperTypeEntry, Field>()
|
||||
private val fields = mutableMapOf<KtDelegatedSuperTypeEntry, Field>()
|
||||
|
||||
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<JsStatement>) {
|
||||
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<JsPropertyInitializer>) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user