From a737352b5dc2b932272412e21f22c35b816385df Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Sun, 5 Oct 2014 22:19:30 +0400 Subject: [PATCH] Assertions when approximating platform types in delegation by expression --- .../jet/backend/common/CodegenUtil.java | 20 ------ .../jet/backend/common/CodegenUtilKt.kt | 64 +++++++++++++++++++ .../jet/codegen/FunctionCodegen.java | 37 +++++++---- .../codegen/ImplementationBodyCodegen.java | 21 +++--- .../jet/codegen/PropertyCodegen.java | 10 ++- .../notNullAssertions/Delegation.java | 7 ++ .../notNullAssertions/Delegation.kt | 21 ++++++ ...ackBoxAgainstJavaCodegenTestGenerated.java | 18 +++++- .../declaration/DelegationTranslator.kt | 3 +- 9 files changed, 151 insertions(+), 50 deletions(-) create mode 100644 compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtilKt.kt create mode 100644 compiler/testData/codegen/boxAgainstJava/notNullAssertions/Delegation.java create mode 100644 compiler/testData/codegen/boxAgainstJava/notNullAssertions/Delegation.kt diff --git a/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtil.java b/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtil.java index f3c0fa0c7dc..52390f8509e 100644 --- a/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtil.java +++ b/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtil.java @@ -126,26 +126,6 @@ public class CodegenUtil { Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)); } - public static Map getDelegates(ClassDescriptor descriptor, ClassDescriptor toClass) { - Map result = new LinkedHashMap(); - for (DeclarationDescriptor declaration : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) { - if (declaration instanceof CallableMemberDescriptor) { - CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) declaration; - if (callableMemberDescriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION) { - Set overriddenDescriptors = callableMemberDescriptor.getOverriddenDescriptors(); - for (CallableMemberDescriptor overriddenDescriptor : overriddenDescriptors) { - if (overriddenDescriptor.getContainingDeclaration() == toClass) { - assert !result.containsKey(callableMemberDescriptor) : - "overridden is already set for " + callableMemberDescriptor; - result.put(callableMemberDescriptor, overriddenDescriptor); - } - } - } - } - } - return result; - } - @NotNull public static Map getTraitMethods(ClassDescriptor descriptor) { Map result = new LinkedHashMap(); diff --git a/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtilKt.kt b/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtilKt.kt new file mode 100644 index 00000000000..b4ede0887b5 --- /dev/null +++ b/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtilKt.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2014 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.jet.backend.common + +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor +import org.jetbrains.jet.lang.descriptors.ClassDescriptor +import org.jetbrains.jet.lang.types.JetType +import kotlin.platform.platformStatic +import org.jetbrains.jet.lang.descriptors.CallableDescriptor +import org.jetbrains.jet.lang.resolve.OverrideResolver +import org.jetbrains.jet.utils.keysToMapExceptNulls + +public object CodegenUtilKt { + + // class Foo : Bar by baz + // descriptor = Foo + // toTrait = Bar + // delegateExpressionType = typeof(baz) + // return Map + public [platformStatic] fun getDelegates( + descriptor: ClassDescriptor, + toTrait: ClassDescriptor, + delegateExpressionType: JetType? = null + ): Map { + + return descriptor.getDefaultType().getMemberScope().getAllDescriptors().stream() + .filterIsInstance(javaClass()) + .filter { it.getKind() == CallableMemberDescriptor.Kind.DELEGATION } + .keysToMapExceptNulls { + delegatingMember -> + + val actualDelegates = OverrideResolver.getAllOverriddenDescriptors(delegatingMember) + .filter { it.getContainingDeclaration() == toTrait } + .map { + overriddenDescriptor -> + val scope = (delegateExpressionType ?: toTrait.getDefaultType()).getMemberScope() + val name = overriddenDescriptor.getName() + + // this is the actual member of delegateExpressionType that we are delegating to + (scope.getFunctions(name) + scope.getProperties(name)) + .first { + (listOf(it) + OverrideResolver.getAllOverriddenDescriptors(it)).map {it.getOriginal()}.contains(overriddenDescriptor.getOriginal()) + } + } + assert(actualDelegates.size <= 1) { "Meny delegates found for $delegatingMember: $actualDelegates" } + + actualDelegates.firstOrNull() + } + } +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index d91af314b59..fedc5d5f5f2 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -25,7 +25,6 @@ import com.intellij.util.containers.ContainerUtil; import kotlin.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.codegen.binding.CalculatedClosure; import org.jetbrains.jet.codegen.binding.CodegenBinding; import org.jetbrains.jet.codegen.bridges.Bridge; import org.jetbrains.jet.codegen.bridges.BridgesPackage; @@ -51,6 +50,8 @@ import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterKind; import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterSignature; import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature; import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.types.Approximation; +import org.jetbrains.jet.lang.types.TypesPackage; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.org.objectweb.asm.AnnotationVisitor; import org.jetbrains.org.objectweb.asm.Label; @@ -799,21 +800,20 @@ public class FunctionCodegen extends ParentCodegenAware { } public void genDelegate(FunctionDescriptor functionDescriptor, FunctionDescriptor overriddenDescriptor, StackValue field) { - genDelegate(functionDescriptor, (ClassDescriptor) overriddenDescriptor.getContainingDeclaration(), field, - typeMapper.mapSignature(functionDescriptor), - typeMapper.mapSignature(overriddenDescriptor.getOriginal()) + genDelegate(functionDescriptor, overriddenDescriptor.getOriginal(), (ClassDescriptor) overriddenDescriptor.getContainingDeclaration(), field ); } public void genDelegate( - FunctionDescriptor functionDescriptor, + final FunctionDescriptor delegateFunction, + final FunctionDescriptor delegatedTo, final ClassDescriptor toClass, - final StackValue field, - final JvmMethodSignature jvmDelegateMethodSignature, - final JvmMethodSignature jvmDelegatingMethodSignature + final StackValue field ) { + final JvmMethodSignature jvmDelegateMethodSignature = typeMapper.mapSignature(delegateFunction); + final JvmMethodSignature jvmDelegateToMethodSignature = typeMapper.mapSignature(delegatedTo); generateMethod( - OtherOrigin(functionDescriptor), jvmDelegateMethodSignature, functionDescriptor, + OtherOrigin(delegateFunction), jvmDelegateMethodSignature, delegateFunction, new FunctionGenerationStrategy() { @Override public void generateBody( @@ -823,11 +823,11 @@ public class FunctionCodegen extends ParentCodegenAware { @NotNull MethodContext context, @NotNull MemberCodegen parentCodegen ) { - Method overriddenMethod = jvmDelegatingMethodSignature.getAsmMethod(); + Method delegateToMethod = jvmDelegateToMethodSignature.getAsmMethod(); Method delegateMethod = jvmDelegateMethodSignature.getAsmMethod(); Type[] argTypes = delegateMethod.getArgumentTypes(); - Type[] originalArgTypes = overriddenMethod.getArgumentTypes(); + Type[] originalArgTypes = delegateToMethod.getArgumentTypes(); InstructionAdapter iv = new InstructionAdapter(mv); iv.load(0, OBJECT_TYPE); @@ -840,13 +840,22 @@ public class FunctionCodegen extends ParentCodegenAware { String internalName = typeMapper.mapType(toClass).getInternalName(); if (toClass.getKind() == ClassKind.TRAIT) { - iv.invokeinterface(internalName, overriddenMethod.getName(), overriddenMethod.getDescriptor()); + iv.invokeinterface(internalName, delegateToMethod.getName(), delegateToMethod.getDescriptor()); } else { - iv.invokevirtual(internalName, overriddenMethod.getName(), overriddenMethod.getDescriptor()); + iv.invokevirtual(internalName, delegateToMethod.getName(), delegateToMethod.getDescriptor()); } - StackValue.onStack(overriddenMethod.getReturnType()).put(delegateMethod.getReturnType(), iv); + StackValue stackValue = AsmUtil.genNotNullAssertions( + state, + StackValue.onStack(delegateToMethod.getReturnType()), + TypesPackage.getApproximationTo( + delegatedTo.getReturnType(), + delegateFunction.getReturnType(), + new Approximation.DataFlowExtras.OnlyMessage(delegatedTo.getName() + "(...)")) + ); + + stackValue.put(delegateMethod.getReturnType(), iv); iv.areturn(delegateMethod.getReturnType()); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index 6dcdf52fd03..7950ec3560a 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -24,6 +24,7 @@ import kotlin.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.backend.common.CodegenUtil; +import org.jetbrains.jet.backend.common.CodegenUtilKt; import org.jetbrains.jet.backend.common.DataClassMethodGenerator; import org.jetbrains.jet.codegen.binding.MutableClosure; import org.jetbrains.jet.codegen.context.*; @@ -1286,13 +1287,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { JetExpression expression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression(); PropertyDescriptor propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, descriptor, bindingContext); - ClassDescriptor superClassDescriptor = getSuperClass(specifier); if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext)) { result.addField((JetDelegatorByExpressionSpecifier) specifier, propertyDescriptor); } else { - result.addField((JetDelegatorByExpressionSpecifier) specifier, typeMapper.mapType(superClassDescriptor), "$delegate_" + n); + JetType expressionType = state.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression); + Type asmType = + expressionType != null ? typeMapper.mapType(expressionType) : typeMapper.mapType(getSuperClass(specifier)); + result.addField((JetDelegatorByExpressionSpecifier) specifier, asmType, "$delegate_" + n); } n++; } @@ -1648,7 +1651,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { if (specifier instanceof JetDelegatorByExpressionSpecifier) { DelegationFieldsInfo.Field field = delegationFieldsInfo.getInfo((JetDelegatorByExpressionSpecifier) specifier); generateDelegateField(field); - generateDelegates(getSuperClass(specifier), field); + JetExpression delegateExpression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression(); + JetType delegateExpressionType = state.getBindingContext().get(BindingContext.EXPRESSION_TYPE, delegateExpression); + generateDelegates(getSuperClass(specifier), delegateExpressionType, field); } } } @@ -1660,17 +1665,17 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { fieldInfo.name, fieldInfo.type.getDescriptor(), /*TODO*/null, null); } - protected void generateDelegates(ClassDescriptor toClass, DelegationFieldsInfo.Field field) { - for (Map.Entry entry : CodegenUtil.getDelegates(descriptor, toClass).entrySet()) { + protected void generateDelegates(ClassDescriptor toTrait, JetType delegateExpressionType, DelegationFieldsInfo.Field field) { + for (Map.Entry entry : CodegenUtilKt.getDelegates(descriptor, toTrait, delegateExpressionType).entrySet()) { CallableMemberDescriptor callableMemberDescriptor = entry.getKey(); - CallableMemberDescriptor overriddenDescriptor = entry.getValue(); + CallableDescriptor delegateTo = entry.getValue(); if (callableMemberDescriptor instanceof PropertyDescriptor) { propertyCodegen - .genDelegate((PropertyDescriptor) callableMemberDescriptor, (PropertyDescriptor) overriddenDescriptor, field.getStackValue()); + .genDelegate((PropertyDescriptor) callableMemberDescriptor, (PropertyDescriptor) delegateTo, field.getStackValue()); } else if (callableMemberDescriptor instanceof FunctionDescriptor) { functionCodegen - .genDelegate((FunctionDescriptor) callableMemberDescriptor, (FunctionDescriptor) overriddenDescriptor, field.getStackValue()); + .genDelegate((FunctionDescriptor) callableMemberDescriptor, (FunctionDescriptor) delegateTo, field.getStackValue()); } } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java index f0421bc429d..c9776979ba4 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java @@ -484,21 +484,19 @@ public class PropertyCodegen { return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString()); } - public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor overridden, @NotNull StackValue field) { - ClassDescriptor toClass = (ClassDescriptor) overridden.getContainingDeclaration(); + public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor delegateTo, @NotNull StackValue field) { + ClassDescriptor toClass = (ClassDescriptor) delegateTo.getContainingDeclaration(); PropertyGetterDescriptor getter = delegate.getGetter(); if (getter != null) { //noinspection ConstantConditions - functionCodegen.genDelegate(getter, toClass, field, - typeMapper.mapSignature(getter), typeMapper.mapSignature(overridden.getGetter().getOriginal())); + functionCodegen.genDelegate(getter, delegateTo.getGetter().getOriginal(), toClass, field); } PropertySetterDescriptor setter = delegate.getSetter(); if (setter != null) { //noinspection ConstantConditions - functionCodegen.genDelegate(setter, toClass, field, - typeMapper.mapSignature(setter), typeMapper.mapSignature(overridden.getSetter().getOriginal())); + functionCodegen.genDelegate(setter, delegateTo.getSetter().getOriginal(), toClass, field); } } } diff --git a/compiler/testData/codegen/boxAgainstJava/notNullAssertions/Delegation.java b/compiler/testData/codegen/boxAgainstJava/notNullAssertions/Delegation.java new file mode 100644 index 00000000000..d6ce0b6c4bb --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/notNullAssertions/Delegation.java @@ -0,0 +1,7 @@ +public class Delegation { + public static class ReturnNull { + public String foo() { + return null; + } + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxAgainstJava/notNullAssertions/Delegation.kt b/compiler/testData/codegen/boxAgainstJava/notNullAssertions/Delegation.kt new file mode 100644 index 00000000000..29b0e162a71 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/notNullAssertions/Delegation.kt @@ -0,0 +1,21 @@ +trait Tr { + fun foo(): String +} + +class DelegateTo : Delegation.ReturnNull(), Tr { + override fun foo() = super.foo() +} + +class DelegateFrom : Tr by DelegateTo() { +} + +fun box(): String { + try { + DelegateFrom().foo() + return "Fail: should have been an exception" + } + catch(e: IllegalStateException) { + println(e.getMessage()) + return "OK" + } +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java index 44a835c763f..3c0647432fb 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java @@ -32,7 +32,7 @@ import java.util.regex.Pattern; @SuppressWarnings("all") @TestMetadata("compiler/testData/codegen/boxAgainstJava") @TestDataPath("$PROJECT_ROOT") -@InnerTestClasses({BlackBoxAgainstJavaCodegenTestGenerated.Annotations.class, BlackBoxAgainstJavaCodegenTestGenerated.CallableReference.class, BlackBoxAgainstJavaCodegenTestGenerated.Constructor.class, BlackBoxAgainstJavaCodegenTestGenerated.Delegation.class, BlackBoxAgainstJavaCodegenTestGenerated.Enum.class, BlackBoxAgainstJavaCodegenTestGenerated.Functions.class, BlackBoxAgainstJavaCodegenTestGenerated.InnerClass.class, BlackBoxAgainstJavaCodegenTestGenerated.Property.class, BlackBoxAgainstJavaCodegenTestGenerated.Reflection.class, BlackBoxAgainstJavaCodegenTestGenerated.Sam.class, BlackBoxAgainstJavaCodegenTestGenerated.StaticFun.class, BlackBoxAgainstJavaCodegenTestGenerated.Visibility.class}) +@InnerTestClasses({BlackBoxAgainstJavaCodegenTestGenerated.Annotations.class, BlackBoxAgainstJavaCodegenTestGenerated.CallableReference.class, BlackBoxAgainstJavaCodegenTestGenerated.Constructor.class, BlackBoxAgainstJavaCodegenTestGenerated.Delegation.class, BlackBoxAgainstJavaCodegenTestGenerated.Enum.class, BlackBoxAgainstJavaCodegenTestGenerated.Functions.class, BlackBoxAgainstJavaCodegenTestGenerated.InnerClass.class, BlackBoxAgainstJavaCodegenTestGenerated.NotNullAssertions.class, BlackBoxAgainstJavaCodegenTestGenerated.Property.class, BlackBoxAgainstJavaCodegenTestGenerated.Reflection.class, BlackBoxAgainstJavaCodegenTestGenerated.Sam.class, BlackBoxAgainstJavaCodegenTestGenerated.StaticFun.class, BlackBoxAgainstJavaCodegenTestGenerated.Visibility.class}) @RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class) public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testAllFilesPresentInBoxAgainstJava() throws Exception { @@ -265,6 +265,22 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCod } + @TestMetadata("compiler/testData/codegen/boxAgainstJava/notNullAssertions") + @TestDataPath("$PROJECT_ROOT") + @RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class) + public static class NotNullAssertions extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInNotNullAssertions() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxAgainstJava/notNullAssertions"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("Delegation.kt") + public void testDelegation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/notNullAssertions/Delegation.kt"); + doTestAgainstJava(fileName); + } + + } + @TestMetadata("compiler/testData/codegen/boxAgainstJava/property") @TestDataPath("$PROJECT_ROOT") @RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class) diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/DelegationTranslator.kt b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/DelegationTranslator.kt index d72fac4cf0d..664a1e58cfc 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/DelegationTranslator.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/DelegationTranslator.kt @@ -36,6 +36,7 @@ import java.util.HashMap import org.jetbrains.k2js.translate.declaration.propertyTranslator.addGetterAndSetter import org.jetbrains.k2js.translate.utils.ManglingUtils.getMangledMemberNameForExplicitDelegation import org.jetbrains.k2js.translate.utils.generateDelegateCall +import org.jetbrains.jet.backend.common.CodegenUtilKt public class DelegationTranslator( private val classDeclaration: JetClassOrObject, @@ -90,7 +91,7 @@ public class DelegationTranslator( CodegenUtil.getSuperClassByDelegationSpecifier(specifier, bindingContext()) private fun generateDelegates(toClass: ClassDescriptor, field: Field, properties: MutableList) { - for ((descriptor, overriddenDescriptor) in CodegenUtil.getDelegates(classDescriptor, toClass)) { + for ((descriptor, overriddenDescriptor) in CodegenUtilKt.getDelegates(classDescriptor, toClass)) { when (descriptor) { is PropertyDescriptor -> generateDelegateCallForPropertyMember(descriptor, field.name, properties)