ValueEncoder.java revision 917cb222329ee8c035c3ffaf947e4265761b9367
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
17package com.android.dexgen.dex.file;
18
19import com.android.dexgen.rop.annotation.Annotation;
20import com.android.dexgen.rop.annotation.NameValuePair;
21import com.android.dexgen.rop.cst.Constant;
22import com.android.dexgen.rop.cst.CstAnnotation;
23import com.android.dexgen.rop.cst.CstArray;
24import com.android.dexgen.rop.cst.CstBoolean;
25import com.android.dexgen.rop.cst.CstByte;
26import com.android.dexgen.rop.cst.CstChar;
27import com.android.dexgen.rop.cst.CstDouble;
28import com.android.dexgen.rop.cst.CstEnumRef;
29import com.android.dexgen.rop.cst.CstFieldRef;
30import com.android.dexgen.rop.cst.CstFloat;
31import com.android.dexgen.rop.cst.CstInteger;
32import com.android.dexgen.rop.cst.CstKnownNull;
33import com.android.dexgen.rop.cst.CstLiteralBits;
34import com.android.dexgen.rop.cst.CstLong;
35import com.android.dexgen.rop.cst.CstMethodRef;
36import com.android.dexgen.rop.cst.CstShort;
37import com.android.dexgen.rop.cst.CstString;
38import com.android.dexgen.rop.cst.CstType;
39import com.android.dexgen.rop.cst.CstUtf8;
40import com.android.dexgen.util.AnnotatedOutput;
41import com.android.dexgen.util.Hex;
42
43import java.util.Collection;
44
45/**
46 * Handler for writing out {@code encoded_values} and parts
47 * thereof.
48 */
49public final class ValueEncoder {
50    /** annotation value type constant: {@code byte} */
51    private static final int VALUE_BYTE = 0x00;
52
53    /** annotation value type constant: {@code short} */
54    private static final int VALUE_SHORT = 0x02;
55
56    /** annotation value type constant: {@code char} */
57    private static final int VALUE_CHAR = 0x03;
58
59    /** annotation value type constant: {@code int} */
60    private static final int VALUE_INT = 0x04;
61
62    /** annotation value type constant: {@code long} */
63    private static final int VALUE_LONG = 0x06;
64
65    /** annotation value type constant: {@code float} */
66    private static final int VALUE_FLOAT = 0x10;
67
68    /** annotation value type constant: {@code double} */
69    private static final int VALUE_DOUBLE = 0x11;
70
71    /** annotation value type constant: {@code string} */
72    private static final int VALUE_STRING = 0x17;
73
74    /** annotation value type constant: {@code type} */
75    private static final int VALUE_TYPE = 0x18;
76
77    /** annotation value type constant: {@code field} */
78    private static final int VALUE_FIELD = 0x19;
79
80    /** annotation value type constant: {@code method} */
81    private static final int VALUE_METHOD = 0x1a;
82
83    /** annotation value type constant: {@code enum} */
84    private static final int VALUE_ENUM = 0x1b;
85
86    /** annotation value type constant: {@code array} */
87    private static final int VALUE_ARRAY = 0x1c;
88
89    /** annotation value type constant: {@code annotation} */
90    private static final int VALUE_ANNOTATION = 0x1d;
91
92    /** annotation value type constant: {@code null} */
93    private static final int VALUE_NULL = 0x1e;
94
95    /** annotation value type constant: {@code boolean} */
96    private static final int VALUE_BOOLEAN = 0x1f;
97
98    /** {@code non-null;} file being written */
99    private final DexFile file;
100
101    /** {@code non-null;} output stream to write to */
102    private final AnnotatedOutput out;
103
104    /**
105     * Construct an instance.
106     *
107     * @param file {@code non-null;} file being written
108     * @param out {@code non-null;} output stream to write to
109     */
110    public ValueEncoder(DexFile file, AnnotatedOutput out) {
111        if (file == null) {
112            throw new NullPointerException("file == null");
113        }
114
115        if (out == null) {
116            throw new NullPointerException("out == null");
117        }
118
119        this.file = file;
120        this.out = out;
121    }
122
123    /**
124     * Writes out the encoded form of the given constant.
125     *
126     * @param cst {@code non-null;} the constant to write
127     */
128    public void writeConstant(Constant cst) {
129        int type = constantToValueType(cst);
130        int arg;
131
132        switch (type) {
133            case VALUE_BYTE:
134            case VALUE_SHORT:
135            case VALUE_INT:
136            case VALUE_LONG: {
137                long value = ((CstLiteralBits) cst).getLongBits();
138                writeSignedIntegralValue(type, value);
139                break;
140            }
141            case VALUE_CHAR: {
142                long value = ((CstLiteralBits) cst).getLongBits();
143                writeUnsignedIntegralValue(type, value);
144                break;
145            }
146            case VALUE_FLOAT: {
147                // Shift value left 32 so that right-zero-extension works.
148                long value = ((CstFloat) cst).getLongBits() << 32;
149                writeRightZeroExtendedValue(type, value);
150                break;
151            }
152            case VALUE_DOUBLE: {
153                long value = ((CstDouble) cst).getLongBits();
154                writeRightZeroExtendedValue(type, value);
155                break;
156            }
157            case VALUE_STRING: {
158                int index = file.getStringIds().indexOf((CstString) cst);
159                writeUnsignedIntegralValue(type, (long) index);
160                break;
161            }
162            case VALUE_TYPE: {
163                int index = file.getTypeIds().indexOf((CstType) cst);
164                writeUnsignedIntegralValue(type, (long) index);
165                break;
166            }
167            case VALUE_FIELD: {
168                int index = file.getFieldIds().indexOf((CstFieldRef) cst);
169                writeUnsignedIntegralValue(type, (long) index);
170                break;
171            }
172            case VALUE_METHOD: {
173                int index = file.getMethodIds().indexOf((CstMethodRef) cst);
174                writeUnsignedIntegralValue(type, (long) index);
175                break;
176            }
177            case VALUE_ENUM: {
178                CstFieldRef fieldRef = ((CstEnumRef) cst).getFieldRef();
179                int index = file.getFieldIds().indexOf(fieldRef);
180                writeUnsignedIntegralValue(type, (long) index);
181                break;
182            }
183            case VALUE_ARRAY: {
184                out.writeByte(type);
185                writeArray((CstArray) cst, false);
186                break;
187            }
188            case VALUE_ANNOTATION: {
189                out.writeByte(type);
190                writeAnnotation(((CstAnnotation) cst).getAnnotation(),
191                        false);
192                break;
193            }
194            case VALUE_NULL: {
195                out.writeByte(type);
196                break;
197            }
198            case VALUE_BOOLEAN: {
199                int value = ((CstBoolean) cst).getIntBits();
200                out.writeByte(type | (value << 5));
201                break;
202            }
203            default: {
204                throw new RuntimeException("Shouldn't happen");
205            }
206        }
207    }
208
209    /**
210     * Gets the value type for the given constant.
211     *
212     * @param cst {@code non-null;} the constant
213     * @return the value type; one of the {@code VALUE_*} constants
214     * defined by this class
215     */
216    private static int constantToValueType(Constant cst) {
217        /*
218         * TODO: Constant should probable have an associated enum, so this
219         * can be a switch().
220         */
221        if (cst instanceof CstByte) {
222            return VALUE_BYTE;
223        } else if (cst instanceof CstShort) {
224            return VALUE_SHORT;
225        } else if (cst instanceof CstChar) {
226            return VALUE_CHAR;
227        } else if (cst instanceof CstInteger) {
228            return VALUE_INT;
229        } else if (cst instanceof CstLong) {
230            return VALUE_LONG;
231        } else if (cst instanceof CstFloat) {
232            return VALUE_FLOAT;
233        } else if (cst instanceof CstDouble) {
234            return VALUE_DOUBLE;
235        } else if (cst instanceof CstString) {
236            return VALUE_STRING;
237        } else if (cst instanceof CstType) {
238            return VALUE_TYPE;
239        } else if (cst instanceof CstFieldRef) {
240            return VALUE_FIELD;
241        } else if (cst instanceof CstMethodRef) {
242            return VALUE_METHOD;
243        } else if (cst instanceof CstEnumRef) {
244            return VALUE_ENUM;
245        } else if (cst instanceof CstArray) {
246            return VALUE_ARRAY;
247        } else if (cst instanceof CstAnnotation) {
248            return VALUE_ANNOTATION;
249        } else if (cst instanceof CstKnownNull) {
250            return VALUE_NULL;
251        } else if (cst instanceof CstBoolean) {
252            return VALUE_BOOLEAN;
253        } else {
254            throw new RuntimeException("Shouldn't happen");
255        }
256    }
257
258    /**
259     * Writes out the encoded form of the given array, that is, as
260     * an {@code encoded_array} and not including a
261     * {@code value_type} prefix. If the output stream keeps
262     * (debugging) annotations and {@code topLevel} is
263     * {@code true}, then this method will write (debugging)
264     * annotations.
265     *
266     * @param array {@code non-null;} array instance to write
267     * @param topLevel {@code true} iff the given annotation is the
268     * top-level annotation or {@code false} if it is a sub-annotation
269     * of some other annotation
270     */
271    public void writeArray(CstArray array, boolean topLevel) {
272        boolean annotates = topLevel && out.annotates();
273        CstArray.List list = ((CstArray) array).getList();
274        int size = list.size();
275
276        if (annotates) {
277            out.annotate("  size: " + Hex.u4(size));
278        }
279
280        out.writeUnsignedLeb128(size);
281
282        for (int i = 0; i < size; i++) {
283            Constant cst = list.get(i);
284            if (annotates) {
285                out.annotate("  [" + Integer.toHexString(i) + "] " +
286                        constantToHuman(cst));
287            }
288            writeConstant(cst);
289        }
290
291        if (annotates) {
292            out.endAnnotation();
293        }
294    }
295
296    /**
297     * Writes out the encoded form of the given annotation, that is,
298     * as an {@code encoded_annotation} and not including a
299     * {@code value_type} prefix. If the output stream keeps
300     * (debugging) annotations and {@code topLevel} is
301     * {@code true}, then this method will write (debugging)
302     * annotations.
303     *
304     * @param annotation {@code non-null;} annotation instance to write
305     * @param topLevel {@code true} iff the given annotation is the
306     * top-level annotation or {@code false} if it is a sub-annotation
307     * of some other annotation
308     */
309    public void writeAnnotation(Annotation annotation, boolean topLevel) {
310        boolean annotates = topLevel && out.annotates();
311        StringIdsSection stringIds = file.getStringIds();
312        TypeIdsSection typeIds = file.getTypeIds();
313
314        CstType type = annotation.getType();
315        int typeIdx = typeIds.indexOf(type);
316
317        if (annotates) {
318            out.annotate("  type_idx: " + Hex.u4(typeIdx) + " // " +
319                    type.toHuman());
320        }
321
322        out.writeUnsignedLeb128(typeIds.indexOf(annotation.getType()));
323
324        Collection<NameValuePair> pairs = annotation.getNameValuePairs();
325        int size = pairs.size();
326
327        if (annotates) {
328            out.annotate("  size: " + Hex.u4(size));
329        }
330
331        out.writeUnsignedLeb128(size);
332
333        int at = 0;
334        for (NameValuePair pair : pairs) {
335            CstUtf8 name = pair.getName();
336            int nameIdx = stringIds.indexOf(name);
337            Constant value = pair.getValue();
338
339            if (annotates) {
340                out.annotate(0, "  elements[" + at + "]:");
341                at++;
342                out.annotate("    name_idx: " + Hex.u4(nameIdx) + " // " +
343                        name.toHuman());
344            }
345
346            out.writeUnsignedLeb128(nameIdx);
347
348            if (annotates) {
349                out.annotate("    value: " + constantToHuman(value));
350            }
351
352            writeConstant(value);
353        }
354
355        if (annotates) {
356            out.endAnnotation();
357        }
358    }
359
360    /**
361     * Gets the colloquial type name and human form of the type of the
362     * given constant, when used as an encoded value.
363     *
364     * @param cst {@code non-null;} the constant
365     * @return {@code non-null;} its type name and human form
366     */
367    public static String constantToHuman(Constant cst) {
368        int type = constantToValueType(cst);
369
370        if (type == VALUE_NULL) {
371            return "null";
372        }
373
374        StringBuilder sb = new StringBuilder();
375
376        sb.append(cst.typeName());
377        sb.append(' ');
378        sb.append(cst.toHuman());
379
380        return sb.toString();
381    }
382
383    /**
384     * Helper for {@link #writeConstant}, which writes out the value
385     * for any signed integral type.
386     *
387     * @param type the type constant
388     * @param value {@code long} bits of the value
389     */
390    private void writeSignedIntegralValue(int type, long value) {
391        /*
392         * Figure out how many bits are needed to represent the value,
393         * including a sign bit: The bit count is subtracted from 65
394         * and not 64 to account for the sign bit. The xor operation
395         * has the effect of leaving non-negative values alone and
396         * unary complementing negative values (so that a leading zero
397         * count always returns a useful number for our present
398         * purpose).
399         */
400        int requiredBits =
401            65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
402
403        // Round up the requiredBits to a number of bytes.
404        int requiredBytes = (requiredBits + 0x07) >> 3;
405
406        /*
407         * Write the header byte, which includes the type and
408         * requiredBytes - 1.
409         */
410        out.writeByte(type | ((requiredBytes - 1) << 5));
411
412        // Write the value, per se.
413        while (requiredBytes > 0) {
414            out.writeByte((byte) value);
415            value >>= 8;
416            requiredBytes--;
417        }
418    }
419
420    /**
421     * Helper for {@link #writeConstant}, which writes out the value
422     * for any unsigned integral type.
423     *
424     * @param type the type constant
425     * @param value {@code long} bits of the value
426     */
427    private void writeUnsignedIntegralValue(int type, long value) {
428        // Figure out how many bits are needed to represent the value.
429        int requiredBits = 64 - Long.numberOfLeadingZeros(value);
430        if (requiredBits == 0) {
431            requiredBits = 1;
432        }
433
434        // Round up the requiredBits to a number of bytes.
435        int requiredBytes = (requiredBits + 0x07) >> 3;
436
437        /*
438         * Write the header byte, which includes the type and
439         * requiredBytes - 1.
440         */
441        out.writeByte(type | ((requiredBytes - 1) << 5));
442
443        // Write the value, per se.
444        while (requiredBytes > 0) {
445            out.writeByte((byte) value);
446            value >>= 8;
447            requiredBytes--;
448        }
449    }
450
451    /**
452     * Helper for {@link #writeConstant}, which writes out a
453     * right-zero-extended value.
454     *
455     * @param type the type constant
456     * @param value {@code long} bits of the value
457     */
458    private void writeRightZeroExtendedValue(int type, long value) {
459        // Figure out how many bits are needed to represent the value.
460        int requiredBits = 64 - Long.numberOfTrailingZeros(value);
461        if (requiredBits == 0) {
462            requiredBits = 1;
463        }
464
465        // Round up the requiredBits to a number of bytes.
466        int requiredBytes = (requiredBits + 0x07) >> 3;
467
468        // Scootch the first bits to be written down to the low-order bits.
469        value >>= 64 - (requiredBytes * 8);
470
471        /*
472         * Write the header byte, which includes the type and
473         * requiredBytes - 1.
474         */
475        out.writeByte(type | ((requiredBytes - 1) << 5));
476
477        // Write the value, per se.
478        while (requiredBytes > 0) {
479            out.writeByte((byte) value);
480            value >>= 8;
481            requiredBytes--;
482        }
483    }
484
485
486    /**
487     * Helper for {@code addContents()} methods, which adds
488     * contents for a particular {@link Annotation}, calling itself
489     * recursively should it encounter a nested annotation.
490     *
491     * @param file {@code non-null;} the file to add to
492     * @param annotation {@code non-null;} the annotation to add contents for
493     */
494    public static void addContents(DexFile file, Annotation annotation) {
495        TypeIdsSection typeIds = file.getTypeIds();
496        StringIdsSection stringIds = file.getStringIds();
497
498        typeIds.intern(annotation.getType());
499
500        for (NameValuePair pair : annotation.getNameValuePairs()) {
501            stringIds.intern(pair.getName());
502            addContents(file, pair.getValue());
503        }
504    }
505
506    /**
507     * Helper for {@code addContents()} methods, which adds
508     * contents for a particular constant, calling itself recursively
509     * should it encounter a {@link CstArray} and calling {@link
510     * #addContents(DexFile,Annotation)} recursively should it
511     * encounter a {@link CstAnnotation}.
512     *
513     * @param file {@code non-null;} the file to add to
514     * @param cst {@code non-null;} the constant to add contents for
515     */
516    public static void addContents(DexFile file, Constant cst) {
517        if (cst instanceof CstAnnotation) {
518            addContents(file, ((CstAnnotation) cst).getAnnotation());
519        } else if (cst instanceof CstArray) {
520            CstArray.List list = ((CstArray) cst).getList();
521            int size = list.size();
522            for (int i = 0; i < size; i++) {
523                addContents(file, list.get(i));
524            }
525        } else {
526            file.internIfAppropriate(cst);
527        }
528    }
529}
530