Skip property accessors for constants

- Inline protected constants from Java at use sites
 - Do not create accessors for private constants in Kotlin

 #KT-11734 In Progress
This commit is contained in:
Mikhail Zarechenskiy
2016-12-05 12:02:43 +03:00
parent bd14c24592
commit 8c1e165f18
12 changed files with 151 additions and 14 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,6 +50,10 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl;
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
@@ -2603,9 +2607,16 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
boolean skipPropertyAccessors;
PropertyDescriptor originalPropertyDescriptor = DescriptorUtils.unwrapFakeOverride(propertyDescriptor);
LanguageVersionSettings languageVersionSettings = state.getConfiguration().get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS);
if (languageVersionSettings == null) {
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT;
}
if (fieldAccessorKind != FieldAccessorKind.NORMAL) {
int flags = AsmUtil.getVisibilityForBackingField(propertyDescriptor, isDelegatedProperty);
skipPropertyAccessors = (flags & ACC_PRIVATE) == 0 || skipAccessorsForPrivateFieldInOuterClass;
boolean isInlinedConst = propertyDescriptor.isConst() && languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals);
skipPropertyAccessors = isInlinedConst || (flags & ACC_PRIVATE) == 0 || skipAccessorsForPrivateFieldInOuterClass;
if (!skipPropertyAccessors) {
//noinspection ConstantConditions
propertyDescriptor = (PropertyDescriptor) backingFieldContext.getAccessor(
@@ -2624,7 +2635,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
if (!skipPropertyAccessors) {
if (!couldUseDirectAccessToProperty(propertyDescriptor, true, isDelegatedProperty, context)) {
if (!couldUseDirectAccessToProperty(propertyDescriptor, true, isDelegatedProperty, context, languageVersionSettings)) {
propertyDescriptor = context.getAccessorForSuperCallIfNeeded(propertyDescriptor, superCallTarget);
propertyDescriptor = context.accessibleDescriptor(propertyDescriptor, superCallTarget);
@@ -2638,7 +2649,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (propertyDescriptor.isVar()) {
PropertySetterDescriptor setter = propertyDescriptor.getSetter();
if (setter != null &&
!couldUseDirectAccessToProperty(propertyDescriptor, false, isDelegatedProperty, context) &&
!couldUseDirectAccessToProperty(propertyDescriptor, false, isDelegatedProperty, context, languageVersionSettings) &&
!isConstOrHasJvmFieldAnnotation(propertyDescriptor)) {
callableSetter = typeMapper.mapToCallableMethod(setter, isSuper);
}
@@ -37,6 +37,9 @@ import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.lexer.KtTokens;
@@ -646,7 +649,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
private Type genPropertyOnStack(InstructionAdapter iv, MethodContext context, @NotNull PropertyDescriptor propertyDescriptor, int index) {
iv.load(index, classAsmType);
if (couldUseDirectAccessToProperty(propertyDescriptor, /* forGetter = */ true, /* isDelegated = */ false, context)) {
LanguageVersionSettings settings = state.getConfiguration().get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS);
if (settings == null) {
settings = LanguageVersionSettingsImpl.DEFAULT;
}
if (couldUseDirectAccessToProperty(propertyDescriptor, /* forGetter = */ true,
/* isDelegated = */ false, context, settings)) {
Type type = typeMapper.mapType(propertyDescriptor.getType());
String fieldName = ((FieldOwnerContext) context.getParentContext()).getFieldName(propertyDescriptor, false);
iv.getfield(classAsmType.getInternalName(), fieldName, type.getDescriptor());
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,8 @@ import org.jetbrains.kotlin.codegen.context.MethodContext;
import org.jetbrains.kotlin.codegen.context.RootContext;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor;
@@ -182,8 +184,11 @@ public class JvmCodegenUtil {
@NotNull PropertyDescriptor property,
boolean forGetter,
boolean isDelegated,
@NotNull MethodContext contextBeforeInline
@NotNull MethodContext contextBeforeInline,
@NotNull LanguageVersionSettings languageVersionSettings
) {
if (languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals) && property.isConst()) return true;
if (KotlinTypeMapper.isAccessor(property)) return false;
CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext();
@@ -0,0 +1,17 @@
// LANGUAGE_VERSION: 1.0
// FILE: Foo.kt
private const val OUTER_PRIVATE = 20
class Foo {
companion object {
private const val LOCAL_PRIVATE = 20
}
fun foo() {
LOCAL_PRIVATE
OUTER_PRIVATE
}
}
// 2 INVOKESTATIC
@@ -0,0 +1,16 @@
// FILE: Foo.kt
private const val OUTER_PRIVATE = 20
class Foo {
companion object {
private const val LOCAL_PRIVATE = 20
}
fun foo() {
LOCAL_PRIVATE
OUTER_PRIVATE
}
}
// 0 INVOKESTATIC
@@ -0,0 +1,22 @@
// FILE: first/Foo.java
package first;
public class Foo {
protected static final int FOO = 42;
}
// FILE: bar.kt
package second
import first.Foo
class Bar : Foo() {
fun bar() = FOO
}
// @second/BarKt.class
// 0 INVOKESTATIC
// 0 GETSTATIC
// 1 BIPUSH 42
@@ -0,0 +1,23 @@
// LANGUAGE_VERSION: 1.0
// FILE: first/Foo.java
package first;
public class Foo {
protected static final int FOO = 42;
}
// FILE: bar.kt
package second
import first.Foo
class Bar : Foo() {
fun bar() = FOO
}
// @second/BarKt.class
// 1 INVOKESTATIC
// 0 GETSTATIC
// 1 BIPUSH 42
@@ -138,6 +138,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
@TestMetadata("inlineProtectedJavaConstantFromOtherPackage.kt")
public void testInlineProtectedJavaConstantFromOtherPackage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineProtectedJavaConstantFromOtherPackage.kt");
doTest(fileName);
}
@TestMetadata("intConstantNotNull.kt")
public void testIntConstantNotNull() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/intConstantNotNull.kt");
@@ -270,6 +276,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
@TestMetadata("noInlineJavaProtectedConstants.kt")
public void testNoInlineJavaProtectedConstants() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/noInlineJavaProtectedConstants.kt");
doTest(fileName);
}
@TestMetadata("noNumberCheckCast.kt")
public void testNoNumberCheckCast() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/noNumberCheckCast.kt");
@@ -712,10 +724,22 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ConstProperty extends AbstractBytecodeTextTest {
@TestMetadata("accessorsForPrivateConstants.kt")
public void testAccessorsForPrivateConstants() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constProperty/accessorsForPrivateConstants.kt");
doTest(fileName);
}
public void testAllFilesPresentInConstProperty() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/constProperty"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("noAccessorsForPrivateConstants.kt")
public void testNoAccessorsForPrivateConstants() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constProperty/noAccessorsForPrivateConstants.kt");
doTest(fileName);
}
@TestMetadata("noInline.kt")
public void testNoInline() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constProperty/noInline.kt");
@@ -34,6 +34,7 @@ enum class LanguageFeature(val sinceVersion: LanguageVersion?) {
DslMarkersSupport(KOTLIN_1_1),
UnderscoresInNumericLiterals(KOTLIN_1_1),
DivisionByZeroInConstantExpressions(KOTLIN_1_1),
InlineConstVals(KOTLIN_1_1),
// Experimental features
MultiPlatformProjects(null),
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,14 +41,12 @@ import org.jetbrains.kotlin.codegen.ClassBuilderFactories;
import org.jetbrains.kotlin.codegen.CompilationErrorHandler;
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.config.JVMConfigurationKeys;
import org.jetbrains.kotlin.config.JvmTarget;
import org.jetbrains.kotlin.config.*;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages;
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
import org.jetbrains.kotlin.idea.debugger.DebuggerUtils;
import org.jetbrains.kotlin.idea.project.PlatformKt;
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade;
import org.jetbrains.kotlin.idea.util.InfinitePeriodicalTask;
import org.jetbrains.kotlin.idea.util.LongRunningReadTask;
@@ -136,6 +134,9 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
configuration.put(JVMConfigurationKeys.IR, true);
}
LanguageVersionSettings languageVersionSettings = PlatformKt.getLanguageVersionSettings(ktFile);
configuration.put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, languageVersionSettings);
return getBytecodeForFile(ktFile, configuration);
}
@@ -25,8 +25,10 @@ import org.jetbrains.java.decompiler.main.decompiler.BaseDecompiler
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences
import org.jetbrains.java.decompiler.main.extern.IResultSaver
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.idea.actions.canBeDecompiledToJava
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
import java.util.jar.Manifest
@@ -72,7 +74,10 @@ class KotlinDecompilerServiceImpl : KotlinDecompilerService {
}
fun bytecodeMapForSourceFile(file: KtFile): Map<File, () -> ByteArray> {
val generationState = KotlinBytecodeToolWindow.compileSingleFile(file, CompilerConfiguration.EMPTY)
val configuration = CompilerConfiguration().apply {
put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, file.languageVersionSettings)
}
val generationState = KotlinBytecodeToolWindow.compileSingleFile(file, configuration)
val bytecodeMap = hashMapOf<File, () -> ByteArray>()
generationState.factory.asList().filter { FileUtilRt.extensionEquals(it.relativePath, "class") }.forEach {
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.internal
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.psi.KtFile
@@ -44,6 +45,9 @@ abstract class AbstractBytecodeToolWindowTest: KotlinLightCodeInsightFixtureTest
if (InTextDirectivesUtils.getPrefixedBoolean(mainFileText, "// INLINE:") == false) {
configuration.put(CommonConfigurationKeys.DISABLE_INLINE, true)
}
configuration.put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, file.languageVersionSettings)
val bytecodes = KotlinBytecodeToolWindow.getBytecodeForFile(file, configuration)
assert(bytecodes.contains("// ================")) {
"The header \"// ================\" is missing.\n This means that there is an exception failed during compilation:\n$bytecodes"