| /* |
| * Copyright (C) 2008 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.ClassAdapter; |
| import org.objectweb.asm.ClassVisitor; |
| import org.objectweb.asm.FieldVisitor; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.Type; |
| |
| import java.util.Set; |
| |
| /** |
| * Class adapter that can stub some or all of the methods of the class. |
| */ |
| class TransformClassAdapter extends ClassAdapter { |
| |
| /** True if all methods should be stubbed, false if only native ones must be stubbed. */ |
| private final boolean mStubAll; |
| /** True if the class is an interface. */ |
| private boolean mIsInterface; |
| private final String mClassName; |
| private final Log mLog; |
| private final Set<String> mStubMethods; |
| private Set<String> mDeleteReturns; |
| |
| /** |
| * Creates a new class adapter that will stub some or all methods. |
| * @param logger |
| * @param stubMethods list of method signatures to always stub out |
| * @param deleteReturns list of types that trigger the deletion of methods returning them. |
| * @param className The name of the class being modified |
| * @param cv The parent class writer visitor |
| * @param stubNativesOnly True if only native methods should be stubbed. False if all |
| * methods should be stubbed. |
| * @param hasNative True if the method has natives, in which case its access should be |
| * changed. |
| */ |
| public TransformClassAdapter(Log logger, Set<String> stubMethods, |
| Set<String> deleteReturns, String className, ClassVisitor cv, |
| boolean stubNativesOnly, boolean hasNative) { |
| super(cv); |
| mLog = logger; |
| mStubMethods = stubMethods; |
| mClassName = className; |
| mStubAll = !stubNativesOnly; |
| mIsInterface = false; |
| mDeleteReturns = deleteReturns; |
| } |
| |
| /* Visits the class header. */ |
| @Override |
| public void visit(int version, int access, String name, |
| String signature, String superName, String[] interfaces) { |
| |
| // This class might be being renamed. |
| name = mClassName; |
| |
| // remove protected or private and set as public |
| access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED); |
| access |= Opcodes.ACC_PUBLIC; |
| // remove final |
| access = access & ~Opcodes.ACC_FINAL; |
| // note: leave abstract classes as such |
| // don't try to implement stub for interfaces |
| |
| mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0); |
| super.visit(version, access, name, signature, superName, interfaces); |
| } |
| |
| /* Visits the header of an inner class. */ |
| @Override |
| public void visitInnerClass(String name, String outerName, String innerName, int access) { |
| // remove protected or private and set as public |
| access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED); |
| access |= Opcodes.ACC_PUBLIC; |
| // remove final |
| access = access & ~Opcodes.ACC_FINAL; |
| // note: leave abstract classes as such |
| // don't try to implement stub for interfaces |
| |
| super.visitInnerClass(name, outerName, innerName, access); |
| } |
| |
| /* Visits a method. */ |
| @Override |
| public MethodVisitor visitMethod(int access, String name, String desc, |
| String signature, String[] exceptions) { |
| |
| if (mDeleteReturns != null) { |
| Type t = Type.getReturnType(desc); |
| if (t.getSort() == Type.OBJECT) { |
| String returnType = t.getInternalName(); |
| if (returnType != null) { |
| if (mDeleteReturns.contains(returnType)) { |
| return null; |
| } |
| } |
| } |
| } |
| |
| String methodSignature = mClassName.replace('/', '.') + "#" + name; |
| |
| // change access to public |
| access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE); |
| access |= Opcodes.ACC_PUBLIC; |
| |
| // remove final |
| access = access & ~Opcodes.ACC_FINAL; |
| |
| // stub this method if they are all to be stubbed or if it is a native method |
| // and don't try to stub interfaces nor abstract non-native methods. |
| if (!mIsInterface && |
| ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != Opcodes.ACC_ABSTRACT) && |
| (mStubAll || |
| (access & Opcodes.ACC_NATIVE) != 0) || |
| mStubMethods.contains(methodSignature)) { |
| |
| boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; |
| boolean isNative = (access & Opcodes.ACC_NATIVE) != 0; |
| |
| // remove abstract, final and native |
| access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE); |
| |
| String invokeSignature = methodSignature + desc; |
| mLog.debug(" Stub: %s (%s)", invokeSignature, isNative ? "native" : ""); |
| |
| MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions); |
| return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature, |
| isStatic, isNative); |
| |
| } else { |
| mLog.debug(" Keep: %s %s", name, desc); |
| return super.visitMethod(access, name, desc, signature, exceptions); |
| } |
| } |
| |
| /* Visits a field. Makes it public. */ |
| @Override |
| public FieldVisitor visitField(int access, String name, String desc, String signature, |
| Object value) { |
| // change access to public |
| access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE); |
| access |= Opcodes.ACC_PUBLIC; |
| |
| return super.visitField(access, name, desc, signature, value); |
| } |
| |
| /** |
| * Extracts the return {@link Type} of this descriptor. |
| */ |
| Type returnType(String desc) { |
| if (desc != null) { |
| try { |
| return Type.getReturnType(desc); |
| } catch (ArrayIndexOutOfBoundsException e) { |
| // ignore, not a valid type. |
| } |
| } |
| return null; |
| } |
| } |