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
b556decf75, but reverted in
847b0d3ad2. 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
This commit is contained in:
Deepanshu Gupta
2015-12-01 21:44:56 -08:00
parent 9c165d7601
commit cbb1ba7f49
5 changed files with 117 additions and 17 deletions

View File

@@ -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<String, ICreateInfo.InjectMethodRunnable> mInjectedMethodsMap;
/** A map { FQCN => set { field names } } which should be promoted to public visibility */
private final Map<String, Set<String>> 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<String, Set<String>>();
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<String> methods = mDelegateMethods.get(className);
if (methods == null) {
methods = new HashSet<String>();
mDelegateMethods.put(className, methods);
}
methods.add(methodName);
}
addToMap(createInfo.getDelegateMethods(), mDelegateMethods);
for (String className : createInfo.getDelegateClassNatives()) {
className = binaryToInternalClassName(className);
Set<String> methods = mDelegateMethods.get(className);
@@ -187,9 +177,33 @@ public class AsmGenerator {
returnTypes.add(binaryToInternalClassName(className));
}
mPromotedFields = new HashMap<String, Set<String>>();
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<String, Set<String>> 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<String> set = map.get(className);
if (set == null) {
set = new HashSet<String>();
map.put(className, set);
}
set.add(methodOrFieldName);
}
}
/**
* Returns the list of classes that have not been renamed yet.
* <p/>
@@ -380,6 +394,10 @@ public class AsmGenerator {
}
}
Set<String> promoteFields = mPromotedFields.get(className);
if (promoteFields != null && !promoteFields.isEmpty()) {
cv = new PromoteFieldClassAdapter(cv, promoteFields);
}
cr.accept(cv, 0);
return cw.toByteArray();
}

View File

@@ -119,6 +119,11 @@ public final class CreateInfo implements ICreateInfo {
return excludedClasses;
}
@Override
public String[] getPromotedFields() {
return PROMOTED_FIELDS;
}
@Override
public Map<String, InjectMethodRunnable> 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"
};
/**

View File

@@ -77,6 +77,13 @@ public interface ICreateInfo {
Set<String> 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.

View File

@@ -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<String> mFieldNames;
private static final int ACC_NOT_PUBLIC = ~(ACC_PRIVATE | ACC_PROTECTED);
public PromoteFieldClassAdapter(ClassVisitor cv, Set<String> 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);
}
}

View File

@@ -137,6 +137,11 @@ public class AsmGeneratorTest {
return new String[0];
}
@Override
public String[] getPromotedFields() {
return new String[0];
}
@Override
public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
return new HashMap<String, InjectMethodRunnable>(0);
@@ -212,6 +217,11 @@ public class AsmGeneratorTest {
return new String[0];
}
@Override
public String[] getPromotedFields() {
return new String[0];
}
@Override
public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
return new HashMap<String, InjectMethodRunnable>(0);
@@ -295,6 +305,11 @@ public class AsmGeneratorTest {
return new String[0];
}
@Override
public String[] getPromotedFields() {
return new String[0];
}
@Override
public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
return new HashMap<String, InjectMethodRunnable>(0);
@@ -373,6 +388,11 @@ public class AsmGeneratorTest {
return new String[0];
}
@Override
public String[] getPromotedFields() {
return new String[0];
}
@Override
public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
HashMap<String, InjectMethodRunnable> map =