summaryrefslogtreecommitdiff
path: root/dexgen/src/com/android/dexgen/dex/file/AnnotationUtils.java
blob: 111ba8ad4338b7efe2df7d99d251aa9f53009ebb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/*
 * 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.dexgen.dex.file;

import com.android.dexgen.rop.annotation.Annotation;
import com.android.dexgen.rop.annotation.NameValuePair;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstAnnotation;
import com.android.dexgen.rop.cst.CstArray;
import com.android.dexgen.rop.cst.CstInteger;
import com.android.dexgen.rop.cst.CstKnownNull;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstString;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.rop.type.TypeList;

import java.util.ArrayList;

import static com.android.dexgen.rop.annotation.AnnotationVisibility.*;

/**
 * Utility class for dealing with annotations.
 */
public final class AnnotationUtils {
    /** {@code non-null;} type for {@code AnnotationDefault} annotations */
    private static final CstType ANNOTATION_DEFAULT_TYPE =
        CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;"));

    /** {@code non-null;} type for {@code EnclosingClass} annotations */
    private static final CstType ENCLOSING_CLASS_TYPE =
        CstType.intern(Type.intern("Ldalvik/annotation/EnclosingClass;"));

    /** {@code non-null;} type for {@code EnclosingMethod} annotations */
    private static final CstType ENCLOSING_METHOD_TYPE =
        CstType.intern(Type.intern("Ldalvik/annotation/EnclosingMethod;"));

    /** {@code non-null;} type for {@code InnerClass} annotations */
    private static final CstType INNER_CLASS_TYPE =
        CstType.intern(Type.intern("Ldalvik/annotation/InnerClass;"));

    /** {@code non-null;} type for {@code MemberClasses} annotations */
    private static final CstType MEMBER_CLASSES_TYPE =
        CstType.intern(Type.intern("Ldalvik/annotation/MemberClasses;"));

    /** {@code non-null;} type for {@code Signature} annotations */
    private static final CstType SIGNATURE_TYPE =
        CstType.intern(Type.intern("Ldalvik/annotation/Signature;"));

    /** {@code non-null;} type for {@code Throws} annotations */
    private static final CstType THROWS_TYPE =
        CstType.intern(Type.intern("Ldalvik/annotation/Throws;"));

    /** {@code non-null;} the UTF-8 constant {@code "accessFlags"} */
    private static final CstUtf8 ACCESS_FLAGS_UTF = new CstUtf8("accessFlags");

    /** {@code non-null;} the UTF-8 constant {@code "name"} */
    private static final CstUtf8 NAME_UTF = new CstUtf8("name");

    /** {@code non-null;} the UTF-8 constant {@code "value"} */
    private static final CstUtf8 VALUE_UTF = new CstUtf8("value");

    /**
     * This class is uninstantiable.
     */
    private AnnotationUtils() {
        // This space intentionally left blank.
    }

    /**
     * Constructs a standard {@code AnnotationDefault} annotation.
     *
     * @param defaults {@code non-null;} the defaults, itself as an annotation
     * @return {@code non-null;} the constructed annotation
     */
    public static Annotation makeAnnotationDefault(Annotation defaults) {
        Annotation result = new Annotation(ANNOTATION_DEFAULT_TYPE, SYSTEM);

        result.put(new NameValuePair(VALUE_UTF, new CstAnnotation(defaults)));
        result.setImmutable();
        return result;
    }

    /**
     * Constructs a standard {@code EnclosingClass} annotation.
     *
     * @param clazz {@code non-null;} the enclosing class
     * @return {@code non-null;} the annotation
     */
    public static Annotation makeEnclosingClass(CstType clazz) {
        Annotation result = new Annotation(ENCLOSING_CLASS_TYPE, SYSTEM);

        result.put(new NameValuePair(VALUE_UTF, clazz));
        result.setImmutable();
        return result;
    }

