Fix for KT-9958: NoSuchMethodError when we call protected method from companion object
#KT-9958 Fixed
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package a
|
||||
|
||||
import b.*
|
||||
|
||||
class B {
|
||||
companion object : A() {}
|
||||
|
||||
init {
|
||||
foo()
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
B()
|
||||
return result
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
+12
@@ -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
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user