• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dx.dex.file;
18 
19 import com.android.dex.EncodedValueCodec;
20 import com.android.dx.rop.annotation.Annotation;
21 import com.android.dx.rop.annotation.NameValuePair;
22 import com.android.dx.rop.cst.Constant;
23 import com.android.dx.rop.cst.CstAnnotation;
24 import com.android.dx.rop.cst.CstArray;
25 import com.android.dx.rop.cst.CstBoolean;
26 import com.android.dx.rop.cst.CstByte;
27 import com.android.dx.rop.cst.CstChar;
28 import com.android.dx.rop.cst.CstDouble;
29 import com.android.dx.rop.cst.CstEnumRef;
30 import com.android.dx.rop.cst.CstFieldRef;
31 import com.android.dx.rop.cst.CstFloat;
32 import com.android.dx.rop.cst.CstInteger;
33 import com.android.dx.rop.cst.CstKnownNull;
34 import com.android.dx.rop.cst.CstLiteralBits;
35 import com.android.dx.rop.cst.CstLong;
36 import com.android.dx.rop.cst.CstMethodRef;
37 import com.android.dx.rop.cst.CstShort;
38 import com.android.dx.rop.cst.CstString;
39 import com.android.dx.rop.cst.CstType;
40 import com.android.dx.util.AnnotatedOutput;
41 import com.android.dx.util.Hex;
42 import java.util.Collection;
43 
44 /**
45  * Handler for writing out {@code encoded_values} and parts
46  * thereof.
47  */
48 public final class ValueEncoder {
49     /** annotation value type constant: {@code byte} */
50     private static final int VALUE_BYTE = 0x00;
51 
52     /** annotation value type constant: {@code short} */
53     private static final int VALUE_SHORT = 0x02;
54 
55     /** annotation value type constant: {@code char} */
56     private static final int VALUE_CHAR = 0x03;
57 
58     /** annotation value type constant: {@code int} */
59     private static final int VALUE_INT = 0x04;
60 
61     /** annotation value type constant: {@code long} */
62     private static final int VALUE_LONG = 0x06;
63 
64     /** annotation value type constant: {@code float} */
65     private static final int VALUE_FLOAT = 0x10;
66 
67     /** annotation value type constant: {@code double} */
68     private static final int VALUE_DOUBLE = 0x11;
69 
70     /** annotation value type constant: {@code string} */
71     private static final int VALUE_STRING = 0x17;
72 
73     /** annotation value type constant: {@code type} */
74     private static final int VALUE_TYPE = 0x18;
75 
76     /** annotation value type constant: {@code field} */
77     private static final int VALUE_FIELD = 0x19;
78 
79     /** annotation value type constant: {@code method} */
80     private static final int VALUE_METHOD = 0x1a;
81 
82     /** annotation value type constant: {@code enum} */
83     private static final int VALUE_ENUM = 0x1b;
84 
85     /** annotation value type constant: {@code array} */
86     private static final int VALUE_ARRAY = 0x1c;
87 
88     /** annotation value type constant: {@code annotation} */
89     private static final int VALUE_ANNOTATION = 0x1d;
90 
91     /** annotation value type constant: {@code null} */
92     private static final int VALUE_NULL = 0x1e;
93 
94     /** annotation value type constant: {@code boolean} */
95     private static final int VALUE_BOOLEAN = 0x1f;
96 
97     /** {@code non-null;} file being written */
98     private final DexFile file;
99 
100     /** {@code non-null;} output stream to write to */
101     private final AnnotatedOutput out;
102 
103     /**
104      * Construct an instance.
105      *
106      * @param file {@code non-null;} file being written
107      * @param out {@code non-null;} output stream to write to
108      */
ValueEncoder(DexFile file, AnnotatedOutput out)109     public ValueEncoder(DexFile file, AnnotatedOutput out) {
110         if (file == null) {
111             throw new NullPointerException("file == null");
112         }
113 
114         if (out == null) {
115             throw new NullPointerException("out == null");
116         }
117 
118         this.file = file;
119         this.out = out;
120     }
121 
122     /**
123      * Writes out the encoded form of the given constant.
124      *
125      * @param cst {@code non-null;} the constant to write
126      */
writeConstant(Constant cst)127     public void writeConstant(Constant cst) {
128         int type = constantToValueType(cst);
129         int arg;
130 
131         switch (type) {
132             case VALUE_BYTE:
133             case VALUE_SHORT:
134             case VALUE_INT:
135             case VALUE_LONG: {
136                 long value = ((CstLiteralBits) cst).getLongBits();
137                 EncodedValueCodec.writeSignedIntegralValue(out, type, value);
138                 break;
139             }
140             case VALUE_CHAR: {
141                 long value = ((CstLiteralBits) cst).getLongBits();
142                 EncodedValueCodec.writeUnsignedIntegralValue(out, type, value);
143                 break;
144             }
145             case VALUE_FLOAT: {
146                 // Shift value left 32 so that right-zero-extension works.
147                 long value = ((CstFloat) cst).getLongBits() << 32;
148                 EncodedValueCodec.writeRightZeroExtendedValue(out, type, value);
149                 break;
150             }
151             case VALUE_DOUBLE: {
152                 long value = ((CstDouble) cst).getLongBits();
153                 EncodedValueCodec.writeRightZeroExtendedValue(out, type, value);
154                 break;
155             }
156             case VALUE_STRING: {
157                 int index = file.getStringIds().indexOf((CstString) cst);
158                 EncodedValueCodec.writeUnsignedIntegralValue(out, type, (long) index);
159                 break;
160             }
161             case VALUE_TYPE: {
162                 int index = file.getTypeIds().indexOf((CstType) cst);
163                 EncodedValueCodec.writeUnsignedIntegralValue(out, type, (long) index);
164                 break;
165             }
166             case VALUE_FIELD: {
167                 int index = file.getFieldIds().indexOf((CstFieldRef) cst);
168                 EncodedValueCodec.writeUnsignedIntegralValue(out, type, (long) index);
169                 break;
170             }
171             case VALUE_METHOD: {
172                 int index = file.getMethodIds().indexOf((CstMethodRef) cst);
173                 EncodedValueCodec.writeUnsignedIntegralValue(out, type, (long) index);
174                 break;
175             }
176             case VALUE_ENUM: {
177                 CstFieldRef fieldRef = ((CstEnumRef) cst).getFieldRef();
178                 int index = file.getFieldIds().indexOf(fieldRef);
179                 EncodedValueCodec.writeUnsignedIntegralValue(out, type, (long) index);
180                 break;
181             }
182             case VALUE_ARRAY: {
183                 out.writeByte(type);
184                 writeArray((CstArray) cst, false);
185                 break;
186             }
187             case VALUE_ANNOTATION: {
188                 out.writeByte(type);
189                 writeAnnotation(((CstAnnotation) cst).getAnnotation(),
190                         false);
191                 break;
192             }
193             case VALUE_NULL: {
194                 out.writeByte(type);
195                 break;
196             }
197             case VALUE_BOOLEAN: {
198                 int value = ((CstBoolean) cst).getIntBits();
199                 out.writeByte(type | (value << 5));
200                 break;
201             }
202             default: {
203                 throw new RuntimeException("Shouldn't happen");
204             }
205         }
206     }
207 
208     /**
209      * Gets the value type for the given constant.
210      *
211      * @param cst {@code non-null;} the constant
212      * @return the value type; one of the {@code VALUE_*} constants
213      * defined by this class
214      */
constantToValueType(Constant cst)215     private static int constantToValueType(Constant cst) {
216         /*
217          * TODO: Constant should probable have an associated enum, so this
218          * can be a switch().
219          */
220         if (cst instanceof CstByte) {
221             return VALUE_BYTE;
222         } else if (cst instanceof CstShort) {
223             return VALUE_SHORT;
224         } else if (cst instanceof CstChar) {
225             return VALUE_CHAR;
226         } else if (cst instanceof CstInteger) {
227             return VALUE_INT;
228         } else if (cst instanceof CstLong) {
229             return VALUE_LONG;
230         } else if (cst instanceof CstFloat) {
231             return VALUE_FLOAT;
232         } else if (cst instanceof CstDouble) {
233             return VALUE_DOUBLE;
234         } else if (cst instanceof CstString) {
235             return VALUE_STRING;
236         } else if (cst instanceof CstType) {
237             return VALUE_TYPE;
238         } else if (cst instanceof CstFieldRef) {
239             return VALUE_FIELD;
240         } else if (cst instanceof CstMethodRef) {
241             return VALUE_METHOD;
242         } else if (cst instanceof CstEnumRef) {
243             return VALUE_ENUM;
244         } else if (cst instanceof CstArray) {
245             return VALUE_ARRAY;
246         } else if (cst instanceof CstAnnotation) {
247             return VALUE_ANNOTATION;
248         } else if (cst instanceof CstKnownNull) {
249             return VALUE_NULL;
250         } else if (cst instanceof CstBoolean) {
251             return VALUE_BOOLEAN;
252         } else {
253             throw new RuntimeException("Shouldn't happen");
254         }
255     }
256 
257     /**
258      * Writes out the encoded form of the given array, that is, as
259      * an {@code encoded_array} and not including a
260      * {@code value_type} prefix. If the output stream keeps
261      * (debugging) annotations and {@code topLevel} is
262      * {@code true}, then this method will write (debugging)
263      * annotations.
264      *
265      * @param array {@code non-null;} array instance to write
266      * @param topLevel {@code true} iff the given annotation is the
267      * top-level annotation or {@code false} if it is a sub-annotation
268      * of some other annotation
269      */
writeArray(CstArray array, boolean topLevel)270     public void writeArray(CstArray array, boolean topLevel) {
271         boolean annotates = topLevel && out.annotates();
272         CstArray.List list = ((CstArray) array).getList();
273         int size = list.size();
274 
275         if (annotates) {
276             out.annotate("  size: " + Hex.u4(size));
277         }
278 
279         out.writeUleb128(size);
280 
281         for (int i = 0; i < size; i++) {
282             Constant cst = list.get(i);
283             if (annotates) {
284                 out.annotate("  [" + Integer.toHexString(i) + "] " +
285                         constantToHuman(cst));
286             }
287             writeConstant(cst);
288         }
289 
290         if (annotates) {
291             out.endAnnotation();
292         }
293     }
294 
295     /**
296      * Writes out the encoded form of the given annotation, that is,
297      * as an {@code encoded_annotation} and not including a
298      * {@code value_type} prefix. If the output stream keeps
299      * (debugging) annotations and {@code topLevel} is
300      * {@code true}, then this method will write (debugging)
301      * annotations.
302      *
303      * @param annotation {@code non-null;} annotation instance to write
304      * @param topLevel {@code true} iff the given annotation is the
305      * top-level annotation or {@code false} if it is a sub-annotation
306      * of some other annotation
307      */
writeAnnotation(Annotation annotation, boolean topLevel)308     public void writeAnnotation(Annotation annotation, boolean topLevel) {
309         boolean annotates = topLevel && out.annotates();
310         StringIdsSection stringIds = file.getStringIds();
311         TypeIdsSection typeIds = file.getTypeIds();
312 
313         CstType type = annotation.getType();
314         int typeIdx = typeIds.indexOf(type);
315 
316         if (annotates) {
317             out.annotate("  type_idx: " + Hex.u4(typeIdx) + " // " +
318                     type.toHuman());
319         }
320 
321         out.writeUleb128(typeIds.indexOf(annotation.getType()));
322 
323         Collection<NameValuePair> pairs = annotation.getNameValuePairs();
324         int size = pairs.size();
325 
326         if (annotates) {
327             out.annotate("  size: " + Hex.u4(size));
328         }
329 
330         out.writeUleb128(size);
331 
332         int at = 0;
333         for (NameValuePair pair : pairs) {
334             CstString name = pair.getName();
335             int nameIdx = stringIds.indexOf(name);
336             Constant value = pair.getValue();
337 
338             if (annotates) {
339                 out.annotate(0, "  elements[" + at + "]:");
340                 at++;
341                 out.annotate("    name_idx: " + Hex.u4(nameIdx) + " // " +
342                         name.toHuman());
343             }
344 
345             out.writeUleb128(nameIdx);
346 
347             if (annotates) {
348                 out.annotate("    value: " + constantToHuman(value));
349             }
350 
351             writeConstant(value);
352         }
353 
354         if (annotates) {
355             out.endAnnotation();
356         }
357     }
358 
359     /**
360      * Gets the colloquial type name and human form of the type of the
361      * given constant, when used as an encoded value.
362      *
363      * @param cst {@code non-null;} the constant
364      * @return {@code non-null;} its type name and human form
365      */
constantToHuman(Constant cst)366     public static String constantToHuman(Constant cst) {
367         int type = constantToValueType(cst);
368 
369         if (type == VALUE_NULL) {
370             return "null";
371         }
372 
373         StringBuilder sb = new StringBuilder();
374 
375         sb.append(cst.typeName());
376         sb.append(' ');
377         sb.append(cst.toHuman());
378 
379         return sb.toString();
380     }
381 
382     /**
383      * Helper for {@code addContents()} methods, which adds
384      * contents for a particular {@link Annotation}, calling itself
385      * recursively should it encounter a nested annotation.
386      *
387      * @param file {@code non-null;} the file to add to
388      * @param annotation {@code non-null;} the annotation to add contents for
389      */
addContents(DexFile file, Annotation annotation)390     public static void addContents(DexFile file, Annotation annotation) {
391         TypeIdsSection typeIds = file.getTypeIds();
392         StringIdsSection stringIds = file.getStringIds();
393 
394         typeIds.intern(annotation.getType());
395 
396         for (NameValuePair pair : annotation.getNameValuePairs()) {
397             stringIds.intern(pair.getName());
398             addContents(file, pair.getValue());
399         }
400     }
401 
402     /**
403      * Helper for {@code addContents()} methods, which adds
404      * contents for a particular constant, calling itself recursively
405      * should it encounter a {@link CstArray} and calling {@link
406      * #addContents(DexFile,Annotation)} recursively should it
407      * encounter a {@link CstAnnotation}.
408      *
409      * @param file {@code non-null;} the file to add to
410      * @param cst {@code non-null;} the constant to add contents for
411      */
addContents(DexFile file, Constant cst)412     public static void addContents(DexFile file, Constant cst) {
413         if (cst instanceof CstAnnotation) {
414             addContents(file, ((CstAnnotation) cst).getAnnotation());
415         } else if (cst instanceof CstArray) {
416             CstArray.List list = ((CstArray) cst).getList();
417             int size = list.size();
418             for (int i = 0; i < size; i++) {
419                 addContents(file, list.get(i));
420             }
421         } else {
422             file.internIfAppropriate(cst);
423         }
424     }
425 }
426