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
|
||||
// Presumably, KT-11823
|
||||
// Presumably, KT-11996
|
||||
/*public void testDeepInnerClassInLocalClass() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
@@ -185,4 +185,8 @@ public final class ClosureTest extends SingleFileTranslationTest {
|
||||
public void testImplicitGenericReceiverInExtensionInLocalClass() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testClosureThisInLambdaInsideObject() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,14 @@ public class NestedTypesTest extends SingleFileTranslationTest {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testImplicitOuterThisFromLambda() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testImplicitOuterThisFromLocalClass() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<String> additionalJsFiles(@NotNull EcmaVersion ecmaVersion) {
|
||||
|
||||
+1
-1
@@ -211,7 +211,7 @@ object ConstructorCallCase : FunctionCallCase() {
|
||||
|
||||
val constructorDescriptor = callableDescriptor as 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)) {
|
||||
return JsNew(functionRef, closureArgs + arguments)
|
||||
|
||||
+29
-2
@@ -466,11 +466,38 @@ public class TranslationContext {
|
||||
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
|
||||
public JsExpression getParameterNameRefForInvocation(@NotNull DeclarationDescriptor descriptor) {
|
||||
public JsExpression getArgumentForClosureConstructor(@NotNull DeclarationDescriptor descriptor) {
|
||||
JsExpression alias = getAliasForDescriptor(descriptor);
|
||||
if (alias != null) return alias;
|
||||
if (descriptor instanceof ReceiverParameterDescriptor) return JsLiteral.THIS;
|
||||
if (descriptor instanceof ReceiverParameterDescriptor) {
|
||||
return getDispatchReceiver((ReceiverParameterDescriptor) descriptor);
|
||||
}
|
||||
return getNameForDescriptor(descriptor).makeRef();
|
||||
}
|
||||
|
||||
|
||||
@@ -134,11 +134,15 @@ class UsageTracker(
|
||||
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 == container) return false
|
||||
|
||||
// This code is necessary for one use case. If we don't treat `O::this` as a free variable of lambda, we'll get
|
||||
// `this` in generated JS. `this` is generated since it's placed in aliasing context for `O::this`, so we will get
|
||||
// it instead of generating FQN. However, we can't refer to `this` from lambda, since `this` points not to an instance of `C`,
|
||||
// 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) {
|
||||
val containingClass = getParentOfType(containingDescriptor, ClassDescriptor::class.java, 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>();
|
||||
if (closure != null) {
|
||||
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) {
|
||||
if (capturedDescriptor == tracker.containingDescriptor) continue
|
||||
|
||||
val capturedRef = invokingContext.getParameterNameRefForInvocation(capturedDescriptor)
|
||||
val capturedRef = invokingContext.getArgumentForClosureConstructor(capturedDescriptor)
|
||||
var additionalArgs = listOf(capturedRef)
|
||||
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