Assertions when approximating platform types in delegation by expression

This commit is contained in:
Andrey Breslav
2014-10-05 22:19:30 +04:00
parent f1c66fa6b0
commit a737352b5d
9 changed files with 151 additions and 50 deletions
@@ -126,26 +126,6 @@ public class CodegenUtil {
Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)); Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor));
} }
public static Map<CallableMemberDescriptor, CallableMemberDescriptor> getDelegates(ClassDescriptor descriptor, ClassDescriptor toClass) {
Map<CallableMemberDescriptor, CallableMemberDescriptor> result = new LinkedHashMap<CallableMemberDescriptor, CallableMemberDescriptor>();
for (DeclarationDescriptor declaration : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
if (declaration instanceof CallableMemberDescriptor) {
CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) declaration;
if (callableMemberDescriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION) {
Set<? extends CallableMemberDescriptor> 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 @NotNull
public static Map<FunctionDescriptor, FunctionDescriptor> getTraitMethods(ClassDescriptor descriptor) { public static Map<FunctionDescriptor, FunctionDescriptor> getTraitMethods(ClassDescriptor descriptor) {
Map<FunctionDescriptor, FunctionDescriptor> result = new LinkedHashMap<FunctionDescriptor, FunctionDescriptor>(); Map<FunctionDescriptor, FunctionDescriptor> result = new LinkedHashMap<FunctionDescriptor, FunctionDescriptor>();
@@ -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<member of Foo, corresponding member of typeOf(baz)>
public [platformStatic] fun getDelegates(
descriptor: ClassDescriptor,
toTrait: ClassDescriptor,
delegateExpressionType: JetType? = null
): Map<CallableMemberDescriptor, CallableDescriptor> {
return descriptor.getDefaultType().getMemberScope().getAllDescriptors().stream()
.filterIsInstance(javaClass<CallableMemberDescriptor>())
.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()
}
}
}
@@ -25,7 +25,6 @@ import com.intellij.util.containers.ContainerUtil;
import kotlin.Function1; import kotlin.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.binding.CodegenBinding; import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.bridges.Bridge; import org.jetbrains.jet.codegen.bridges.Bridge;
import org.jetbrains.jet.codegen.bridges.BridgesPackage; 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.JvmMethodParameterSignature;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature; import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature;
import org.jetbrains.jet.lang.resolve.name.FqName; 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.jet.lang.types.lang.KotlinBuiltIns;
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;
@@ -799,21 +800,20 @@ public class FunctionCodegen extends ParentCodegenAware {
} }
public void genDelegate(FunctionDescriptor functionDescriptor, FunctionDescriptor overriddenDescriptor, StackValue field) { public void genDelegate(FunctionDescriptor functionDescriptor, FunctionDescriptor overriddenDescriptor, StackValue field) {
genDelegate(functionDescriptor, (ClassDescriptor) overriddenDescriptor.getContainingDeclaration(), field, genDelegate(functionDescriptor, overriddenDescriptor.getOriginal(), (ClassDescriptor) overriddenDescriptor.getContainingDeclaration(), field
typeMapper.mapSignature(functionDescriptor),
typeMapper.mapSignature(overriddenDescriptor.getOriginal())
); );
} }
public void genDelegate( public void genDelegate(
FunctionDescriptor functionDescriptor, final FunctionDescriptor delegateFunction,
final FunctionDescriptor delegatedTo,
final ClassDescriptor toClass, final ClassDescriptor toClass,
final StackValue field, final StackValue field
final JvmMethodSignature jvmDelegateMethodSignature,
final JvmMethodSignature jvmDelegatingMethodSignature
) { ) {
final JvmMethodSignature jvmDelegateMethodSignature = typeMapper.mapSignature(delegateFunction);
final JvmMethodSignature jvmDelegateToMethodSignature = typeMapper.mapSignature(delegatedTo);
generateMethod( generateMethod(
OtherOrigin(functionDescriptor), jvmDelegateMethodSignature, functionDescriptor, OtherOrigin(delegateFunction), jvmDelegateMethodSignature, delegateFunction,
new FunctionGenerationStrategy() { new FunctionGenerationStrategy() {
@Override @Override
public void generateBody( public void generateBody(
@@ -823,11 +823,11 @@ public class FunctionCodegen extends ParentCodegenAware {
@NotNull MethodContext context, @NotNull MethodContext context,
@NotNull MemberCodegen<?> parentCodegen @NotNull MemberCodegen<?> parentCodegen
) { ) {
Method overriddenMethod = jvmDelegatingMethodSignature.getAsmMethod(); Method delegateToMethod = jvmDelegateToMethodSignature.getAsmMethod();
Method delegateMethod = jvmDelegateMethodSignature.getAsmMethod(); Method delegateMethod = jvmDelegateMethodSignature.getAsmMethod();
Type[] argTypes = delegateMethod.getArgumentTypes(); Type[] argTypes = delegateMethod.getArgumentTypes();
Type[] originalArgTypes = overriddenMethod.getArgumentTypes(); Type[] originalArgTypes = delegateToMethod.getArgumentTypes();
InstructionAdapter iv = new InstructionAdapter(mv); InstructionAdapter iv = new InstructionAdapter(mv);
iv.load(0, OBJECT_TYPE); iv.load(0, OBJECT_TYPE);
@@ -840,13 +840,22 @@ public class FunctionCodegen extends ParentCodegenAware {
String internalName = typeMapper.mapType(toClass).getInternalName(); String internalName = typeMapper.mapType(toClass).getInternalName();
if (toClass.getKind() == ClassKind.TRAIT) { if (toClass.getKind() == ClassKind.TRAIT) {
iv.invokeinterface(internalName, overriddenMethod.getName(), overriddenMethod.getDescriptor()); iv.invokeinterface(internalName, delegateToMethod.getName(), delegateToMethod.getDescriptor());
} }
else { 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()); iv.areturn(delegateMethod.getReturnType());
} }
@@ -24,6 +24,7 @@ import kotlin.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.backend.common.CodegenUtil; 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.backend.common.DataClassMethodGenerator;
import org.jetbrains.jet.codegen.binding.MutableClosure; import org.jetbrains.jet.codegen.binding.MutableClosure;
import org.jetbrains.jet.codegen.context.*; import org.jetbrains.jet.codegen.context.*;
@@ -1286,13 +1287,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
JetExpression expression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression(); JetExpression expression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression();
PropertyDescriptor propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, descriptor, bindingContext); PropertyDescriptor propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, descriptor, bindingContext);
ClassDescriptor superClassDescriptor = getSuperClass(specifier);
if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext)) { if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext)) {
result.addField((JetDelegatorByExpressionSpecifier) specifier, propertyDescriptor); result.addField((JetDelegatorByExpressionSpecifier) specifier, propertyDescriptor);
} }
else { 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++; n++;
} }
@@ -1648,7 +1651,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
if (specifier instanceof JetDelegatorByExpressionSpecifier) { if (specifier instanceof JetDelegatorByExpressionSpecifier) {
DelegationFieldsInfo.Field field = delegationFieldsInfo.getInfo((JetDelegatorByExpressionSpecifier) specifier); DelegationFieldsInfo.Field field = delegationFieldsInfo.getInfo((JetDelegatorByExpressionSpecifier) specifier);
generateDelegateField(field); 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); fieldInfo.name, fieldInfo.type.getDescriptor(), /*TODO*/null, null);
} }
protected void generateDelegates(ClassDescriptor toClass, DelegationFieldsInfo.Field field) { protected void generateDelegates(ClassDescriptor toTrait, JetType delegateExpressionType, DelegationFieldsInfo.Field field) {
for (Map.Entry<CallableMemberDescriptor, CallableMemberDescriptor> entry : CodegenUtil.getDelegates(descriptor, toClass).entrySet()) { for (Map.Entry<CallableMemberDescriptor, CallableDescriptor> entry : CodegenUtilKt.getDelegates(descriptor, toTrait, delegateExpressionType).entrySet()) {
CallableMemberDescriptor callableMemberDescriptor = entry.getKey(); CallableMemberDescriptor callableMemberDescriptor = entry.getKey();
CallableMemberDescriptor overriddenDescriptor = entry.getValue(); CallableDescriptor delegateTo = entry.getValue();
if (callableMemberDescriptor instanceof PropertyDescriptor) { if (callableMemberDescriptor instanceof PropertyDescriptor) {
propertyCodegen propertyCodegen
.genDelegate((PropertyDescriptor) callableMemberDescriptor, (PropertyDescriptor) overriddenDescriptor, field.getStackValue()); .genDelegate((PropertyDescriptor) callableMemberDescriptor, (PropertyDescriptor) delegateTo, field.getStackValue());
} }
else if (callableMemberDescriptor instanceof FunctionDescriptor) { else if (callableMemberDescriptor instanceof FunctionDescriptor) {
functionCodegen functionCodegen
.genDelegate((FunctionDescriptor) callableMemberDescriptor, (FunctionDescriptor) overriddenDescriptor, field.getStackValue()); .genDelegate((FunctionDescriptor) callableMemberDescriptor, (FunctionDescriptor) delegateTo, field.getStackValue());
} }
} }
} }
@@ -484,21 +484,19 @@ public class PropertyCodegen {
return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString()); return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
} }
public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor overridden, @NotNull StackValue field) { public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor delegateTo, @NotNull StackValue field) {
ClassDescriptor toClass = (ClassDescriptor) overridden.getContainingDeclaration(); ClassDescriptor toClass = (ClassDescriptor) delegateTo.getContainingDeclaration();
PropertyGetterDescriptor getter = delegate.getGetter(); PropertyGetterDescriptor getter = delegate.getGetter();
if (getter != null) { if (getter != null) {
//noinspection ConstantConditions //noinspection ConstantConditions
functionCodegen.genDelegate(getter, toClass, field, functionCodegen.genDelegate(getter, delegateTo.getGetter().getOriginal(), toClass, field);
typeMapper.mapSignature(getter), typeMapper.mapSignature(overridden.getGetter().getOriginal()));
} }
PropertySetterDescriptor setter = delegate.getSetter(); PropertySetterDescriptor setter = delegate.getSetter();
if (setter != null) { if (setter != null) {
//noinspection ConstantConditions //noinspection ConstantConditions
functionCodegen.genDelegate(setter, toClass, field, functionCodegen.genDelegate(setter, delegateTo.getSetter().getOriginal(), toClass, field);
typeMapper.mapSignature(setter), typeMapper.mapSignature(overridden.getSetter().getOriginal()));
} }
} }
} }
@@ -0,0 +1,7 @@
public class Delegation {
public static class ReturnNull {
public String foo() {
return null;
}
}
}
@@ -0,0 +1,21 @@
trait Tr {
fun foo(): String
}
class DelegateTo : Delegation.ReturnNull(), Tr {
override fun foo() = super<Delegation.ReturnNull>.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"
}
}
@@ -32,7 +32,7 @@ import java.util.regex.Pattern;
@SuppressWarnings("all") @SuppressWarnings("all")
@TestMetadata("compiler/testData/codegen/boxAgainstJava") @TestMetadata("compiler/testData/codegen/boxAgainstJava")
@TestDataPath("$PROJECT_ROOT") @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) @RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class)
public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInBoxAgainstJava() throws Exception { 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") @TestMetadata("compiler/testData/codegen/boxAgainstJava/property")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class) @RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class)
@@ -36,6 +36,7 @@ import java.util.HashMap
import org.jetbrains.k2js.translate.declaration.propertyTranslator.addGetterAndSetter import org.jetbrains.k2js.translate.declaration.propertyTranslator.addGetterAndSetter
import org.jetbrains.k2js.translate.utils.ManglingUtils.getMangledMemberNameForExplicitDelegation import org.jetbrains.k2js.translate.utils.ManglingUtils.getMangledMemberNameForExplicitDelegation
import org.jetbrains.k2js.translate.utils.generateDelegateCall import org.jetbrains.k2js.translate.utils.generateDelegateCall
import org.jetbrains.jet.backend.common.CodegenUtilKt
public class DelegationTranslator( public class DelegationTranslator(
private val classDeclaration: JetClassOrObject, private val classDeclaration: JetClassOrObject,
@@ -90,7 +91,7 @@ public class DelegationTranslator(
CodegenUtil.getSuperClassByDelegationSpecifier(specifier, bindingContext()) CodegenUtil.getSuperClassByDelegationSpecifier(specifier, bindingContext())
private fun generateDelegates(toClass: ClassDescriptor, field: Field, properties: MutableList<JsPropertyInitializer>) { private fun generateDelegates(toClass: ClassDescriptor, field: Field, properties: MutableList<JsPropertyInitializer>) {
for ((descriptor, overriddenDescriptor) in CodegenUtil.getDelegates(classDescriptor, toClass)) { for ((descriptor, overriddenDescriptor) in CodegenUtilKt.getDelegates(classDescriptor, toClass)) {
when (descriptor) { when (descriptor) {
is PropertyDescriptor -> is PropertyDescriptor ->
generateDelegateCallForPropertyMember(descriptor, field.name, properties) generateDelegateCallForPropertyMember(descriptor, field.name, properties)