    /**
     * Constructs a standard {@code EnclosingMethod} annotation.
     *
     * @param method {@code non-null;} the enclosing method
     * @return {@code non-null;} the annotation
     */
    public static Annotation makeEnclosingMethod(CstMethodRef method) {
        Annotation result = new Annotation(ENCLOSING_METHOD_TYPE, SYSTEM);

        result.put(new NameValuePair(VALUE_UTF, method));
        result.setImmutable();
        return result;
    }

    /**
     * Constructs a standard {@code InnerClass} annotation.
     *
     * @param name {@code null-ok;} the original name of the class, or
     * {@code null} to represent an anonymous class
     * @param accessFlags the original access flags
     * @return {@code non-null;} the annotation
     */
    public static Annotation makeInnerClass(CstUtf8 name, int accessFlags) {
        Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM);
        Constant nameCst =
            (name != null) ? new CstString(name) : CstKnownNull.THE_ONE;

        result.put(new NameValuePair(NAME_UTF, nameCst));
        result.put(new NameValuePair(ACCESS_FLAGS_UTF,
                        CstInteger.make(accessFlags)));
        result.setImmutable();
        return result;
    }

    /**
     * Constructs a standard {@code MemberClasses} annotation.
     *
     * @param types {@code non-null;} the list of (the types of) the member classes
     * @return {@code non-null;} the annotation
     */
    public static Annotation makeMemberClasses(TypeList types) {
        CstArray array = makeCstArray(types);
        Annotation result = new Annotation(MEMBER_CLASSES_TYPE, SYSTEM);
        result.put(new NameValuePair(VALUE_UTF, array));
        result.setImmutable();
        return result;
    }

    /**
     * Constructs a standard {@code Signature} annotation.
     *
     * @param signature {@code non-null;} the signature string
     * @return {@code non-null;} the annotation
     */
    public static Annotation makeSignature(CstUtf8 signature) {
        Annotation result = new Annotation(SIGNATURE_TYPE, SYSTEM);

        /*
         * Split the string into pieces that are likely to be common
         * across many signatures and the rest of the file.
         */

        String raw = signature.getString();
        int rawLength = raw.length();
        ArrayList<String> pieces = new ArrayList<String>(20);

        for (int at = 0; at < rawLength; /*at*/) {
            char c = raw.charAt(at);
            int endAt = at + 1;
            if (c == 'L') {
                // Scan to ';' or '<'. Consume ';' but not '<'.
                while (endAt < rawLength) {
                    c = raw.charAt(endAt);
                    if (c == ';') {
                        endAt++;
                        break;
                    } else if (c == '<') {
                        break;
                    }
                    endAt++;
                }
            } else {
                // Scan to 'L' without consuming it.
                while (endAt < rawLength) {
                    c = raw.charAt(endAt);
                    if (c == 'L') {
                        break;
                    }
                    endAt++;
                }
            }

            pieces.add(raw.substring(at, endAt));
            at = endAt;
        }

        int size = pieces.size();
        CstArray.List list = new CstArray.List(size);

        for (int i = 0; i < size; i++) {
            list.set(i, new CstString(pieces.get(i)));
        }

        list.setImmutable();

        result.put(new NameValuePair(VALUE_UTF, new CstArray(list)));
        result.setImmutable();
        return result;
    }

    /**
     * Constructs a standard {@code Throws} annotation.
     *
     * @param types {@code non-null;} the list of thrown types
     * @return {@code non-null;} the annotation
     */
    public static Annotation makeThrows(TypeList types) {
        CstArray array = makeCstArray(types);
        Annotation result = new Annotation(THROWS_TYPE, SYSTEM);
        result.put(new NameValuePair(VALUE_UTF, array));
        result.setImmutable();
        return result;
    }

    /**
     * Converts a {@link TypeList} to a {@link CstArray}.
     *
     * @param types {@code non-null;} the type list
     * @return {@code non-null;} the corresponding array constant
     */
    private static CstArray makeCstArray(TypeList types) {
        int size = types.size();
        CstArray.List list = new CstArray.List(size);

        for (int i = 0; i < size; i++) {
            list.set(i, CstType.intern(types.getType(i)));
        }

        list.setImmutable();
        return new CstArray(list);
    }
}