KT-11823 Generate proper argument for closure constructor for this as a free variable. Give some explanation for function that generates closure arguments as well as a clear name. Give explanation to some code in UsageTracker.isSingletonReceiver, augmented by a test case.
This commit is contained in:
@@ -173,7 +173,7 @@ public final class ClosureTest extends SingleFileTranslationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: tests UsageTracker but encounters issue somewhere else. Uncomment when issue with `this` generation gets fixed
|
// TODO: tests UsageTracker but encounters issue somewhere else. Uncomment when issue with `this` generation gets fixed
|
||||||
// Presumably, KT-11823
|
// Presumably, KT-11996
|
||||||
/*public void testDeepInnerClassInLocalClass() throws Exception {
|
/*public void testDeepInnerClassInLocalClass() throws Exception {
|
||||||
checkFooBoxIsOk();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
@@ -185,4 +185,8 @@ public final class ClosureTest extends SingleFileTranslationTest {
|
|||||||
public void testImplicitGenericReceiverInExtensionInLocalClass() throws Exception {
|
public void testImplicitGenericReceiverInExtensionInLocalClass() throws Exception {
|
||||||
checkFooBoxIsOk();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testClosureThisInLambdaInsideObject() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,14 @@ public class NestedTypesTest extends SingleFileTranslationTest {
|
|||||||
checkFooBoxIsOk();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testImplicitOuterThisFromLambda() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testImplicitOuterThisFromLocalClass() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
protected List<String> additionalJsFiles(@NotNull EcmaVersion ecmaVersion) {
|
protected List<String> additionalJsFiles(@NotNull EcmaVersion ecmaVersion) {
|
||||||
|
|||||||
+1
-1
@@ -211,7 +211,7 @@ object ConstructorCallCase : FunctionCallCase() {
|
|||||||
|
|
||||||
val constructorDescriptor = callableDescriptor as ConstructorDescriptor
|
val constructorDescriptor = callableDescriptor as ConstructorDescriptor
|
||||||
val closure = context.getClassOrConstructorClosure(constructorDescriptor)
|
val closure = context.getClassOrConstructorClosure(constructorDescriptor)
|
||||||
val closureArgs = closure?.map { context.getParameterNameRefForInvocation(it) } ?: emptyList()
|
val closureArgs = closure?.map { context.getArgumentForClosureConstructor(it) } ?: emptyList()
|
||||||
|
|
||||||
if (constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) {
|
if (constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) {
|
||||||
return JsNew(functionRef, closureArgs + arguments)
|
return JsNew(functionRef, closureArgs + arguments)
|
||||||
|
|||||||
+29
-2
@@ -466,11 +466,38 @@ public class TranslationContext {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an expression to pass to a constructor of a closure function. I.e. consider the case:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* fun a(x) {
|
||||||
|
* fun b(y) = x + y
|
||||||
|
* return b
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Here, `x` is a free variable of `b`. Transform `a` into the following form:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* fun a(x) {
|
||||||
|
* fun b0(x0) = { y -> x0 * y }
|
||||||
|
* return b0(x)
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* This function generates arguments passed to newly generated `b0` closure, as well as for the similar case of local class and
|
||||||
|
* object expression.
|
||||||
|
*
|
||||||
|
* @param descriptor represents a free variable or, more generally, free declaration.
|
||||||
|
* @return expression to pass to a closure constructor.
|
||||||
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsExpression getParameterNameRefForInvocation(@NotNull DeclarationDescriptor descriptor) {
|
public JsExpression getArgumentForClosureConstructor(@NotNull DeclarationDescriptor descriptor) {
|
||||||
JsExpression alias = getAliasForDescriptor(descriptor);
|
JsExpression alias = getAliasForDescriptor(descriptor);
|
||||||
if (alias != null) return alias;
|
if (alias != null) return alias;
|
||||||
if (descriptor instanceof ReceiverParameterDescriptor) return JsLiteral.THIS;
|
if (descriptor instanceof ReceiverParameterDescriptor) {
|
||||||
|
return getDispatchReceiver((ReceiverParameterDescriptor) descriptor);
|
||||||
|
}
|
||||||
return getNameForDescriptor(descriptor).makeRef();
|
return getNameForDescriptor(descriptor).makeRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,11 +134,15 @@ class UsageTracker(
|
|||||||
val container = descriptor.containingDeclaration
|
val container = descriptor.containingDeclaration
|
||||||
if (!DescriptorUtils.isObject(container)) return false
|
if (!DescriptorUtils.isObject(container)) return false
|
||||||
|
|
||||||
// This is workaround, since sometimes translator generates wrong expression for `this` expressions.
|
// This code is necessary for one use case. If we don't treat `O::this` as a free variable of lambda, we'll get
|
||||||
// Presumably, it's related to KT-11823
|
// `this` in generated JS. `this` is generated since it's placed in aliasing context for `O::this`, so we will get
|
||||||
// TODO: remove when issue gets fixed.
|
// it instead of generating FQN. However, we can't refer to `this` from lambda, since `this` points not to an instance of `C`,
|
||||||
if (containingDescriptor == container) return false
|
// but to lambda function itself. We avoid it by treating `O::this` as a free variable.
|
||||||
|
// Example is:
|
||||||
|
//
|
||||||
|
// object A(val x: Int) {
|
||||||
|
// fun foo() = { x }
|
||||||
|
// }
|
||||||
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 == container) return false
|
if (containingClass == container) return false
|
||||||
|
|||||||
+1
-1
@@ -524,7 +524,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
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) {
|
||||||
closureArgs.add(context.getParameterNameRefForInvocation(capturedValue));
|
closureArgs.add(context.getArgumentForClosureConstructor(capturedValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -78,7 +78,7 @@ fun JsFunction.withCapturedParameters(context: TranslationContext, invokingConte
|
|||||||
for ((capturedDescriptor, name) in tracker.capturedDescriptorToJsName) {
|
for ((capturedDescriptor, name) in tracker.capturedDescriptorToJsName) {
|
||||||
if (capturedDescriptor == tracker.containingDescriptor) continue
|
if (capturedDescriptor == tracker.containingDescriptor) continue
|
||||||
|
|
||||||
val capturedRef = invokingContext.getParameterNameRefForInvocation(capturedDescriptor)
|
val capturedRef = invokingContext.getArgumentForClosureConstructor(capturedDescriptor)
|
||||||
var additionalArgs = listOf(capturedRef)
|
var additionalArgs = listOf(capturedRef)
|
||||||
var additionalParams = listOf(JsParameter(name))
|
var additionalParams = listOf(JsParameter(name))
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
object A {
|
||||||
|
val x = 23
|
||||||
|
val y = { x }
|
||||||
|
fun foo() = { x }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals(23, A.foo()())
|
||||||
|
assertEquals(23, A.y())
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// See KT-11823
|
||||||
|
package foo
|
||||||
|
|
||||||
|
class Outer(val x: Int) {
|
||||||
|
inner class Inner() {
|
||||||
|
fun foo() = { x }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals(23, Outer(23).Inner().foo()())
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
// See KT-11823
|
||||||
|
package foo
|
||||||
|
|
||||||
|
class Outer(val x: Int) {
|
||||||
|
inner class Inner() {
|
||||||
|
fun foo(): Int {
|
||||||
|
class A {
|
||||||
|
fun bar() = x
|
||||||
|
}
|
||||||
|
return A().bar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals(23, Outer(23).Inner().foo())
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user