KT-12868: if a property initializer is a complex expression that emits additional statements, emit these statements directly before assignment to the property, not at the beginning of constructor
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ fun box(): String {
|
||||
|
||||
if (A().s2 != "capturedOK") return "fail 1: ${A().s2}"
|
||||
|
||||
if (A("Test").s2 != "capturedTest") return "fail 1: ${A("Test").s2}"
|
||||
if (A("Test").s2 != "capturedTest") return "fail 2: ${A("Test").s2}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -38,4 +38,12 @@ public class InitializerTest extends SingleFileTranslationTest {
|
||||
public void testDeclarationInitializedWithThrow() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testComplexPropertyInitializer() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testComplexTopLevelPropertyInitializer() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,6 +311,10 @@ public class TranslationContext {
|
||||
dynamicContext.jsBlock().getStatements().add(statement);
|
||||
}
|
||||
|
||||
public void addStatementsToCurrentBlock(@NotNull Collection<JsStatement> statements) {
|
||||
dynamicContext.jsBlock().getStatements().addAll(statements);
|
||||
}
|
||||
|
||||
public void addStatementsToCurrentBlockFrom(@NotNull TranslationContext context) {
|
||||
addStatementsToCurrentBlockFrom(context.dynamicContext().jsBlock());
|
||||
}
|
||||
|
||||
+5
-12
@@ -36,16 +36,11 @@ class FileDeclarationVisitor(
|
||||
) : DeclarationBodyVisitor(initializers, SmartList(), scope) {
|
||||
|
||||
private val initializer = JsAstUtils.createFunctionWithEmptyBody(context.scope())
|
||||
private val initializerContext = context.contextWithScope(initializer)
|
||||
private val initializerStatements = initializer.body.statements
|
||||
private val initializerVisitor = InitializerVisitor(initializerStatements)
|
||||
private val initializerContext = context.contextWithScope(initializer).innerBlock(initializer.body)
|
||||
private val initializerVisitor = InitializerVisitor()
|
||||
|
||||
fun computeInitializer(): JsFunction? {
|
||||
if (initializerStatements.isEmpty()) {
|
||||
return null
|
||||
} else {
|
||||
return initializer
|
||||
}
|
||||
return if (initializer.body.statements.isNotEmpty()) initializer else null
|
||||
}
|
||||
|
||||
override fun visitClassOrObject(declaration: KtClassOrObject, context: TranslationContext?): Void? {
|
||||
@@ -61,12 +56,10 @@ class FileDeclarationVisitor(
|
||||
if (initializer != null) {
|
||||
val value = Translation.translateAsExpression(initializer, initializerContext)
|
||||
val propertyDescriptor: PropertyDescriptor = getPropertyDescriptor(context.bindingContext(), expression)
|
||||
initializerStatements.add(generateInitializerForProperty(context, propertyDescriptor, value))
|
||||
this.initializer.body.statements += generateInitializerForProperty(context, propertyDescriptor, value)
|
||||
}
|
||||
|
||||
val delegate = generateInitializerForDelegate(context, expression)
|
||||
if (delegate != null)
|
||||
initializerStatements.add(delegate)
|
||||
generateInitializerForDelegate(context, expression)?.let { this.initializer.body.statements += it }
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
+8
-20
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.js.translate.initializer;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
|
||||
@@ -52,7 +51,6 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
||||
@NotNull
|
||||
private final KtClassOrObject classDeclaration;
|
||||
@NotNull
|
||||
private final List<JsStatement> initializerStatements = new SmartList<JsStatement>();
|
||||
private final JsFunction initFunction;
|
||||
private final TranslationContext context;
|
||||
|
||||
@@ -105,26 +103,15 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
||||
if (primaryConstructor != null) {
|
||||
initFunction.getBody().getStatements().addAll(setDefaultValueForArguments(primaryConstructor, context()));
|
||||
|
||||
mayBeAddCallToSuperMethod(initFunction, classDescriptor);
|
||||
|
||||
//NOTE: while we translate constructor parameters we also add property initializer statements
|
||||
// for properties declared as constructor parameters
|
||||
initFunction.getParameters().addAll(translatePrimaryConstructorParameters());
|
||||
|
||||
mayBeAddCallToSuperMethod(initFunction, classDescriptor);
|
||||
}
|
||||
|
||||
delegationTranslator.addInitCode(initializerStatements);
|
||||
new InitializerVisitor(initializerStatements).traverseContainer(classDeclaration, context());
|
||||
|
||||
List<JsStatement> statements = initFunction.getBody().getStatements();
|
||||
|
||||
for (JsStatement statement : initializerStatements) {
|
||||
if (statement instanceof JsBlock) {
|
||||
statements.addAll(((JsBlock) statement).getStatements());
|
||||
}
|
||||
else {
|
||||
statements.add(statement);
|
||||
}
|
||||
}
|
||||
delegationTranslator.addInitCode(initFunction.getBody().getStatements());
|
||||
new InitializerVisitor().traverseContainer(classDeclaration, context().innerBlock(initFunction.getBody()));
|
||||
|
||||
return initFunction;
|
||||
}
|
||||
@@ -170,7 +157,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
||||
JsExpression expression = CallTranslator.translate(context(), superCall, null);
|
||||
JsExpression fixedInvocation = AstUtilsKt.toInvocationWith(expression, Collections.<JsExpression>emptyList(), 0,
|
||||
JsLiteral.THIS);
|
||||
initializerStatements.add(0, fixedInvocation.makeStmt());
|
||||
initFunction.getBody().getStatements().add(fixedInvocation.makeStmt());
|
||||
}
|
||||
else {
|
||||
List<JsExpression> arguments = new ArrayList<JsExpression>();
|
||||
@@ -217,7 +204,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
||||
JsInvocation call = new JsInvocation(Namer.getFunctionCallRef(Namer.superMethodNameRef(initializer.getName())));
|
||||
call.getArguments().add(JsLiteral.THIS);
|
||||
call.getArguments().addAll(arguments);
|
||||
initializerStatements.add(0, call.makeStmt());
|
||||
initFunction.getBody().getStatements().add(call.makeStmt());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -252,6 +239,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
||||
}
|
||||
|
||||
private void addInitializerOrPropertyDefinition(@NotNull JsNameRef initialValue, @NotNull PropertyDescriptor propertyDescriptor) {
|
||||
initializerStatements.add(InitializerUtils.generateInitializerForProperty(context(), propertyDescriptor, initialValue));
|
||||
initFunction.getBody().getStatements().add(
|
||||
InitializerUtils.generateInitializerForProperty(context(), propertyDescriptor, initialValue));
|
||||
}
|
||||
}
|
||||
|
||||
+6
-11
@@ -24,20 +24,12 @@ import org.jetbrains.kotlin.js.translate.general.TranslatorVisitor;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.js.translate.general.Translation.translateAsStatementAndMergeInBlockIfNeeded;
|
||||
import static org.jetbrains.kotlin.js.translate.initializer.InitializerUtils.generateInitializerForDelegate;
|
||||
import static org.jetbrains.kotlin.js.translate.initializer.InitializerUtils.generateInitializerForProperty;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getPropertyDescriptor;
|
||||
|
||||
public final class InitializerVisitor extends TranslatorVisitor<Void> {
|
||||
private final List<JsStatement> result;
|
||||
|
||||
public InitializerVisitor(List<JsStatement> result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void emptyResult(@NotNull TranslationContext context) {
|
||||
return null;
|
||||
@@ -50,12 +42,14 @@ public final class InitializerVisitor extends TranslatorVisitor<Void> {
|
||||
JsStatement statement = generateInitializerForProperty(context, getPropertyDescriptor(context.bindingContext(), property),
|
||||
Translation.translateAsExpression(initializer, context));
|
||||
if (!JsAstUtils.isEmptyStatement(statement)) {
|
||||
result.add(statement);
|
||||
context.addStatementsToCurrentBlock(JsAstUtils.flattenStatement(statement));
|
||||
}
|
||||
}
|
||||
|
||||
JsStatement delegate = generateInitializerForDelegate(context, property);
|
||||
if (delegate != null) result.add(delegate);
|
||||
if (delegate != null) {
|
||||
context.addStatementToCurrentBlock(delegate);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -64,7 +58,8 @@ public final class InitializerVisitor extends TranslatorVisitor<Void> {
|
||||
public Void visitAnonymousInitializer(@NotNull KtAnonymousInitializer initializer, @NotNull TranslationContext context) {
|
||||
KtExpression initializerBody = initializer.getBody();
|
||||
if (initializerBody != null) {
|
||||
result.add(translateAsStatementAndMergeInBlockIfNeeded(initializerBody, context));
|
||||
context.addStatementsToCurrentBlock(JsAstUtils.flattenStatement(
|
||||
translateAsStatementAndMergeInBlockIfNeeded(initializerBody, context)));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package foo
|
||||
|
||||
fun f() {}
|
||||
|
||||
class A(selector: Boolean, val y: Int) {
|
||||
val x = if (selector) { f(); y } else 999
|
||||
val z = if (selector) { f(); x + 1 } else 999
|
||||
}
|
||||
|
||||
class B(selector: Boolean, val y: Int, val x: Int = if (selector) { f(); y } else { 999 })
|
||||
|
||||
fun box(): String {
|
||||
val a = A(true, 23)
|
||||
if (a.x != 23) return "fail: wrong evaluation order for property initializer (1): ${a.x}"
|
||||
if (a.z != 24) return "fail: wrong evaluation order for property initializer (2): ${a.z}"
|
||||
|
||||
val b = B(true, 23)
|
||||
if (b.x != 23) return "fail: wrong evaluation order for default constructor arguments"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package foo
|
||||
|
||||
fun f() {}
|
||||
fun selector() = true
|
||||
|
||||
val y = if (selector()) 23 else throw Exception()
|
||||
val x = if (selector()) { f(); y + 1 } else 999
|
||||
val z = if (selector()) { f(); x + 1 } else 999
|
||||
|
||||
fun box(): String {
|
||||
if (x != 24) return "fail1: $x"
|
||||
if (z != 25) return "fail2: $z"
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user