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().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"
|
return "OK"
|
||||||
}
|
}
|
||||||
@@ -38,4 +38,12 @@ public class InitializerTest extends SingleFileTranslationTest {
|
|||||||
public void testDeclarationInitializedWithThrow() throws Exception {
|
public void testDeclarationInitializedWithThrow() throws Exception {
|
||||||
checkFooBoxIsOk();
|
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);
|
dynamicContext.jsBlock().getStatements().add(statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addStatementsToCurrentBlock(@NotNull Collection<JsStatement> statements) {
|
||||||
|
dynamicContext.jsBlock().getStatements().addAll(statements);
|
||||||
|
}
|
||||||
|
|
||||||
public void addStatementsToCurrentBlockFrom(@NotNull TranslationContext context) {
|
public void addStatementsToCurrentBlockFrom(@NotNull TranslationContext context) {
|
||||||
addStatementsToCurrentBlockFrom(context.dynamicContext().jsBlock());
|
addStatementsToCurrentBlockFrom(context.dynamicContext().jsBlock());
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-12
@@ -36,16 +36,11 @@ class FileDeclarationVisitor(
|
|||||||
) : DeclarationBodyVisitor(initializers, SmartList(), scope) {
|
) : DeclarationBodyVisitor(initializers, SmartList(), scope) {
|
||||||
|
|
||||||
private val initializer = JsAstUtils.createFunctionWithEmptyBody(context.scope())
|
private val initializer = JsAstUtils.createFunctionWithEmptyBody(context.scope())
|
||||||
private val initializerContext = context.contextWithScope(initializer)
|
private val initializerContext = context.contextWithScope(initializer).innerBlock(initializer.body)
|
||||||
private val initializerStatements = initializer.body.statements
|
private val initializerVisitor = InitializerVisitor()
|
||||||
private val initializerVisitor = InitializerVisitor(initializerStatements)
|
|
||||||
|
|
||||||
fun computeInitializer(): JsFunction? {
|
fun computeInitializer(): JsFunction? {
|
||||||
if (initializerStatements.isEmpty()) {
|
return if (initializer.body.statements.isNotEmpty()) initializer else null
|
||||||
return null
|
|
||||||
} else {
|
|
||||||
return initializer
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitClassOrObject(declaration: KtClassOrObject, context: TranslationContext?): Void? {
|
override fun visitClassOrObject(declaration: KtClassOrObject, context: TranslationContext?): Void? {
|
||||||
@@ -61,12 +56,10 @@ class FileDeclarationVisitor(
|
|||||||
if (initializer != null) {
|
if (initializer != null) {
|
||||||
val value = Translation.translateAsExpression(initializer, initializerContext)
|
val value = Translation.translateAsExpression(initializer, initializerContext)
|
||||||
val propertyDescriptor: PropertyDescriptor = getPropertyDescriptor(context.bindingContext(), expression)
|
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)
|
generateInitializerForDelegate(context, expression)?.let { this.initializer.body.statements += it }
|
||||||
if (delegate != null)
|
|
||||||
initializerStatements.add(delegate)
|
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-20
@@ -17,7 +17,6 @@
|
|||||||
package org.jetbrains.kotlin.js.translate.initializer;
|
package org.jetbrains.kotlin.js.translate.initializer;
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*;
|
import com.google.dart.compiler.backend.js.ast.*;
|
||||||
import com.intellij.util.SmartList;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
|
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
|
||||||
@@ -52,7 +51,6 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private final KtClassOrObject classDeclaration;
|
private final KtClassOrObject classDeclaration;
|
||||||
@NotNull
|
@NotNull
|
||||||
private final List<JsStatement> initializerStatements = new SmartList<JsStatement>();
|
|
||||||
private final JsFunction initFunction;
|
private final JsFunction initFunction;
|
||||||
private final TranslationContext context;
|
private final TranslationContext context;
|
||||||
|
|
||||||
@@ -105,26 +103,15 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
|||||||
if (primaryConstructor != null) {
|
if (primaryConstructor != null) {
|
||||||
initFunction.getBody().getStatements().addAll(setDefaultValueForArguments(primaryConstructor, context()));
|
initFunction.getBody().getStatements().addAll(setDefaultValueForArguments(primaryConstructor, context()));
|
||||||
|
|
||||||
|
mayBeAddCallToSuperMethod(initFunction, classDescriptor);
|
||||||
|
|
||||||
//NOTE: while we translate constructor parameters we also add property initializer statements
|
//NOTE: while we translate constructor parameters we also add property initializer statements
|
||||||
// for properties declared as constructor parameters
|
// for properties declared as constructor parameters
|
||||||
initFunction.getParameters().addAll(translatePrimaryConstructorParameters());
|
initFunction.getParameters().addAll(translatePrimaryConstructorParameters());
|
||||||
|
|
||||||
mayBeAddCallToSuperMethod(initFunction, classDescriptor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegationTranslator.addInitCode(initializerStatements);
|
delegationTranslator.addInitCode(initFunction.getBody().getStatements());
|
||||||
new InitializerVisitor(initializerStatements).traverseContainer(classDeclaration, context());
|
new InitializerVisitor().traverseContainer(classDeclaration, context().innerBlock(initFunction.getBody()));
|
||||||
|
|
||||||
List<JsStatement> statements = initFunction.getBody().getStatements();
|
|
||||||
|
|
||||||
for (JsStatement statement : initializerStatements) {
|
|
||||||
if (statement instanceof JsBlock) {
|
|
||||||
statements.addAll(((JsBlock) statement).getStatements());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
statements.add(statement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return initFunction;
|
return initFunction;
|
||||||
}
|
}
|
||||||
@@ -170,7 +157,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
|||||||
JsExpression expression = CallTranslator.translate(context(), superCall, null);
|
JsExpression expression = CallTranslator.translate(context(), superCall, null);
|
||||||
JsExpression fixedInvocation = AstUtilsKt.toInvocationWith(expression, Collections.<JsExpression>emptyList(), 0,
|
JsExpression fixedInvocation = AstUtilsKt.toInvocationWith(expression, Collections.<JsExpression>emptyList(), 0,
|
||||||
JsLiteral.THIS);
|
JsLiteral.THIS);
|
||||||
initializerStatements.add(0, fixedInvocation.makeStmt());
|
initFunction.getBody().getStatements().add(fixedInvocation.makeStmt());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
List<JsExpression> arguments = new ArrayList<JsExpression>();
|
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())));
|
JsInvocation call = new JsInvocation(Namer.getFunctionCallRef(Namer.superMethodNameRef(initializer.getName())));
|
||||||
call.getArguments().add(JsLiteral.THIS);
|
call.getArguments().add(JsLiteral.THIS);
|
||||||
call.getArguments().addAll(arguments);
|
call.getArguments().addAll(arguments);
|
||||||
initializerStatements.add(0, call.makeStmt());
|
initFunction.getBody().getStatements().add(call.makeStmt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -252,6 +239,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addInitializerOrPropertyDefinition(@NotNull JsNameRef initialValue, @NotNull PropertyDescriptor propertyDescriptor) {
|
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.js.translate.utils.JsAstUtils;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
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.general.Translation.translateAsStatementAndMergeInBlockIfNeeded;
|
||||||
import static org.jetbrains.kotlin.js.translate.initializer.InitializerUtils.generateInitializerForDelegate;
|
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.initializer.InitializerUtils.generateInitializerForProperty;
|
||||||
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getPropertyDescriptor;
|
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getPropertyDescriptor;
|
||||||
|
|
||||||
public final class InitializerVisitor extends TranslatorVisitor<Void> {
|
public final class InitializerVisitor extends TranslatorVisitor<Void> {
|
||||||
private final List<JsStatement> result;
|
|
||||||
|
|
||||||
public InitializerVisitor(List<JsStatement> result) {
|
|
||||||
this.result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void emptyResult(@NotNull TranslationContext context) {
|
protected Void emptyResult(@NotNull TranslationContext context) {
|
||||||
return null;
|
return null;
|
||||||
@@ -50,12 +42,14 @@ public final class InitializerVisitor extends TranslatorVisitor<Void> {
|
|||||||
JsStatement statement = generateInitializerForProperty(context, getPropertyDescriptor(context.bindingContext(), property),
|
JsStatement statement = generateInitializerForProperty(context, getPropertyDescriptor(context.bindingContext(), property),
|
||||||
Translation.translateAsExpression(initializer, context));
|
Translation.translateAsExpression(initializer, context));
|
||||||
if (!JsAstUtils.isEmptyStatement(statement)) {
|
if (!JsAstUtils.isEmptyStatement(statement)) {
|
||||||
result.add(statement);
|
context.addStatementsToCurrentBlock(JsAstUtils.flattenStatement(statement));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JsStatement delegate = generateInitializerForDelegate(context, property);
|
JsStatement delegate = generateInitializerForDelegate(context, property);
|
||||||
if (delegate != null) result.add(delegate);
|
if (delegate != null) {
|
||||||
|
context.addStatementToCurrentBlock(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -64,7 +58,8 @@ public final class InitializerVisitor extends TranslatorVisitor<Void> {
|
|||||||
public Void visitAnonymousInitializer(@NotNull KtAnonymousInitializer initializer, @NotNull TranslationContext context) {
|
public Void visitAnonymousInitializer(@NotNull KtAnonymousInitializer initializer, @NotNull TranslationContext context) {
|
||||||
KtExpression initializerBody = initializer.getBody();
|
KtExpression initializerBody = initializer.getBody();
|
||||||
if (initializerBody != null) {
|
if (initializerBody != null) {
|
||||||
result.add(translateAsStatementAndMergeInBlockIfNeeded(initializerBody, context));
|
context.addStatementsToCurrentBlock(JsAstUtils.flattenStatement(
|
||||||
|
translateAsStatementAndMergeInBlockIfNeeded(initializerBody, context)));
|
||||||
}
|
}
|
||||||
return null;
|
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