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