From cbb1ba7f493116882b79578ec68646366f29cde4 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Tue, 1 Dec 2015 21:44:56 -0800 Subject: [PATCH] Add ability to promote fields. This adds the ability to promote private fields to public ones to enable layoutlib to access them. This was first added in change b556decf75b2b084e1aed54ac7fa23a141eedb7f, but reverted in 847b0d3ad22a47e0bca3d8bc8168fea7a0ba2f80. This is essentially a revert of the change again, but without the SimpleMonthView related stuff. Also, promote the Choreographer's field as is needed to support animations. Change-Id: Iee45a465c7a1cadd362c1841385941e6232e4900 --- .../tools/layoutlib/create/AsmGenerator.java | 46 +++++++++++----- .../tools/layoutlib/create/CreateInfo.java | 9 ++-- .../tools/layoutlib/create/ICreateInfo.java | 7 +++ .../create/PromoteFieldClassAdapter.java | 52 +++++++++++++++++++ .../layoutlib/create/AsmGeneratorTest.java | 20 +++++++ 5 files changed, 117 insertions(+), 17 deletions(-) create mode 100644 tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index f6c2626e42713..8f0ad01c6dc3a 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -77,6 +77,8 @@ public class AsmGenerator { /** Methods to inject. FQCN of class in which method should be injected => runnable that does * the injection. */ private final Map mInjectedMethodsMap; + /** A map { FQCN => set { field names } } which should be promoted to public visibility */ + private final Map> mPromotedFields; /** * Creates a new generator that can generate the output JAR with the stubbed classes. @@ -109,20 +111,8 @@ public class AsmGenerator { // Create the map/set of methods to change to delegates mDelegateMethods = new HashMap>(); - for (String signature : createInfo.getDelegateMethods()) { - int pos = signature.indexOf('#'); - if (pos <= 0 || pos >= signature.length() - 1) { - continue; - } - String className = binaryToInternalClassName(signature.substring(0, pos)); - String methodName = signature.substring(pos + 1); - Set methods = mDelegateMethods.get(className); - if (methods == null) { - methods = new HashSet(); - mDelegateMethods.put(className, methods); - } - methods.add(methodName); - } + addToMap(createInfo.getDelegateMethods(), mDelegateMethods); + for (String className : createInfo.getDelegateClassNatives()) { className = binaryToInternalClassName(className); Set methods = mDelegateMethods.get(className); @@ -187,9 +177,33 @@ public class AsmGenerator { returnTypes.add(binaryToInternalClassName(className)); } + mPromotedFields = new HashMap>(); + addToMap(createInfo.getPromotedFields(), mPromotedFields); + mInjectedMethodsMap = createInfo.getInjectedMethodsMap(); } + /** + * For each value in the array, split the value on '#' and add the parts to the map as key + * and value. + */ + private void addToMap(String[] entries, Map> map) { + for (String entry : entries) { + int pos = entry.indexOf('#'); + if (pos <= 0 || pos >= entry.length() - 1) { + return; + } + String className = binaryToInternalClassName(entry.substring(0, pos)); + String methodOrFieldName = entry.substring(pos + 1); + Set set = map.get(className); + if (set == null) { + set = new HashSet(); + map.put(className, set); + } + set.add(methodOrFieldName); + } + } + /** * Returns the list of classes that have not been renamed yet. *

@@ -380,6 +394,10 @@ public class AsmGenerator { } } + Set promoteFields = mPromotedFields.get(className); + if (promoteFields != null && !promoteFields.isEmpty()) { + cv = new PromoteFieldClassAdapter(cv, promoteFields); + } cr.accept(cv, 0); return cw.toByteArray(); } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 7879d9d5417c0..ef2addd3d2c32 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -119,6 +119,11 @@ public final class CreateInfo implements ICreateInfo { return excludedClasses; } + @Override + public String[] getPromotedFields() { + return PROMOTED_FIELDS; + } + @Override public Map getInjectedMethodsMap() { return INJECTED_METHODS; @@ -291,9 +296,7 @@ public final class CreateInfo implements ICreateInfo { }; private final static String[] PROMOTED_FIELDS = new String[] { - "android.widget.SimpleMonthView#mTitle", - "android.widget.SimpleMonthView#mCalendar", - "android.widget.SimpleMonthView#mDayOfWeekLabelCalendar" + "android.view.Choreographer#mLastFrameTimeNanos" }; /** diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java index 54b1fe6287698..6c62423a2a892 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java @@ -77,6 +77,13 @@ public interface ICreateInfo { Set getExcludedClasses(); + /** + * Returns a list of fields which should be promoted to public visibility. The array values + * are in the form of the binary FQCN of the class containing the field and the field name + * separated by a '#'. + */ + String[] getPromotedFields(); + /** * Returns a map from binary FQCN className to {@link InjectMethodRunnable} which will be * called to inject methods into a class. diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java new file mode 100644 index 0000000000000..e4b70da2504f7 --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 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 org.objectweb.asm.FieldVisitor; + +import java.util.Set; + +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_PROTECTED; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ASM4; + +/** + * Promotes given fields to public visibility. + */ +public class PromoteFieldClassAdapter extends ClassVisitor { + + private final Set mFieldNames; + private static final int ACC_NOT_PUBLIC = ~(ACC_PRIVATE | ACC_PROTECTED); + + public PromoteFieldClassAdapter(ClassVisitor cv, Set fieldNames) { + super(ASM4, cv); + mFieldNames = fieldNames; + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, + Object value) { + if (mFieldNames.contains(name)) { + if ((access & ACC_PUBLIC) == 0) { + access = (access & ACC_NOT_PUBLIC) | ACC_PUBLIC; + } + } + return super.visitField(access, name, desc, signature, value); + } +} diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java index 2c21470d6a2fb..8a2235b8526c8 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java @@ -137,6 +137,11 @@ public class AsmGeneratorTest { return new String[0]; } + @Override + public String[] getPromotedFields() { + return new String[0]; + } + @Override public Map getInjectedMethodsMap() { return new HashMap(0); @@ -212,6 +217,11 @@ public class AsmGeneratorTest { return new String[0]; } + @Override + public String[] getPromotedFields() { + return new String[0]; + } + @Override public Map getInjectedMethodsMap() { return new HashMap(0); @@ -295,6 +305,11 @@ public class AsmGeneratorTest { return new String[0]; } + @Override + public String[] getPromotedFields() { + return new String[0]; + } + @Override public Map getInjectedMethodsMap() { return new HashMap(0); @@ -373,6 +388,11 @@ public class AsmGeneratorTest { return new String[0]; } + @Override + public String[] getPromotedFields() { + return new String[0]; + } + @Override public Map getInjectedMethodsMap() { HashMap map =