Skip to content

Commit

Permalink
Add support to MemberDescriptorReferencedClassVisitor for visiting …
Browse files Browse the repository at this point in the history
…referenced Kotlin inline class parameters
  • Loading branch information
James Hamilton committed Jan 6, 2022
1 parent 818baea commit cdf64c2
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 4 deletions.
1 change: 1 addition & 0 deletions docs/md/releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- Convert to/from Kotlin unsigned integers in Kotlin annotation unsigned type arguments. (`T5405`)
- Initialize array dimension in Kotlin annotation `ClassValue` type arguments. (`T5406`)
- Add support for Kotlin inline class underlying type to Kotlin metadata model. (`T4774`)
- Add support to `MemberDescriptorReferencedClassVisitor` for visiting referenced Kotlin inline class parameters. (`T13653`)

## Version 8.0.4

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* ProGuardCORE -- library to process Java bytecode.
*
* Copyright (c) 2002-2020 Guardsquare NV
* Copyright (c) 2002-2022 Guardsquare NV
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,22 +18,38 @@
package proguard.classfile.visitor;

import proguard.classfile.*;
import proguard.classfile.kotlin.KotlinFunctionMetadata;
import proguard.classfile.kotlin.KotlinMetadata;
import proguard.classfile.kotlin.KotlinTypeMetadata;
import proguard.classfile.kotlin.KotlinValueParameterMetadata;
import proguard.classfile.kotlin.visitor.*;
import proguard.classfile.kotlin.visitor.filter.KotlinClassKindFilter;

/**
* This {@link MemberVisitor} lets a given {@link ClassVisitor} visit all the classes
* referenced by the descriptors of the class members that it visits.
*
* It also takes into account functions with Kotlin inline class parameters, if
* includeKotlinMetadata = true: in the case of inline classes, in the underlying JVM
* method the actual class will not be referenced since the Kotlin compiler inlines uses.
*
* @author Eric Lafortune
*/
public class MemberDescriptorReferencedClassVisitor
implements MemberVisitor
{
private final ClassVisitor classVisitor;

private final KotlinFunctionDescriptorReferenceVisitor kotlinFunRefVisitor;

public MemberDescriptorReferencedClassVisitor(ClassVisitor classVisitor)
{
this.classVisitor = classVisitor;
this(false, classVisitor);
}

public MemberDescriptorReferencedClassVisitor(boolean includeKotlinMetadata, ClassVisitor classVisitor)
{
this.classVisitor = classVisitor;
this.kotlinFunRefVisitor = includeKotlinMetadata ? new KotlinFunctionDescriptorReferenceVisitor() : null;
}


Expand All @@ -43,12 +59,61 @@ public void visitProgramMember(ProgramClass programClass, ProgramMember programM
{
// Let the visitor visit the classes referenced in the descriptor string.
programMember.referencedClassesAccept(classVisitor);
if (this.kotlinFunRefVisitor != null)
{
programMember.accept(programClass, new MethodToKotlinFunctionVisitor(this.kotlinFunRefVisitor));
}
}


public void visitLibraryMember(LibraryClass programClass, LibraryMember libraryMember)
public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)
{
// Let the visitor visit the classes referenced in the descriptor string.
libraryMember.referencedClassesAccept(classVisitor);
if (this.kotlinFunRefVisitor != null)
{
libraryMember.accept(libraryClass, new MethodToKotlinFunctionVisitor(this.kotlinFunRefVisitor));
}
}


private class KotlinFunctionDescriptorReferenceVisitor
implements KotlinFunctionVisitor,
KotlinValueParameterVisitor,
KotlinTypeVisitor
{

@Override
public void visitAnyFunction(Clazz clazz,
KotlinMetadata kotlinMetadata,
KotlinFunctionMetadata kotlinFunctionMetadata)
{
kotlinFunctionMetadata.valueParametersAccept(clazz, kotlinMetadata, this);
}

@Override
public void visitAnyValueParameter(Clazz clazz, KotlinValueParameterMetadata kotlinValueParameterMetadata) { }

@Override
public void visitFunctionValParameter(Clazz clazz,
KotlinMetadata kotlinMetadata,
KotlinFunctionMetadata kotlinFunctionMetadata,
KotlinValueParameterMetadata kotlinValueParameterMetadata)
{
kotlinValueParameterMetadata.typeAccept(clazz, kotlinMetadata, kotlinFunctionMetadata, this);
}

@Override
public void visitAnyType(Clazz clazz, KotlinTypeMetadata kotlinTypeMetadata)
{
kotlinTypeMetadata.referencedClassAccept(
new ReferencedKotlinMetadataVisitor(
new KotlinClassKindFilter(
metadata -> metadata.flags.isValue,
new KotlinMetadataToClazzVisitor(classVisitor)
)
)
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* ProGuardCORE -- library to process Java bytecode.
*
* Copyright (c) 2002-2022 Guardsquare NV
*
* 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 proguard.classfile.kotlin.visitor

import io.kotest.core.spec.style.FreeSpec
import io.mockk.spyk
import io.mockk.verify
import proguard.classfile.visitor.AllMemberVisitor
import proguard.classfile.visitor.ClassVisitor
import proguard.classfile.visitor.MemberDescriptorReferencedClassVisitor
import testutils.ClassPoolBuilder
import testutils.KotlinSource

class MemberDescriptorReferencedClassVisitorTest : FreeSpec({
"Given an inline class" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
@JvmInline
value class Password(val s: String)

fun login(password: Password) {
println(password);
}
""".trimIndent()
)
)

"Then visiting the referenced descriptor methods of login with includeKotlinMetadata true" - {
val visitor = spyk<ClassVisitor>()
programClassPool.classesAccept(
"TestKt",
AllMemberVisitor(MemberDescriptorReferencedClassVisitor(true, visitor))
)

"Should visit the Password class" {
verify(exactly = 1) {
visitor.visitAnyClass(programClassPool.getClass("Password"))
}
}
}

"Then visiting the referenced descriptor methods of login with includeKotlinMetadata false" - {
val visitor = spyk<ClassVisitor>()
programClassPool.classesAccept(
"TestKt",
AllMemberVisitor(MemberDescriptorReferencedClassVisitor(false, visitor))
)

"Should not visit the Password class" {
verify(exactly = 0) {
visitor.visitAnyClass(programClassPool.getClass("Password"))
}
}
}
}
})

0 comments on commit cdf64c2

Please sign in to comment.