1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/*
2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Copyright (C) 2008 The Android Open Source Project
3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * you may not use this file except in compliance with the License.
6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * You may obtain a copy of the License at
7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Unless required by applicable law or agreed to in writing, software
11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * See the License for the specific language governing permissions and
14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * limitations under the License.
15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipackage com.android.tools.layoutlib.create;
18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.objectweb.asm.ClassVisitor;
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.objectweb.asm.MethodVisitor;
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.objectweb.asm.Opcodes;
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.objectweb.asm.Type;
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
24282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.Set;
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/**
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Class adapter that can stub some or all of the methods of the class.
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiclass TransformClassAdapter extends ClassVisitor {
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /** True if all methods should be stubbed, false if only native ones must be stubbed. */
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final boolean mStubAll;
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /** True if the class is an interface. */
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private boolean mIsInterface;
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final String mClassName;
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final Log mLog;
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final Set<String> mStubMethods;
38282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private Set<String> mDeleteReturns;
39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Creates a new class adapter that will stub some or all methods.
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param stubMethods  list of method signatures to always stub out
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param deleteReturns list of types that trigger the deletion of methods returning them.
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param className The name of the class being modified
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param cv The parent class writer visitor
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param stubNativesOnly True if only native methods should be stubbed. False if all
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *                        methods should be stubbed.
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public TransformClassAdapter(Log logger, Set<String> stubMethods,
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Set<String> deleteReturns, String className, ClassVisitor cv,
51c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta            boolean stubNativesOnly) {
52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        super(Opcodes.ASM4, cv);
53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mLog = logger;
54282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mStubMethods = stubMethods;
55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mClassName = className;
56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mStubAll = !stubNativesOnly;
57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mIsInterface = false;
58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mDeleteReturns = deleteReturns;
59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /* Visits the class header. */
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    @Override
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void visit(int version, int access, String name,
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            String signature, String superName, String[] interfaces) {
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // This class might be being renamed.
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        name = mClassName;
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // remove final
70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        access = access & ~Opcodes.ACC_FINAL;
71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // note: leave abstract classes as such
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // don't try to implement stub for interfaces
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        super.visit(version, access, name, signature, superName, interfaces);
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /* Visits the header of an inner class. */
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    @Override
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void visitInnerClass(String name, String outerName, String innerName, int access) {
81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // remove final
82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        access = access & ~Opcodes.ACC_FINAL;
83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // note: leave abstract classes as such
84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // don't try to implement stub for interfaces
85282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
86282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        super.visitInnerClass(name, outerName, innerName, access);
87282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /* Visits a method. */
90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    @Override
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public MethodVisitor visitMethod(int access, String name, String desc,
92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            String signature, String[] exceptions) {
93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mDeleteReturns != null) {
95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Type t = Type.getReturnType(desc);
96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (t.getSort() == Type.OBJECT) {
97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                String returnType = t.getInternalName();
98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (returnType != null) {
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (mDeleteReturns.contains(returnType)) {
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        return null;
101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        String methodSignature = mClassName.replace('/', '.') + "#" + name;
107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // remove final
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        access = access & ~Opcodes.ACC_FINAL;
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // stub this method if they are all to be stubbed or if it is a native method
112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // and don't try to stub interfaces nor abstract non-native methods.
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (!mIsInterface &&
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != Opcodes.ACC_ABSTRACT) &&
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (mStubAll ||
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski             (access & Opcodes.ACC_NATIVE) != 0) ||
117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski             mStubMethods.contains(methodSignature)) {
118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // remove abstract, final and native
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE);
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            String invokeSignature = methodSignature + desc;
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mLog.debug("  Stub: %s (%s)", invokeSignature, isNative ? "native" : "");
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature,
130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    isStatic, isNative);
131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mLog.debug("  Keep: %s %s", name, desc);
134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return super.visitMethod(access, name, desc, signature, exceptions);
135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
139282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Extracts the return {@link Type} of this descriptor.
140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    Type returnType(String desc) {
142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (desc != null) {
143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            try {
144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return Type.getReturnType(desc);
145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } catch (ArrayIndexOutOfBoundsException e) {
146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // ignore, not a valid type.
147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return null;
150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
152