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

Commit b74f7485 authored by Diego Perez's avatar Diego Perez
Browse files

Add ability to promote classes to public visibility

Test: Added new PromoteClassClassAdapterTest
Change-Id: I30f9ee259d39e2b2768c1ceb45aa2161983c5a5e
(cherry picked from commit 294f0850f7623737899c9ea0b03cebc2cf7e4176)
parent 3084ec2d
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;

/**
 * Class that generates a new JAR from a list of classes, some of which are to be kept as-is
@@ -78,6 +79,8 @@ public class AsmGenerator {
    private final Map<String, ICreateInfo.InjectMethodRunnable> mInjectedMethodsMap;
    /** A map { FQCN => set { field names } } which should be promoted to public visibility */
    private final Map<String, Set<String>> mPromotedFields;
    /** A list of classes to be promoted to public visibility */
    private final Set<String> mPromotedClasses;

    /**
     * Creates a new generator that can generate the output JAR with the stubbed classes.
@@ -179,6 +182,9 @@ public class AsmGenerator {
        addToMap(createInfo.getPromotedFields(), mPromotedFields);

        mInjectedMethodsMap = createInfo.getInjectedMethodsMap();

        mPromotedClasses =
                Arrays.stream(createInfo.getPromotedClasses()).collect(Collectors.toSet());
    }

    /**
@@ -400,7 +406,11 @@ public class AsmGenerator {
        if (promoteFields != null && !promoteFields.isEmpty()) {
            cv = new PromoteFieldClassAdapter(cv, promoteFields);
        }
        if (!mPromotedClasses.isEmpty()) {
            cv = new PromoteClassClassAdapter(cv, mPromotedClasses);
        }
        cr.accept(cv, 0);

        return cw.toByteArray();
    }

+12 −0
Original line number Diff line number Diff line
@@ -112,6 +112,11 @@ public final class CreateInfo implements ICreateInfo {
        return PROMOTED_FIELDS;
    }

    @Override
    public String[] getPromotedClasses() {
        return PROMOTED_CLASSES;
    }

    @Override
    public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
        return INJECTED_METHODS;
@@ -343,6 +348,13 @@ public final class CreateInfo implements ICreateInfo {
        "android.graphics.FontFamily#mBuilderPtr"
    };

    /**
     * List of classes to be promoted to public visibility. Prefer using PROMOTED_FIELDS to this
     * if possible.
     */
    private final static String[] PROMOTED_CLASSES = new String[] {
    };

    /**
     * List of classes for which the methods returning them should be deleted.
     * The array contains a list of null terminated section starting with the name of the class
+5 −0
Original line number Diff line number Diff line
@@ -77,6 +77,11 @@ public interface ICreateInfo {
     */
    String[] getPromotedFields();

    /**
     * Returns a list of classes to be promoted to public visibility.
     */
    String[] getPromotedClasses();

    /**
     * Returns a map from binary FQCN className to {@link InjectMethodRunnable} which will be
     * called to inject methods into a class.
+64 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.tools.layoutlib.create;

import org.objectweb.asm.ClassVisitor;

import java.util.Set;
import java.util.stream.Collectors;

import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;

/**
 * Promotes given classes to public visibility.
 */
public class PromoteClassClassAdapter extends ClassVisitor {

    private final Set<String> mClassNames;
    private static final int CLEAR_PRIVATE_MASK = ~(ACC_PRIVATE | ACC_PROTECTED);

    public PromoteClassClassAdapter(ClassVisitor cv, Set<String> classNames) {
        super(Main.ASM_VERSION, cv);
        mClassNames =
                classNames.stream().map(name -> name.replace(".", "/")).collect(Collectors.toSet());
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        if (mClassNames.contains(name)) {
            if ((access & ACC_PUBLIC) == 0) {
                access = (access & CLEAR_PRIVATE_MASK) | ACC_PUBLIC;
            }
        }

        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
        if (mClassNames.contains(name)) {
            if ((access & ACC_PUBLIC) == 0) {
                access = (access & CLEAR_PRIVATE_MASK) | ACC_PUBLIC;
            }
        }

        super.visitInnerClass(name, outerName, innerName, access);
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
public class PromoteFieldClassAdapter extends ClassVisitor {

    private final Set<String> mFieldNames;
    private static final int ACC_NOT_PUBLIC = ~(ACC_PRIVATE | ACC_PROTECTED);
    private static final int CLEAR_PRIVATE_MASK = ~(ACC_PRIVATE | ACC_PROTECTED);

    public PromoteFieldClassAdapter(ClassVisitor cv, Set<String> fieldNames) {
        super(Main.ASM_VERSION, cv);
@@ -43,7 +43,7 @@ public class PromoteFieldClassAdapter extends ClassVisitor {
            Object value) {
        if (mFieldNames.contains(name)) {
            if ((access & ACC_PUBLIC) == 0) {
                access = (access & ACC_NOT_PUBLIC) | ACC_PUBLIC;
                access = (access & CLEAR_PRIVATE_MASK) | ACC_PUBLIC;
            }
        }
        return super.visitField(access, name, desc, signature, value);
Loading