Emit not-null assertions for enhanced types
This commit is contained in:
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.load.java.JavaVisibilities;
|
|||||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
|
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
|
||||||
|
import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo;
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.FqName;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.kotlin.resolve.annotations.AnnotationsPackage;
|
import org.jetbrains.kotlin.resolve.annotations.AnnotationsPackage;
|
||||||
@@ -45,7 +46,6 @@ import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
|
|||||||
import org.jetbrains.kotlin.resolve.jvm.JvmPackage;
|
import org.jetbrains.kotlin.resolve.jvm.JvmPackage;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
|
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||||
import org.jetbrains.kotlin.types.Approximation;
|
|
||||||
import org.jetbrains.kotlin.types.JetType;
|
import org.jetbrains.kotlin.types.JetType;
|
||||||
import org.jetbrains.kotlin.types.TypesPackage;
|
import org.jetbrains.kotlin.types.TypesPackage;
|
||||||
import org.jetbrains.org.objectweb.asm.*;
|
import org.jetbrains.org.objectweb.asm.*;
|
||||||
@@ -637,10 +637,10 @@ public class AsmUtil {
|
|||||||
public static StackValue genNotNullAssertions(
|
public static StackValue genNotNullAssertions(
|
||||||
@NotNull GenerationState state,
|
@NotNull GenerationState state,
|
||||||
@NotNull final StackValue stackValue,
|
@NotNull final StackValue stackValue,
|
||||||
@Nullable final Approximation.Info approximationInfo
|
@Nullable final RuntimeAssertionInfo runtimeAssertionInfo
|
||||||
) {
|
) {
|
||||||
if (!state.isCallAssertionsEnabled()) return stackValue;
|
if (!state.isCallAssertionsEnabled()) return stackValue;
|
||||||
if (approximationInfo == null || !TypesPackage.assertNotNull(approximationInfo)) return stackValue;
|
if (runtimeAssertionInfo == null || !runtimeAssertionInfo.getNeedNotNullAssertion()) return stackValue;
|
||||||
|
|
||||||
return new StackValue(stackValue.type) {
|
return new StackValue(stackValue.type) {
|
||||||
|
|
||||||
@@ -649,7 +649,7 @@ public class AsmUtil {
|
|||||||
stackValue.put(type, v);
|
stackValue.put(type, v);
|
||||||
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
|
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
|
||||||
v.dup();
|
v.dup();
|
||||||
v.visitLdcInsn(approximationInfo.getMessage());
|
v.visitLdcInsn(runtimeAssertionInfo.getMessage());
|
||||||
v.invokestatic("kotlin/jvm/internal/Intrinsics", "checkExpressionValueIsNotNull",
|
v.invokestatic("kotlin/jvm/internal/Intrinsics", "checkExpressionValueIsNotNull",
|
||||||
"(Ljava/lang/Object;Ljava/lang/String;)V", false);
|
"(Ljava/lang/Object;Ljava/lang/String;)V", false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,10 +49,12 @@ import org.jetbrains.kotlin.descriptors.*;
|
|||||||
import org.jetbrains.kotlin.descriptors.impl.ScriptCodeDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.ScriptCodeDescriptor;
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
|
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||||
|
import org.jetbrains.kotlin.jvm.bindingContextSlices.BindingContextSlicesPackage;
|
||||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
|
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor;
|
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor;
|
||||||
|
import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||||
@@ -75,7 +77,6 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
|
|||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.*;
|
||||||
import org.jetbrains.kotlin.types.Approximation;
|
|
||||||
import org.jetbrains.kotlin.types.JetType;
|
import org.jetbrains.kotlin.types.JetType;
|
||||||
import org.jetbrains.kotlin.types.TypeProjection;
|
import org.jetbrains.kotlin.types.TypeProjection;
|
||||||
import org.jetbrains.kotlin.types.TypeUtils;
|
import org.jetbrains.kotlin.types.TypeUtils;
|
||||||
@@ -280,12 +281,12 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
|
|
||||||
StackValue stackValue = selector.accept(visitor, receiver);
|
StackValue stackValue = selector.accept(visitor, receiver);
|
||||||
|
|
||||||
Approximation.Info approximationInfo = null;
|
RuntimeAssertionInfo runtimeAssertionInfo = null;
|
||||||
if (selector instanceof JetExpression) {
|
if (selector instanceof JetExpression) {
|
||||||
approximationInfo = bindingContext.get(BindingContext.EXPRESSION_RESULT_APPROXIMATION, (JetExpression) selector);
|
runtimeAssertionInfo = bindingContext.get(BindingContextSlicesPackage.getRUNTIME_ASSERTION_INFO(), (JetExpression) selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
return genNotNullAssertions(state, stackValue, approximationInfo);
|
return genNotNullAssertions(state, stackValue, runtimeAssertionInfo);
|
||||||
}
|
}
|
||||||
catch (ProcessCanceledException e) {
|
catch (ProcessCanceledException e) {
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
|
|||||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||||
|
import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo;
|
||||||
import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeDeclarationsPackage;
|
import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeDeclarationsPackage;
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.FqName;
|
||||||
import org.jetbrains.kotlin.psi.JetNamedFunction;
|
import org.jetbrains.kotlin.psi.JetNamedFunction;
|
||||||
@@ -51,8 +52,6 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
|||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||||
import org.jetbrains.kotlin.types.Approximation;
|
|
||||||
import org.jetbrains.kotlin.types.TypesPackage;
|
|
||||||
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
|
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
|
||||||
import org.jetbrains.org.objectweb.asm.Label;
|
import org.jetbrains.org.objectweb.asm.Label;
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||||
@@ -834,10 +833,10 @@ public class FunctionCodegen {
|
|||||||
StackValue stackValue = AsmUtil.genNotNullAssertions(
|
StackValue stackValue = AsmUtil.genNotNullAssertions(
|
||||||
state,
|
state,
|
||||||
StackValue.onStack(delegateToMethod.getReturnType()),
|
StackValue.onStack(delegateToMethod.getReturnType()),
|
||||||
TypesPackage.getApproximationTo(
|
RuntimeAssertionInfo.create(
|
||||||
delegatedTo.getReturnType(),
|
|
||||||
delegateFunction.getReturnType(),
|
delegateFunction.getReturnType(),
|
||||||
new Approximation.DataFlowExtras.OnlyMessage(delegatedTo.getName() + "(...)")
|
delegatedTo.getReturnType(),
|
||||||
|
new RuntimeAssertionInfo.DataFlowExtras.OnlyMessage(delegatedTo.getName() + "(...)")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.jvm
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||||
|
import org.jetbrains.kotlin.jvm.bindingContextSlices.RUNTIME_ASSERTION_INFO
|
||||||
|
import org.jetbrains.kotlin.psi.JetExpression
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
|
import org.jetbrains.kotlin.types.JetType
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||||
|
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||||
|
import kotlin.platform.platformStatic
|
||||||
|
|
||||||
|
public class RuntimeAssertionInfo(public val needNotNullAssertion: Boolean, public val message: String) {
|
||||||
|
public interface DataFlowExtras {
|
||||||
|
class OnlyMessage(message: String) : DataFlowExtras {
|
||||||
|
override val canBeNull: Boolean get() = true
|
||||||
|
override val possibleTypes: Set<JetType> get() = setOf()
|
||||||
|
override val presentableText: String = message
|
||||||
|
}
|
||||||
|
|
||||||
|
val canBeNull: Boolean
|
||||||
|
val possibleTypes: Set<JetType>
|
||||||
|
val presentableText: String
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
platformStatic public fun create(
|
||||||
|
expectedType: JetType,
|
||||||
|
expressionType: JetType,
|
||||||
|
dataFlowExtras: DataFlowExtras
|
||||||
|
): RuntimeAssertionInfo? {
|
||||||
|
fun assertNotNull(): Boolean {
|
||||||
|
if (expectedType.isError() || expressionType.isError()) return false
|
||||||
|
|
||||||
|
// T : Any, T! = T..T?
|
||||||
|
// Let T$ will be copy of T! with enhanced nullability.
|
||||||
|
// Cases when nullability assertion needed: T! -> T, T$ -> T
|
||||||
|
|
||||||
|
// Expected type either T?, T! or T$
|
||||||
|
if (TypeUtils.isNullableType(expectedType) || expectedType.hasEnhancedNullability()) return false
|
||||||
|
|
||||||
|
// Expression type is not nullable and not enhanced (neither T?, T! or T$)
|
||||||
|
val isExpressionTypeNullable = TypeUtils.isNullableType(expressionType)
|
||||||
|
if (!isExpressionTypeNullable && !expressionType.hasEnhancedNullability()) return false
|
||||||
|
|
||||||
|
// Smart-cast T! or T?
|
||||||
|
if (!dataFlowExtras.canBeNull && isExpressionTypeNullable) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (assertNotNull())
|
||||||
|
RuntimeAssertionInfo(needNotNullAssertion = true, message = dataFlowExtras.presentableText)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JetType.hasEnhancedNullability()
|
||||||
|
= getAnnotations().findAnnotation(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION) != null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object RuntimeAssertionsTypeChecker : AdditionalTypeChecker {
|
||||||
|
override fun checkType(expression: JetExpression, expressionType: JetType, c: ResolutionContext<*>) {
|
||||||
|
if (TypeUtils.noExpectedType(c.expectedType)) return
|
||||||
|
|
||||||
|
val assertionInfo = RuntimeAssertionInfo.create(
|
||||||
|
c.expectedType,
|
||||||
|
expressionType,
|
||||||
|
object : RuntimeAssertionInfo.DataFlowExtras {
|
||||||
|
override val canBeNull: Boolean
|
||||||
|
get() = c.dataFlowInfo.getNullability(dataFlowValue).canBeNull()
|
||||||
|
override val possibleTypes: Set<JetType>
|
||||||
|
get() = c.dataFlowInfo.getPossibleTypes(dataFlowValue)
|
||||||
|
override val presentableText: String
|
||||||
|
get() = StringUtil.trimMiddle(expression.getText(), 50)
|
||||||
|
|
||||||
|
private val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (assertionInfo != null) {
|
||||||
|
c.trace.record(RUNTIME_ASSERTION_INFO, expression, assertionInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkReceiver(
|
||||||
|
receiverParameter: ReceiverParameterDescriptor,
|
||||||
|
receiverArgument: ReceiverValue,
|
||||||
|
safeAccess: Boolean,
|
||||||
|
c: CallResolutionContext<*>
|
||||||
|
) { }
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.jvm.bindingContextSlices
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo
|
||||||
|
import org.jetbrains.kotlin.psi.JetExpression
|
||||||
|
import org.jetbrains.kotlin.util.slicedMap.BasicWritableSlice
|
||||||
|
import org.jetbrains.kotlin.util.slicedMap.RewritePolicy
|
||||||
|
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||||
|
import org.jetbrains.kotlin.utils.DO_NOTHING
|
||||||
|
|
||||||
|
public val RUNTIME_ASSERTION_INFO: WritableSlice<JetExpression, RuntimeAssertionInfo> = BasicWritableSlice(RewritePolicy.DO_NOTHING)
|
||||||
+2
-1
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.cfg.WhenChecker
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
|
import org.jetbrains.kotlin.jvm.RuntimeAssertionsTypeChecker
|
||||||
import org.jetbrains.kotlin.lexer.JetTokens
|
import org.jetbrains.kotlin.lexer.JetTokens
|
||||||
import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNotNull
|
import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNotNull
|
||||||
import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNullable
|
import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNullable
|
||||||
@@ -63,7 +64,7 @@ public class KotlinJvmCheckerProvider(private val module: ModuleDescriptor) : Ad
|
|||||||
JavaAnnotationMethodCallChecker(), TraitDefaultMethodCallChecker(),
|
JavaAnnotationMethodCallChecker(), TraitDefaultMethodCallChecker(),
|
||||||
ReflectionAPICallChecker(module)),
|
ReflectionAPICallChecker(module)),
|
||||||
|
|
||||||
additionalTypeCheckers = listOf(JavaNullabilityWarningsChecker()),
|
additionalTypeCheckers = listOf(JavaNullabilityWarningsChecker(), RuntimeAssertionsTypeChecker),
|
||||||
additionalSymbolUsageValidators = listOf()
|
additionalSymbolUsageValidators = listOf()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ fun checkAssertions(illegalStateExpected: Boolean) {
|
|||||||
check("plus") { A() + A() }
|
check("plus") { A() + A() }
|
||||||
|
|
||||||
// field
|
// field
|
||||||
// check("NULL") { A().NULL }
|
check("NULL") { A().NULL }
|
||||||
|
|
||||||
// static field
|
// static field
|
||||||
// check("STATIC_NULL") { A.STATIC_NULL }
|
check("STATIC_NULL") { A.STATIC_NULL }
|
||||||
|
|
||||||
// postfix expression
|
// postfix expression
|
||||||
// TODO:
|
// TODO:
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ public class GenerateNotNullAssertionsTest extends CodegenTestCase {
|
|||||||
loadFile("notNullAssertions/arrayListGet.kt");
|
loadFile("notNullAssertions/arrayListGet.kt");
|
||||||
String text = generateToText();
|
String text = generateToText();
|
||||||
|
|
||||||
|
assertTrue(text.contains("checkExpressionValueIsNotNull"));
|
||||||
assertTrue(text.contains("checkParameterIsNotNull"));
|
assertTrue(text.contains("checkParameterIsNotNull"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +137,7 @@ public class GenerateNotNullAssertionsTest extends CodegenTestCase {
|
|||||||
loadFile("notNullAssertions/javaMultipleSubstitutions.kt");
|
loadFile("notNullAssertions/javaMultipleSubstitutions.kt");
|
||||||
String text = generateToText();
|
String text = generateToText();
|
||||||
|
|
||||||
assertEquals(0, StringUtil.getOccurrenceCount(text, "checkExpressionValueIsNotNull"));
|
assertEquals(3, StringUtil.getOccurrenceCount(text, "checkExpressionValueIsNotNull"));
|
||||||
assertEquals(3, StringUtil.getOccurrenceCount(text, "checkParameterIsNotNull"));
|
assertEquals(3, StringUtil.getOccurrenceCount(text, "checkParameterIsNotNull"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ public final class JvmAnnotationNames {
|
|||||||
|
|
||||||
public static final FqName PURELY_IMPLEMENTS_ANNOTATION = new FqName("kotlin.jvm.PurelyImplements");
|
public static final FqName PURELY_IMPLEMENTS_ANNOTATION = new FqName("kotlin.jvm.PurelyImplements");
|
||||||
|
|
||||||
|
// Just for internal use: there is no such real classes in bytecode
|
||||||
|
public static final FqName ENHANCED_NULLABILITY_ANNOTATION = new FqName("kotlin.jvm.internal.EnhancedNullability");
|
||||||
|
public static final FqName ENHANCED_MUTABILITY_ANNOTATION = new FqName("kotlin.jvm.internal.EnhancedMutability");
|
||||||
|
|
||||||
public static class KotlinClass {
|
public static class KotlinClass {
|
||||||
public static final JvmClassName CLASS_NAME = JvmClassName.byInternalName("kotlin/jvm/internal/KotlinClass");
|
public static final JvmClassName CLASS_NAME = JvmClassName.byInternalName("kotlin/jvm/internal/KotlinClass");
|
||||||
public static final ClassId KIND_CLASS_ID =
|
public static final ClassId KIND_CLASS_ID =
|
||||||
|
|||||||
+64
-14
@@ -18,12 +18,19 @@ package org.jetbrains.kotlin.load.java.typeEnhacement
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations
|
||||||
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||||
import org.jetbrains.kotlin.load.java.typeEnhacement.JavaTypeQualifiers
|
import org.jetbrains.kotlin.load.java.typeEnhacement.JavaTypeQualifiers
|
||||||
import org.jetbrains.kotlin.load.java.typeEnhacement.MutabilityQualifier.MUTABLE
|
import org.jetbrains.kotlin.load.java.typeEnhacement.MutabilityQualifier.MUTABLE
|
||||||
import org.jetbrains.kotlin.load.java.typeEnhacement.MutabilityQualifier.READ_ONLY
|
import org.jetbrains.kotlin.load.java.typeEnhacement.MutabilityQualifier.READ_ONLY
|
||||||
import org.jetbrains.kotlin.load.java.typeEnhacement.NullabilityQualifier.NOT_NULL
|
import org.jetbrains.kotlin.load.java.typeEnhacement.NullabilityQualifier.NOT_NULL
|
||||||
import org.jetbrains.kotlin.load.java.typeEnhacement.NullabilityQualifier.NULLABLE
|
import org.jetbrains.kotlin.load.java.typeEnhacement.NullabilityQualifier.NULLABLE
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
|
|
||||||
// The index in the lambda is the position of the type component:
|
// The index in the lambda is the position of the type component:
|
||||||
@@ -68,7 +75,7 @@ private fun JetType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers, i
|
|||||||
?: return Result(this, 1)
|
?: return Result(this, 1)
|
||||||
|
|
||||||
val effectiveQualifiers = qualifiers(index)
|
val effectiveQualifiers = qualifiers(index)
|
||||||
val enhancedClassifier = originalClass.enhanceMutability(effectiveQualifiers, position)
|
val (enhancedClassifier, enhancedMutabilityAnnotations) = originalClass.enhanceMutability(effectiveQualifiers, position)
|
||||||
|
|
||||||
var globalArgIndex = index + 1
|
var globalArgIndex = index + 1
|
||||||
val enhancedArguments = getArguments().mapIndexed {
|
val enhancedArguments = getArguments().mapIndexed {
|
||||||
@@ -87,10 +94,17 @@ private fun JetType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers, i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val enhancedType = JetTypeImpl(
|
val (enhancedNullability, enhancedNullabilityAnnotations) = this.getEnhancedNullability(effectiveQualifiers, position)
|
||||||
|
val newAnnotations = listOf(
|
||||||
getAnnotations(),
|
getAnnotations(),
|
||||||
|
enhancedMutabilityAnnotations,
|
||||||
|
enhancedNullabilityAnnotations
|
||||||
|
).filterNotNull().compositeAnnotationsOrSingle()
|
||||||
|
|
||||||
|
val enhancedType = JetTypeImpl(
|
||||||
|
newAnnotations,
|
||||||
enhancedClassifier.getTypeConstructor(),
|
enhancedClassifier.getTypeConstructor(),
|
||||||
this.getEnhancedNullability(effectiveQualifiers, position),
|
enhancedNullability,
|
||||||
enhancedArguments,
|
enhancedArguments,
|
||||||
if (enhancedClassifier is ClassDescriptor)
|
if (enhancedClassifier is ClassDescriptor)
|
||||||
enhancedClassifier.getMemberScope(enhancedArguments)
|
enhancedClassifier.getMemberScope(enhancedArguments)
|
||||||
@@ -99,36 +113,72 @@ private fun JetType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers, i
|
|||||||
return Result(enhancedType, globalArgIndex - index)
|
return Result(enhancedType, globalArgIndex - index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun List<Annotations>.compositeAnnotationsOrSingle() = when (size()) {
|
||||||
|
0 -> error("At least one Annotations object expected")
|
||||||
|
1 -> single()
|
||||||
|
else -> CompositeAnnotations(this)
|
||||||
|
}
|
||||||
|
|
||||||
private fun TypeComponentPosition.shouldEnhance() = this != TypeComponentPosition.INFLEXIBLE
|
private fun TypeComponentPosition.shouldEnhance() = this != TypeComponentPosition.INFLEXIBLE
|
||||||
|
|
||||||
private fun ClassifierDescriptor.enhanceMutability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): ClassifierDescriptor {
|
private data class EnhancementResult<T>(val result: T, val enhancementAnnotations: Annotations?)
|
||||||
if (!position.shouldEnhance()) return this
|
private fun <T> T.noChange() = EnhancementResult(this, null)
|
||||||
if (this !is ClassDescriptor) return this // mutability is not applicable for type parameters
|
private fun <T> T.enhancedNullability() = EnhancementResult(this, ENHANCED_NULLABILITY_ANNOTATIONS)
|
||||||
|
private fun <T> T.enhancedMutability() = EnhancementResult(this, ENHANCED_MUTABILITY_ANNOTATIONS)
|
||||||
|
|
||||||
|
private fun ClassifierDescriptor.enhanceMutability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): EnhancementResult<ClassifierDescriptor> {
|
||||||
|
if (!position.shouldEnhance()) return this.noChange()
|
||||||
|
if (this !is ClassDescriptor) return this.noChange() // mutability is not applicable for type parameters
|
||||||
|
|
||||||
val mapping = JavaToKotlinClassMap.INSTANCE
|
val mapping = JavaToKotlinClassMap.INSTANCE
|
||||||
|
|
||||||
when (qualifiers.mutability) {
|
when (qualifiers.mutability) {
|
||||||
READ_ONLY -> {
|
READ_ONLY -> {
|
||||||
if (position == TypeComponentPosition.FLEXIBLE_LOWER && mapping.isMutable(this)) {
|
if (position == TypeComponentPosition.FLEXIBLE_LOWER && mapping.isMutable(this)) {
|
||||||
return mapping.convertMutableToReadOnly(this)
|
return mapping.convertMutableToReadOnly(this).enhancedMutability()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MUTABLE -> {
|
MUTABLE -> {
|
||||||
if (position == TypeComponentPosition.FLEXIBLE_UPPER && mapping.isReadOnly(this) ) {
|
if (position == TypeComponentPosition.FLEXIBLE_UPPER && mapping.isReadOnly(this) ) {
|
||||||
return mapping.convertReadOnlyToMutable(this)
|
return mapping.convertReadOnlyToMutable(this).enhancedMutability()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this
|
return this.noChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun JetType.getEnhancedNullability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): Boolean {
|
private fun JetType.getEnhancedNullability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): EnhancementResult<Boolean> {
|
||||||
if (!position.shouldEnhance()) return this.isMarkedNullable()
|
if (!position.shouldEnhance()) return this.isMarkedNullable().noChange()
|
||||||
|
|
||||||
return when (qualifiers.nullability) {
|
return when (qualifiers.nullability) {
|
||||||
NULLABLE -> true
|
NULLABLE -> true.enhancedNullability()
|
||||||
NOT_NULL -> false
|
NOT_NULL -> false.enhancedNullability()
|
||||||
else -> this.isMarkedNullable()
|
else -> this.isMarkedNullable().noChange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val ENHANCED_NULLABILITY_ANNOTATIONS = EnhancedTypeAnnotations(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION)
|
||||||
|
private val ENHANCED_MUTABILITY_ANNOTATIONS = EnhancedTypeAnnotations(JvmAnnotationNames.ENHANCED_MUTABILITY_ANNOTATION)
|
||||||
|
|
||||||
|
private class EnhancedTypeAnnotations(private val fqNameToMatch: FqName) : Annotations {
|
||||||
|
override fun isEmpty() = false
|
||||||
|
|
||||||
|
override fun findAnnotation(fqName: FqName) = when (fqName) {
|
||||||
|
fqNameToMatch -> EnhancedTypeAnnotationDescriptor
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findExternalAnnotation(fqName: FqName) = null
|
||||||
|
|
||||||
|
// Note, that this class may break Annotations contract (!isEmpty && iterator.isEmpty())
|
||||||
|
// It's a hack that we need unless we have stable "user data" in JetType
|
||||||
|
override fun iterator(): Iterator<AnnotationDescriptor> = emptyList<AnnotationDescriptor>().iterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
private object EnhancedTypeAnnotationDescriptor : AnnotationDescriptor {
|
||||||
|
private fun throwError() = error("No methods should be called on this descriptor. Only it's presence matters")
|
||||||
|
override fun getType() = throwError()
|
||||||
|
override fun getAllValueArguments() = throwError()
|
||||||
|
override fun toString() = "[EnhancedType]"
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke
|
|||||||
stepIntoSpecificKotlinClasses.kt:8
|
stepIntoSpecificKotlinClasses.kt:8
|
||||||
MyJavaClass.java:12
|
MyJavaClass.java:12
|
||||||
stepIntoSpecificKotlinClasses.kt:8
|
stepIntoSpecificKotlinClasses.kt:8
|
||||||
|
Intrinsics.!EXT!
|
||||||
|
stepIntoSpecificKotlinClasses.kt:8
|
||||||
stepIntoSpecificKotlinClasses.kt:9
|
stepIntoSpecificKotlinClasses.kt:9
|
||||||
stepIntoSpecificKotlinClasses.kt:10
|
|
||||||
Thread.!EXT!
|
|
||||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
Process finished with exit code 0
|
Process finished with exit code 0
|
||||||
|
|||||||
Reference in New Issue
Block a user