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:
Alexey Andreev
2016-09-07 14:11:01 +03:00
parent 80361bf2fb
commit 16af7d667f
8 changed files with 67 additions and 44 deletions
+1 -1
View File
@@ -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());
}
@@ -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
}
@@ -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));
}
}
@@ -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"
}
@@ -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"
}