Fix for KT-9958: NoSuchMethodError when we call protected method from companion object

#KT-9958 Fixed
This commit is contained in:
Michael Bogdanov
2015-12-04 13:55:40 +03:00
parent a7eea6e4dd
commit a2d644f708
11 changed files with 177 additions and 24 deletions
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -51,11 +52,19 @@ public abstract class ClassBodyCodegen extends MemberCodegen<KtClassOrObject> {
@Override
protected void generateBody() {
List<KtObjectDeclaration> companions = new ArrayList<KtObjectDeclaration>();
if (kind != OwnerKind.DEFAULT_IMPLS) {
//generate nested classes first and only then generate class body. It necessary to access to nested CodegenContexts
for (KtDeclaration declaration : myClass.getDeclarations()) {
if (shouldProcessFirst(declaration)) {
generateDeclaration(declaration);
//Generate companions after class body generation (need to record all synthetic accessors)
if (declaration instanceof KtObjectDeclaration && ((KtObjectDeclaration) declaration).isCompanion()) {
companions.add((KtObjectDeclaration) declaration);
CodegenUtilKt.populateCompanionBackingFieldNamesToOuterContextIfNeeded((KtObjectDeclaration) declaration, context, state);
}
else {
generateDeclaration(declaration);
}
}
}
}
@@ -66,6 +75,12 @@ public abstract class ClassBodyCodegen extends MemberCodegen<KtClassOrObject> {
}
}
generatePrimaryConstructorProperties();
generateConstructors();
for (KtObjectDeclaration companion : companions) {
generateDeclaration(companion);
}
if (!DescriptorUtils.isInterface(descriptor)) {
for (DeclarationDescriptor memberDescriptor : DescriptorUtils.getAllDescriptors(descriptor.getDefaultType().getMemberScope())) {
if (memberDescriptor instanceof CallableMemberDescriptor) {
@@ -88,8 +103,10 @@ public abstract class ClassBodyCodegen extends MemberCodegen<KtClassOrObject> {
}
}
}
}
protected void generateConstructors() {
generatePrimaryConstructorProperties();
}
private static boolean shouldProcessFirst(KtDeclaration declaration) {
@@ -98,6 +98,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
private List<PropertyAndDefaultValue> companionObjectPropertiesToCopy;
private final DelegationFieldsInfo delegationFieldsInfo;
private final List<Function2<ImplementationBodyCodegen, ClassBuilder, Unit>> additionalTasks =
new ArrayList<Function2<ImplementationBodyCodegen, ClassBuilder, Unit>>();
@@ -112,6 +114,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
super(aClass, context, v, state, parentCodegen);
this.classAsmType = typeMapper.mapClass(descriptor);
this.isLocal = isLocal;
delegationFieldsInfo = getDelegationFieldsInfo(myClass.getSuperTypeListEntries());
}
@Override
@@ -228,7 +231,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
@Override
protected void generateBody() {
super.generateBody();
if (isInterface(descriptor) && !isLocal) {
Type defaultImplsType = state.getTypeMapper().mapDefaultImpls(descriptor);
ClassBuilder defaultImplsBuilder =
@@ -237,9 +239,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
CodegenContext parentContext = context.getParentContext();
assert parentContext != null : "Parent context of interface declaration should not be null";
ClassContext defaultImplsContext = parentContext.intoClass(descriptor, OwnerKind.DEFAULT_IMPLS, state);
ClassContext defaultImplsContext = parentContext.intoDefaultImplsClass(descriptor, (ClassContext) context, state);
new InterfaceImplBodyCodegen(myClass, defaultImplsContext, defaultImplsBuilder, state, this).generate();
}
super.generateBody();
}
@Override
@@ -371,24 +374,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
generateCompanionObjectBackingFieldCopies();
DelegationFieldsInfo delegationFieldsInfo = getDelegationFieldsInfo(myClass.getSuperTypeListEntries());
try {
lookupConstructorExpressionsInClosureIfPresent();
generatePrimaryConstructor(delegationFieldsInfo);
for (ConstructorDescriptor secondaryConstructor : DescriptorUtilsKt.getSecondaryConstructors(descriptor)) {
generateSecondaryConstructor(secondaryConstructor);
}
}
catch (CompilationException e) {
throw e;
}
catch (ProcessCanceledException e) {
throw e;
}
catch (RuntimeException e) {
throw new RuntimeException("Error generating constructors of class " + myClass.getName() + " with kind " + kind, e);
}
generateTraitMethods();
generateDelegates(delegationFieldsInfo);
@@ -410,6 +395,26 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
}
}
@Override
protected void generateConstructors() {
try {
lookupConstructorExpressionsInClosureIfPresent();
generatePrimaryConstructor(delegationFieldsInfo);
for (ConstructorDescriptor secondaryConstructor : DescriptorUtilsKt.getSecondaryConstructors(descriptor)) {
generateSecondaryConstructor(secondaryConstructor);
}
}
catch (CompilationException e) {
throw e;
}
catch (ProcessCanceledException e) {
throw e;
}
catch (RuntimeException e) {
throw new RuntimeException("Error generating constructors of class " + myClass.getName() + " with kind " + kind, e);
}
}
private boolean isGenericToArrayPresent() {
Collection<FunctionDescriptor> functions =
descriptor.getDefaultType().getMemberScope().getContributedFunctions(Name.identifier("toArray"), NoLookupLocation.FROM_BACKEND);
@@ -17,8 +17,15 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -68,3 +75,23 @@ fun generateNullCheckForNonSafeAs(
public fun SpecialSignatureInfo.replaceValueParametersIn(sourceSignature: String?): String?
= valueParametersSignature?.let { sourceSignature?.replace("^\\(.*\\)".toRegex(), "($it)") }
fun populateCompanionBackingFieldNamesToOuterContextIfNeeded(companion: KtObjectDeclaration, outerContext: FieldOwnerContext<*>, state: GenerationState) {
val descriptor = state.bindingContext.get(BindingContext.CLASS, companion)
if (descriptor == null || ErrorUtils.isError(descriptor)) {
return
}
if (!AsmUtil.isCompanionObjectWithBackingFieldsInOuter(descriptor)) {
return
}
val properties = companion.declarations.filterIsInstance<KtProperty>()
properties.forEach {
val variableDescriptor = state.bindingContext.get(BindingContext.VARIABLE, it)
if (variableDescriptor is PropertyDescriptor) {
outerContext.getFieldName(variableDescriptor, it.hasDelegate())
}
}
}
@@ -266,6 +266,10 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
return new MultifileClassFacadeContext(descriptor, this, multifileClassType, filePartType);
}
public ClassContext intoDefaultImplsClass(ClassDescriptor descriptor, ClassContext interfaceContext, GenerationState state) {
return new DefaultImplsClassContext(state.getTypeMapper(), descriptor, OwnerKind.DEFAULT_IMPLS, this, null, interfaceContext);
}
@NotNull
public ClassContext intoClass(ClassDescriptor descriptor, OwnerKind kind, GenerationState state) {
if (descriptor.isCompanionObject()) {
@@ -624,7 +628,7 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
}
@Nullable
public CodegenContext findChildContext(@NotNull DeclarationDescriptor child) {
protected CodegenContext findChildContext(@NotNull DeclarationDescriptor child) {
return childContexts == null ? null : childContexts.get(child);
}
@@ -0,0 +1,36 @@
/*
* 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.codegen.context
import org.jetbrains.kotlin.codegen.OwnerKind
import org.jetbrains.kotlin.codegen.state.JetTypeMapper
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
class DefaultImplsClassContext(
typeMapper: JetTypeMapper,
contextDescriptor: ClassDescriptor,
contextKind: OwnerKind,
parentContext: CodegenContext<*>?,
localLookup: ((DeclarationDescriptor) -> Boolean)?,
val interfaceContext: ClassContext
) : ClassContext(typeMapper, contextDescriptor, contextKind, parentContext, localLookup) {
override fun getCompanionObjectContext(): CodegenContext<*>? {
return interfaceContext.companionObjectContext
}
}
+16
View File
@@ -0,0 +1,16 @@
package a
import b.*
class B {
companion object : A() {}
init {
foo()
}
}
fun box(): String {
B()
return result
}
+9
View File
@@ -0,0 +1,9 @@
package b
var result = "fail"
abstract class A {
protected fun foo() {
result = "OK"
}
}
@@ -0,0 +1,18 @@
package a
import b.*
interface B {
companion object : A() {}
fun test() {
foo()
}
}
class C : B
fun box(): String {
C().test()
return result
}
@@ -0,0 +1,9 @@
package b
var result = "fail"
abstract class A {
protected fun foo() {
result = "OK"
}
}
@@ -107,6 +107,18 @@ public class BlackBoxMultiFileCodegenTestGenerated extends AbstractBlackBoxCodeg
doTestMultiFile(fileName);
}
@TestMetadata("kt9958")
public void testKt9958() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxMultiFile/kt9958/");
doTestMultiFile(fileName);
}
@TestMetadata("kt9958Interface")
public void testKt9958Interface() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxMultiFile/kt9958Interface/");
doTestMultiFile(fileName);
}
@TestMetadata("mainInFiles")
public void testMainInFiles() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxMultiFile/mainInFiles/");
@@ -1,7 +1,7 @@
a java.lang.Deprecated 0
p org.test 0
c 0 0/SomeClass$Companion
c 0 0/SomeClass$SomeInnerObject
c 0 0/SomeClass$InnerClass
c 0 0/SomeClass$InnerClass$InnerClassInInnerClass
c 0 0/SomeClass$NestedClass
c 0 0/SomeClass$Companion