Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 14426b1a authored by Makoto Onuki's avatar Makoto Onuki Committed by Android (Google) Code Review
Browse files

Merge "Make Ravenwood annotations runtime-visible, but only on Ravewood" into main

parents f5b77c3e 6addcb4f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ class HostStubGenClassProcessor(
            deleteClassFinals = options.deleteFinals.get,
            deleteMethodFinals = options.deleteFinals.get,
            throwExceptionType = options.throwExceptionType.get,
            annotationsToMakeVisible = options.allAnnotationSet
        )

        val verbosePrinter = PrintWriter(log.getWriter(LogLevel.Verbose))
+7 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.hoststubgen
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.utils.ArgIterator
import com.android.hoststubgen.utils.BaseOptions
import com.android.hoststubgen.utils.ClassDescriptorSet
import com.android.hoststubgen.utils.FileOrResource
import com.android.hoststubgen.utils.SetOnce

@@ -68,6 +69,8 @@ open class HostStubGenClassProcessorOptions(
    val enableClassChecker: SetOnce<Boolean> = SetOnce(false),
    val enablePreTrace: SetOnce<Boolean> = SetOnce(false),
    val enablePostTrace: SetOnce<Boolean> = SetOnce(false),

    val allAnnotationSet: ClassDescriptorSet = ClassDescriptorSet()
) : BaseOptions() {

    private val allAnnotations = mutableSetOf<String>()
@@ -83,7 +86,10 @@ open class HostStubGenClassProcessorOptions(
        // Define some shorthands...
        fun nextArg(): String = args.nextArgRequired(option)
        fun MutableSet<String>.addUniqueAnnotationArg(): String =
            nextArg().also { this += ensureUniqueAnnotation(it) }
            nextArg().also {
                this += ensureUniqueAnnotation(it)
                allAnnotationSet.addType(it)
            }

        when (option) {
            "--policy-override-file" -> policyOverrideFiles.add(FileOrResource(nextArg()))
+42 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * 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 com.android.hoststubgen.utils

import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.asm.toJvmClassName

/**
 * Represents a set of "class descriptors" for faster lookup, which supports all of the
 * "com/android/ravenwood/Xxx", "Lcom/android/ravenwood/Xxx;" and
 * "com.android.ravenwood.Xxx" formats.
 */
class ClassDescriptorSet {
    private val matches = mutableSetOf<String>()

    fun addType(typeName: String) {
        val internalName = typeName.toJvmClassName()
        matches.add(internalName)
        matches.add("L$internalName;")
        matches.add(typeName.toHumanReadableClassName())
    }

    fun contains(descriptor: String?): Boolean {
        if (descriptor == null) {
            return false
        }
        return matches.contains(descriptor)
    }
}
 No newline at end of file
+78 −2
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ import com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
import com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrowButSupported
import com.android.hoststubgen.hosthelper.HostTestUtils
import com.android.hoststubgen.log
import com.android.hoststubgen.utils.ClassDescriptorSet
import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.MethodVisitor
@@ -49,6 +51,7 @@ import org.objectweb.asm.Opcodes.INVOKESPECIAL
import org.objectweb.asm.Opcodes.INVOKESTATIC
import org.objectweb.asm.Opcodes.INVOKEVIRTUAL
import org.objectweb.asm.Type
import java.lang.annotation.RetentionPolicy

const val OPCODE_VERSION = Opcodes.ASM9

@@ -77,6 +80,9 @@ class ImplGeneratingAdapter(
        // val deleteFieldFinals: Boolean,

        val throwExceptionType: String,

        // We make all annotations in this set "runtime-visible".
        val annotationsToMakeVisible: ClassDescriptorSet,
    )

    private lateinit var currentPackageName: String
@@ -153,6 +159,32 @@ class ImplGeneratingAdapter(
        super.visitEnd()
    }

    /**
     * Tweak annotation "visibility" aka "retention policy".
     */
    override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
        // If it's a "known" annotation -- i.e. any Ravenwood annotations -- we do the following:
        // 1. For the annotation type itself, change the retention policy to "RUNTIME".
        // 2. Make the annotation "runtime-visible" across the whole jar.

        // For 1.
        if (options.annotationsToMakeVisible.contains(currentClassName)) {
            // This current type is a known annotation. We change the retention policy.

            if ("Ljava/lang/annotation/Retention;" == descriptor) {
                // If it is, we return our custom AnnotationVisitor to modify its value.
                // We pass the original visitor from the superclass to maintain the chain.
                return RetentionPolicyAnnotationVisitor(
                    super.visitAnnotation(descriptor, visible))
            }
        }

        // For 2. If the annotation we're processing now is "known" (i.e. RavenwoodKeep, etc),
        // force it to be "runtime-visible" aka RUNTIME.
        return super.visitAnnotation(descriptor,
            visible || options.annotationsToMakeVisible.contains(descriptor))
    }

    var skipMemberModificationNestCount = 0

    /**
@@ -236,7 +268,7 @@ class ImplGeneratingAdapter(
            UnifiedVisitor.on(ret)
                .visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true)

            return ret
            return ForceFieldAnnotationVisibilityVisitor(ret)
        }
    }

@@ -328,7 +360,7 @@ class ImplGeneratingAdapter(
                    .visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true)
            }

            return ret
            return ForceMethodAnnotationVisibilityVisitor(ret)
        }
    }

@@ -655,4 +687,48 @@ class ImplGeneratingAdapter(
            }
        }
    }



    /**
     * An AnnotationVisitor that specifically targets the `value` of a @Retention
     * annotation and forces it to be `RUNTIME`.
     */
    class RetentionPolicyAnnotationVisitor(
        next: AnnotationVisitor?,
    ) : AnnotationVisitor(OPCODE_VERSION, next) {
        override fun visitEnum(name: String?, descriptor: String?, value: String?) {
            // We only care about the "value" property of the @Retention annotation.
            if ("value" == name && "Ljava/lang/annotation/RetentionPolicy;" == descriptor) {
                super.visitEnum(name, descriptor, RetentionPolicy.RUNTIME.name)
            } else {
                // For any other enum property, delegate to the default behavior.
                super.visitEnum(name, descriptor, value)
            }
        }
    }

    /**
     * Force a field's annotation to be runtime-visible if it's "known".
     */
    inner class ForceFieldAnnotationVisibilityVisitor(
        next: FieldVisitor?,
    ) : FieldVisitor(OPCODE_VERSION, next) {
        override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
            return super.visitAnnotation(descriptor,
                visible || options.annotationsToMakeVisible.contains(descriptor))
        }
    }

    /**
     * Force a method's annotation to be runtime-visible if it's "known".
     */
    inner class ForceMethodAnnotationVisibilityVisitor(
        next: MethodVisitor?,
    ) : MethodVisitor(OPCODE_VERSION, next) {
        override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
            return super.visitAnnotation(descriptor,
                visible || options.annotationsToMakeVisible.contains(descriptor))
        }
    }
}
+32 −92

File changed.

Preview size limit exceeded, changes collapsed.

Loading