diff --git a/compiler/testData/codegen/box/localClasses/kt3584.kt b/compiler/testData/codegen/box/localClasses/kt3584.kt index 06dd0e8e2cb..21cdb764faa 100644 --- a/compiler/testData/codegen/box/localClasses/kt3584.kt +++ b/compiler/testData/codegen/box/localClasses/kt3584.kt @@ -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" } \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InitializerTest.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InitializerTest.java index 11cc37c4d7c..ce6abb86a2f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InitializerTest.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InitializerTest.java @@ -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(); + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index 9002b608767..13ddb65ddc1 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -311,6 +311,10 @@ public class TranslationContext { dynamicContext.jsBlock().getStatements().add(statement); } + public void addStatementsToCurrentBlock(@NotNull Collection statements) { + dynamicContext.jsBlock().getStatements().addAll(statements); + } + public void addStatementsToCurrentBlockFrom(@NotNull TranslationContext context) { addStatementsToCurrentBlockFrom(context.dynamicContext().jsBlock()); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt index a05590303bd..f65a8b51458 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt @@ -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 } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java index b27ad5e9c15..7e03197ef11 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java @@ -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 initializerStatements = new SmartList(); 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 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.emptyList(), 0, JsLiteral.THIS); - initializerStatements.add(0, fixedInvocation.makeStmt()); + initFunction.getBody().getStatements().add(fixedInvocation.makeStmt()); } else { List arguments = new ArrayList(); @@ -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)); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/InitializerVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/InitializerVisitor.java index 686be239c20..1a80cedaeee 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/InitializerVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/InitializerVisitor.java @@ -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 { - private final List result; - - public InitializerVisitor(List result) { - this.result = result; - } - @Override protected Void emptyResult(@NotNull TranslationContext context) { return null; @@ -50,12 +42,14 @@ public final class InitializerVisitor extends TranslatorVisitor { 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 { 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; } diff --git a/js/js.translator/testData/initialize/cases/complexPropertyInitializer.kt b/js/js.translator/testData/initialize/cases/complexPropertyInitializer.kt new file mode 100644 index 00000000000..43b1c28af48 --- /dev/null +++ b/js/js.translator/testData/initialize/cases/complexPropertyInitializer.kt @@ -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" +} \ No newline at end of file diff --git a/js/js.translator/testData/initialize/cases/complexTopLevelPropertyInitializer.kt b/js/js.translator/testData/initialize/cases/complexTopLevelPropertyInitializer.kt new file mode 100644 index 00000000000..1db26e40b4a --- /dev/null +++ b/js/js.translator/testData/initialize/cases/complexTopLevelPropertyInitializer.kt @@ -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" +} \ No newline at end of file