diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 1d87a312814..23dec00c143 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.util.ArrayUtil; @@ -71,6 +72,7 @@ import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; import org.jetbrains.kotlin.resolve.constants.ConstantValue; import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluatorKt; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt; @@ -1235,11 +1237,20 @@ public class ExpressionCodegen extends KtVisitor impleme @Override public StackValue visitConstantExpression(@NotNull KtConstantExpression expression, StackValue receiver) { - ConstantValue compileTimeValue = getCompileTimeConstant(expression, bindingContext); + ConstantValue compileTimeValue = getPrimitiveOrStringCompileTimeConstant(expression, bindingContext); assert compileTimeValue != null; return StackValue.constant(compileTimeValue.getValue(), expressionType(expression)); } + @Nullable + public static ConstantValue getPrimitiveOrStringCompileTimeConstant(@NotNull KtExpression expression, @NotNull BindingContext bindingContext) { + ConstantValue constant = getCompileTimeConstant(expression, bindingContext, false); + if (constant == null || ConstantExpressionEvaluatorKt.isStandaloneOnlyConstant(constant)) { + return null; + } + return constant; + } + @Nullable public static ConstantValue getCompileTimeConstant(@NotNull KtExpression expression, @NotNull BindingContext bindingContext) { return getCompileTimeConstant(expression, bindingContext, false); @@ -1248,15 +1259,45 @@ public class ExpressionCodegen extends KtVisitor impleme @Nullable public static ConstantValue getCompileTimeConstant( @NotNull KtExpression expression, - @NotNull BindingContext bindingContext, - boolean checkPure + @NotNull final BindingContext bindingContext, + boolean takeUpConstValsAsConst ) { CompileTimeConstant compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, bindingContext); - if (compileTimeValue == null) { + if (compileTimeValue == null || compileTimeValue.getUsesNonConstValAsConstant()) { return null; } - if (compileTimeValue.getUsesNonConstValAsConstant() || (checkPure && !compileTimeValue.getParameters().isPure())) return null; + if (!takeUpConstValsAsConst && compileTimeValue.getUsesVariableAsConstant()) { + final Ref containsNonInlinedVals = new Ref(false); + KtVisitor constantChecker = new KtVisitor() { + @Override + public Object visitSimpleNameExpression(@NotNull KtSimpleNameExpression expression, Object data) { + ResolvedCall resolvedCall = CallUtilKt.getResolvedCall(expression, bindingContext); + if (resolvedCall != null) { + CallableDescriptor callableDescriptor = resolvedCall.getResultingDescriptor(); + if (callableDescriptor instanceof PropertyDescriptor && + !JvmCodegenUtil.isInlinedJavaConstProperty((VariableDescriptor) callableDescriptor)) { + containsNonInlinedVals.set(true); + } + } + return null; + } + + @Override + public Object visitKtElement(@NotNull KtElement element, Object data) { + if (!containsNonInlinedVals.get()) { + element.acceptChildren(this); + } + return null; + } + }; + + expression.accept(constantChecker); + + if (containsNonInlinedVals.get()) { + return null; + } + } KotlinType expectedType = bindingContext.getType(expression); return compileTimeValue.toConstantValue(expectedType); @@ -2902,7 +2943,7 @@ public class ExpressionCodegen extends KtVisitor impleme expression.getRight(), reference); } else { - ConstantValue compileTimeConstant = getCompileTimeConstant(expression, bindingContext, true); + ConstantValue compileTimeConstant = getPrimitiveOrStringCompileTimeConstant(expression, bindingContext); if (compileTimeConstant != null) { return StackValue.constant(compileTimeConstant.getValue(), expressionType(expression)); } @@ -3025,7 +3066,7 @@ public class ExpressionCodegen extends KtVisitor impleme } private boolean isIntZero(KtExpression expr, Type exprType) { - ConstantValue exprValue = getCompileTimeConstant(expr, bindingContext); + ConstantValue exprValue = getPrimitiveOrStringCompileTimeConstant(expr, bindingContext); return isIntPrimitive(exprType) && exprValue != null && Integer.valueOf(0).equals(exprValue.getValue()); } @@ -3179,7 +3220,7 @@ public class ExpressionCodegen extends KtVisitor impleme @Override public StackValue visitPrefixExpression(@NotNull KtPrefixExpression expression, @NotNull StackValue receiver) { - ConstantValue compileTimeConstant = getCompileTimeConstant(expression, bindingContext, true); + ConstantValue compileTimeConstant = getPrimitiveOrStringCompileTimeConstant(expression, bindingContext); if (compileTimeConstant != null) { return StackValue.constant(compileTimeConstant.getValue(), expressionType(expression)); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java index fb724359e3e..a2577ad4e1a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.codegen.context.MethodContext; import org.jetbrains.kotlin.codegen.context.RootContext; import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor; import org.jetbrains.kotlin.load.kotlin.ModuleMapping; import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt; import org.jetbrains.kotlin.psi.KtFile; @@ -217,4 +218,9 @@ public class JvmCodegenUtil { public static String getMappingFileName(@NotNull String moduleName) { return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT; } + + public static boolean isInlinedJavaConstProperty(VariableDescriptor descriptor) { + if (!(descriptor instanceof JavaPropertyDescriptor)) return false; + return descriptor.isConst(); + } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index 75e1c15e09e..217fcc02490 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -224,7 +224,7 @@ public class PropertyCodegen { KtExpression defaultValue = p.getDefaultValue(); if (defaultValue != null) { - ConstantValue constant = ExpressionCodegen.getCompileTimeConstant(defaultValue, bindingContext); + ConstantValue constant = ExpressionCodegen.getCompileTimeConstant(defaultValue, bindingContext, true); assert state.getClassBuilderMode() != ClassBuilderMode.FULL || constant != null : "Default value for annotation parameter should be compile time value: " + defaultValue.getText(); if (constant != null) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index b554e7bd1ec..a3650948b3c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -1131,9 +1131,11 @@ public abstract class StackValue { } private boolean inlineJavaConstantIfNeeded(@NotNull Type type, @NotNull InstructionAdapter v) { - if (!isStaticPut) return false; - if (!(descriptor instanceof JavaPropertyDescriptor)) return false; - if (!AsmUtil.isPrimitive(this.type) && !this.type.equals(Type.getObjectType("java/lang/String"))) return false; + if (!JvmCodegenUtil.isInlinedJavaConstProperty(descriptor)) return false; + + assert AsmUtil.isPrimitive(this.type) || AsmTypes.JAVA_STRING_TYPE.equals(this.type) : + "Java const property should have primitive or string type: " + descriptor; + assert isStaticPut : "Java const property should be static" + descriptor; JavaPropertyDescriptor javaPropertyDescriptor = (JavaPropertyDescriptor) descriptor; ConstantValue constantValue = javaPropertyDescriptor.getCompileTimeInitializer(); diff --git a/compiler/testData/codegen/boxWithStdlib/const/constValInAnnotationDefault.kt b/compiler/testData/codegen/boxWithStdlib/const/constValInAnnotationDefault.kt new file mode 100644 index 00000000000..ae18c7a874a --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/const/constValInAnnotationDefault.kt @@ -0,0 +1,10 @@ +const val z = "OK" + +annotation class A(val value: String = z) + +@A +class Test + +fun box(): String { + return Test::class.java.getAnnotation(A::class.java).value +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/constProperty/noInline.kt b/compiler/testData/codegen/bytecodeText/constProperty/noInline.kt new file mode 100644 index 00000000000..e6ee45771b7 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constProperty/noInline.kt @@ -0,0 +1,7 @@ +const val z = 0 + +fun a() { + val x = z +} + +// 1 GETSTATIC NoInlineKt.z : I \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/constProperty/noInlineInCmp.kt b/compiler/testData/codegen/bytecodeText/constProperty/noInlineInCmp.kt new file mode 100644 index 00000000000..62cf1dbd8f3 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constProperty/noInlineInCmp.kt @@ -0,0 +1,8 @@ +const val z = 0 + +fun a() { + if (z == 2) { + } +} + +// 1 GETSTATIC NoInlineInCmpKt.z : I \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/constants/string.kt b/compiler/testData/codegen/bytecodeText/constants/string.kt new file mode 100644 index 00000000000..ea1c09b8141 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constants/string.kt @@ -0,0 +1,6 @@ +fun box(): String { + return "O" + "K".toString() + 1.toLong() +} + +// 1 LDC "OK1" +// 1 LDC \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/whenStringOptimization/nonInlinedConst.kt b/compiler/testData/codegen/bytecodeText/whenStringOptimization/nonInlinedConst.kt index 8563b273642..4585f5a23a0 100644 --- a/compiler/testData/codegen/bytecodeText/whenStringOptimization/nonInlinedConst.kt +++ b/compiler/testData/codegen/bytecodeText/whenStringOptimization/nonInlinedConst.kt @@ -9,4 +9,4 @@ fun foo(x : String) : String { return "other" } -// 1 LOOKUPSWITCH +// 0 LOOKUPSWITCH diff --git a/compiler/testData/codegen/bytecodeTextMultifile/inlineJavaStaticFields/test.kt b/compiler/testData/codegen/bytecodeTextMultifile/inlineJavaStaticFields/test.kt index 951fd4a6495..065d435c909 100644 --- a/compiler/testData/codegen/bytecodeTextMultifile/inlineJavaStaticFields/test.kt +++ b/compiler/testData/codegen/bytecodeTextMultifile/inlineJavaStaticFields/test.kt @@ -20,10 +20,10 @@ fun test() { JClass.PrimitiveFloat JClass.PrimitiveDouble JClass.Str + JClass.StrNullable JClass.BoxedInt JClass.NonFinal - JClass.StrNullable JClass().NonStatic @@ -44,10 +44,10 @@ fun test() { // 1 LDC 36.6 // 1 LDC 42.4242 // 1 LDC ":J" +// 1 LDC "nullable" // 1 GETSTATIC JClass.BoxedInt : Ljava/lang/Integer; // 1 GETSTATIC JClass.NonFinal : I // 1 GETFIELD JClass.NonStatic : I -// 1 LDC "nullable" // 1 GETSTATIC KoKobject.JvmStatic : I // 1 GETSTATIC KoKobject.JvmStaticString : Ljava/lang/String // 3 POP2 diff --git a/compiler/testData/codegen/bytecodeTextMultifile/preEvaluateInlineJavaStaticFields/JClass.java b/compiler/testData/codegen/bytecodeTextMultifile/preEvaluateInlineJavaStaticFields/JClass.java new file mode 100644 index 00000000000..9ab411639f6 --- /dev/null +++ b/compiler/testData/codegen/bytecodeTextMultifile/preEvaluateInlineJavaStaticFields/JClass.java @@ -0,0 +1,24 @@ +import org.jetbrains.annotations.NotNull; + +public class JClass { + public final static int PrimitiveInt = 9000; + public final static int BigPrimitiveInt = 59000; + public final static long PrimitiveLong = 100000; + public final static short PrimitiveShort = 901; + public final static boolean PrimitiveBool = false; + public final static float PrimitiveFloat = 36.6; + public final static double PrimitiveDouble = 42.4242; + public final static byte PrimitiveByte = -8; + public final static char PrimitiveChar = 'K'; + public final static String Str = ":J"; + + @Nullable + public final static String StrNullable = "nullable"; + + @NotNull + public final static Integer BoxedInt = 9500; + + public static int NonFinal = 9700; + + public final int NonStatic = 9800; +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeTextMultifile/preEvaluateInlineJavaStaticFields/test.kt b/compiler/testData/codegen/bytecodeTextMultifile/preEvaluateInlineJavaStaticFields/test.kt new file mode 100644 index 00000000000..9f1fb1a5f73 --- /dev/null +++ b/compiler/testData/codegen/bytecodeTextMultifile/preEvaluateInlineJavaStaticFields/test.kt @@ -0,0 +1,45 @@ +enum class EClass { + VALUE +} +object KoKobject { + @JvmField + val JvmStatic: Int = 1 + + @JvmField + val JvmStaticString: String? = "123" +} + +fun test() { + "res1: " + + Integer.MIN_VALUE + " " + + java.lang.Long.MAX_VALUE + " " + + JClass.PrimitiveInt + " " + + JClass.BigPrimitiveInt + " " + + JClass.PrimitiveByte + " " + + JClass.PrimitiveChar + " " + + JClass.PrimitiveLong + " " + + JClass.PrimitiveShort + " " + + JClass.PrimitiveBool + " " + + JClass.PrimitiveFloat + " " + + JClass.PrimitiveDouble + " " + + JClass.Str + " " + + JClass.StrNullable + + "res2: " + JClass.BoxedInt + "res3: " + JClass.NonFinal + "res4: " + JClass().NonStatic + "res5: " + KoKobject.JvmStatic + "res6: " + KoKobject.JvmStaticString + "res7: " + EClass.VALUE + "res8: " + EClass::class +} + +// @TestKt.class: +// 1 LDC "res1: -2147483648 9223372036854775807 9000 59000 -8 K 100000 901 false 36.6 42.4242 :J nullable" +// 1 LDC "res2: " +// 1 LDC "res3: " +// 1 LDC "res4: " +// 1 LDC "res5: " +// 1 LDC "res6: " +// 1 LDC "res7: " +// 1 LDC "res8: " diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextMultifileTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextMultifileTestGenerated.java index 3ba8e695ec9..16ce0383e50 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextMultifileTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextMultifileTestGenerated.java @@ -58,4 +58,10 @@ public class BytecodeTextMultifileTestGenerated extends AbstractBytecodeTextTest String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeTextMultifile/partMembersInline/"); doTestMultiFile(fileName); } + + @TestMetadata("preEvaluateInlineJavaStaticFields") + public void testPreEvaluateInlineJavaStaticFields() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeTextMultifile/preEvaluateInlineJavaStaticFields/"); + doTestMultiFile(fileName); + } } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 035ac303cf8..62dcf8832bc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -572,6 +572,27 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/constProperty") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConstProperty extends AbstractBytecodeTextTest { + public void testAllFilesPresentInConstProperty() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/constProperty"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("noInline.kt") + public void testNoInline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constProperty/noInline.kt"); + doTest(fileName); + } + + @TestMetadata("noInlineInCmp.kt") + public void testNoInlineInCmp() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constProperty/noInlineInCmp.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/constants") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -609,6 +630,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constants/short.kt"); doTest(fileName); } + + @TestMetadata("string.kt") + public void testString() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constants/string.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 8ef3c01e9d3..9dcb416a542 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -1090,6 +1090,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/const"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("constValInAnnotationDefault.kt") + public void testConstValInAnnotationDefault() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/const/constValInAnnotationDefault.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("interfaceCompanion.kt") public void testInterfaceCompanion() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/const/interfaceCompanion.kt");