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