JS: insert dummy initializers for property that initialized on init block or in secondary constructors. See KT-8211

This commit is contained in:
Alexey Andreev
2017-01-17 17:23:47 +03:00
parent a927770935
commit af1b7c49b4
3 changed files with 107 additions and 3 deletions
@@ -3611,6 +3611,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
doTest(fileName);
}
@TestMetadata("propertyInitializationOrder.kt")
public void testPropertyInitializationOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/initialize/propertyInitializationOrder.kt");
doTest(fileName);
}
@TestMetadata("rootPackageValInit.kt")
public void testRootPackageValInit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/initialize/rootPackageValInit.kt");
@@ -16,16 +16,21 @@
package org.jetbrains.kotlin.js.translate.initializer;
import org.jetbrains.kotlin.js.backend.ast.JsExpression;
import org.jetbrains.kotlin.js.backend.ast.JsStatement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.declaration.PropertyTranslatorKt;
import org.jetbrains.kotlin.js.translate.general.TranslatorVisitor;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.types.KotlinType;
import static org.jetbrains.kotlin.js.translate.general.Translation.translateAsStatementAndMergeInBlockIfNeeded;
import static org.jetbrains.kotlin.js.translate.initializer.InitializerUtils.generateInitializerForDelegate;
@@ -49,10 +54,19 @@ public final class InitializerVisitor extends TranslatorVisitor<Void> {
assert value != null;
statement = generateInitializerForProperty(context, descriptor, value);
}
if (delegate != null) {
else if (delegate != null) {
assert value != null;
statement = generateInitializerForDelegate(descriptor, value);
}
else if (Boolean.TRUE.equals(context.bindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, descriptor))) {
JsExpression defaultValue = generateDefaultValue(descriptor, context);
statement = TranslationUtils.assignmentToBackingField(context, descriptor, defaultValue).makeStmt();
}
else if (JsDescriptorUtils.isSimpleFinalProperty(descriptor)) {
JsExpression defaultValue = generateDefaultValue(descriptor, context);
JsName propertyName = context.getNameForDescriptor(descriptor);
statement = JsAstUtils.assignment(new JsNameRef(propertyName, JsLiteral.THIS), defaultValue).makeStmt();
}
if (statement != null && !JsAstUtils.isEmptyStatement(statement)) {
context.addStatementsToCurrentBlock(JsAstUtils.flattenStatement(statement));
@@ -61,6 +75,24 @@ public final class InitializerVisitor extends TranslatorVisitor<Void> {
return null;
}
@NotNull
private static JsExpression generateDefaultValue(@NotNull PropertyDescriptor property, @NotNull TranslationContext context) {
if (property.isLateInit()) return Namer.getUndefinedExpression();
KotlinType type = property.getType();
if (KotlinBuiltIns.isInt(type) || KotlinBuiltIns.isFloat(type) || KotlinBuiltIns.isDouble(type) ||
KotlinBuiltIns.isByte(type) || KotlinBuiltIns.isShort(type)
) {
return context.program().getNumberLiteral(0);
}
else if (KotlinBuiltIns.isBoolean(type)) {
return JsLiteral.FALSE;
}
else {
return JsLiteral.NULL;
}
}
@Override
public Void visitAnonymousInitializer(@NotNull KtAnonymousInitializer initializer, @NotNull TranslationContext context) {
KtExpression initializerBody = initializer.getBody();
@@ -0,0 +1,66 @@
open class A {
val a: Int
open val b: Int
val c = 99
val d: Int = 555
get() = field
val e: Int
get() = field
@get:JsName("getF")
val f: Int
@get:JsName("getG")
val g: Int = 777
lateinit var h: String
init {
foo()
a = 23
b = 42
e = 987
f = 888
}
}
fun foo() {}
fun box(): String {
val aBody = js("A").toString()
val expectedRegex = build {
property("a")
field("b")
property("c")
field("d")
field("e")
field("f")
field("g")
property("h")
}
if (expectedRegex.find(aBody) == null) return "fail"
return "OK"
}
fun build(f: RegexBuilder.() -> Unit): Regex {
val builder = RegexBuilder()
builder.f()
return Regex(builder.string + "foo()", RegexOption.MULTILINE)
}
class RegexBuilder {
var string = ""
fun property(name: String) {
string += "this.$name\\s+=$ANY_CHARS"
}
fun field(name: String) {
string += "this.$name$FIELD_SUFFIX\\s+=$ANY_CHARS"
}
}
val ANY_CHARS = "(.|\n)+"
val FIELD_SUFFIX = "_[a-zA-Z0-9\\\$_]+"