InsnFormat.java revision 579d7739c53a2707ad711a2d2cae46d7d782f061
1/*
2 * Copyright (C) 2007 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.code;
18
19import com.android.dx.rop.code.RegisterSpec;
20import com.android.dx.rop.code.RegisterSpecList;
21import com.android.dx.rop.cst.Constant;
22import com.android.dx.rop.cst.CstInteger;
23import com.android.dx.rop.cst.CstKnownNull;
24import com.android.dx.rop.cst.CstLiteral64;
25import com.android.dx.rop.cst.CstLiteralBits;
26import com.android.dx.rop.cst.CstString;
27import com.android.dx.util.AnnotatedOutput;
28import com.android.dx.util.Hex;
29
30import java.util.BitSet;
31
32/**
33 * Base class for all instruction format handlers. Instruction format
34 * handlers know how to translate {@link DalvInsn} instances into
35 * streams of code units, as well as human-oriented listing strings
36 * representing such translations.
37 */
38public abstract class InsnFormat {
39    /**
40     * flag to enable/disable the new extended opcode formats; meant as a
41     * temporary measure until VM support for the salient opcodes is
42     * added. TODO: Remove this declaration when the VM can deal.
43     */
44    public static boolean ALLOW_EXTENDED_OPCODES = true;
45
46    /**
47     * Returns the string form, suitable for inclusion in a listing
48     * dump, of the given instruction. The instruction must be of this
49     * instance's format for proper operation.
50     *
51     * @param insn {@code non-null;} the instruction
52     * @param noteIndices whether to include an explicit notation of
53     * constant pool indices
54     * @return {@code non-null;} the string form
55     */
56    public final String listingString(DalvInsn insn, boolean noteIndices) {
57        String op = insn.getOpcode().getName();
58        String arg = insnArgString(insn);
59        String comment = insnCommentString(insn, noteIndices);
60        StringBuilder sb = new StringBuilder(100);
61
62        sb.append(op);
63
64        if (arg.length() != 0) {
65            sb.append(' ');
66            sb.append(arg);
67        }
68
69        if (comment.length() != 0) {
70            sb.append(" // ");
71            sb.append(comment);
72        }
73
74        return sb.toString();
75    }
76
77    /**
78     * Returns the string form of the arguments to the given instruction.
79     * The instruction must be of this instance's format. If the instruction
80     * has no arguments, then the result should be {@code ""}, not
81     * {@code null}.
82     *
83     * <p>Subclasses must override this method.</p>
84     *
85     * @param insn {@code non-null;} the instruction
86     * @return {@code non-null;} the string form
87     */
88    public abstract String insnArgString(DalvInsn insn);
89
90    /**
91     * Returns the associated comment for the given instruction, if any.
92     * The instruction must be of this instance's format. If the instruction
93     * has no comment, then the result should be {@code ""}, not
94     * {@code null}.
95     *
96     * <p>Subclasses must override this method.</p>
97     *
98     * @param insn {@code non-null;} the instruction
99     * @param noteIndices whether to include an explicit notation of
100     * constant pool indices
101     * @return {@code non-null;} the string form
102     */
103    public abstract String insnCommentString(DalvInsn insn,
104            boolean noteIndices);
105
106    /**
107     * Gets the code size of instructions that use this format. The
108     * size is a number of 16-bit code units, not bytes. This should
109     * throw an exception if this format is of variable size.
110     *
111     * @return {@code >= 0;} the instruction length in 16-bit code units
112     */
113    public abstract int codeSize();
114
115    /**
116     * Returns whether or not the given instruction's arguments will
117     * fit in this instance's format. This includes such things as
118     * counting register arguments, checking register ranges, and
119     * making sure that additional arguments are of appropriate types
120     * and are in-range. If this format has a branch target but the
121     * instruction's branch offset is unknown, this method will simply
122     * not check the offset.
123     *
124     * <p>Subclasses must override this method.</p>
125     *
126     * @param insn {@code non-null;} the instruction to check
127     * @return {@code true} iff the instruction's arguments are
128     * appropriate for this instance, or {@code false} if not
129     */
130    public abstract boolean isCompatible(DalvInsn insn);
131
132    /**
133     * Returns which of a given instruction's registers will fit in
134     * this instance's format.
135     *
136     * <p>The default implementation of this method always returns
137     * an empty BitSet. Subclasses must override this method if they
138     * have registers.</p>
139     *
140     * @param insn {@code non-null;} the instruction to check
141     * @return {@code non-null;} a BitSet flagging registers in the
142     * register list that are compatible to this format
143     */
144    public BitSet compatibleRegs(DalvInsn insn) {
145        return new BitSet();
146    }
147
148    /**
149     * Returns whether or not the given instruction's branch offset will
150     * fit in this instance's format. This always returns {@code false}
151     * for formats that don't include a branch offset.
152     *
153     * <p>The default implementation of this method always returns
154     * {@code false}. Subclasses must override this method if they
155     * include branch offsets.</p>
156     *
157     * @param insn {@code non-null;} the instruction to check
158     * @return {@code true} iff the instruction's branch offset is
159     * appropriate for this instance, or {@code false} if not
160     */
161    public boolean branchFits(TargetInsn insn) {
162        return false;
163    }
164
165    /**
166     * Writes the code units for the given instruction to the given
167     * output destination. The instruction must be of this instance's format.
168     *
169     * <p>Subclasses must override this method.</p>
170     *
171     * @param out {@code non-null;} the output destination to write to
172     * @param insn {@code non-null;} the instruction to write
173     */
174    public abstract void writeTo(AnnotatedOutput out, DalvInsn insn);
175
176    /**
177     * Helper method to return a register list string.
178     *
179     * @param list {@code non-null;} the list of registers
180     * @return {@code non-null;} the string form
181     */
182    protected static String regListString(RegisterSpecList list) {
183        int sz = list.size();
184        StringBuffer sb = new StringBuffer(sz * 5 + 2);
185
186        sb.append('{');
187
188        for (int i = 0; i < sz; i++) {
189            if (i != 0) {
190                sb.append(", ");
191            }
192            sb.append(list.get(i).regString());
193        }
194
195        sb.append('}');
196
197        return sb.toString();
198    }
199
200    /**
201     * Helper method to return a register range string.
202     *
203     * @param list {@code non-null;} the list of registers (which must be
204     * sequential)
205     * @return {@code non-null;} the string form
206     */
207    protected static String regRangeString(RegisterSpecList list) {
208        int size = list.size();
209        StringBuilder sb = new StringBuilder(30);
210
211        sb.append("{");
212
213        switch (size) {
214            case 0: {
215                // Nothing to do.
216                break;
217            }
218            case 1: {
219                sb.append(list.get(0).regString());
220                break;
221            }
222            default: {
223                RegisterSpec lastReg = list.get(size - 1);
224                if (lastReg.getCategory() == 2) {
225                    /*
226                     * Add one to properly represent a list-final
227                     * category-2 register.
228                     */
229                    lastReg = lastReg.withOffset(1);
230                }
231
232                sb.append(list.get(0).regString());
233                sb.append("..");
234                sb.append(lastReg.regString());
235            }
236        }
237
238        sb.append("}");
239
240        return sb.toString();
241    }
242
243    /**
244     * Helper method to return a literal bits argument string.
245     *
246     * @param value the value
247     * @return {@code non-null;} the string form
248     */
249    protected static String literalBitsString(CstLiteralBits value) {
250        StringBuffer sb = new StringBuffer(100);
251
252        sb.append('#');
253
254        if (value instanceof CstKnownNull) {
255            sb.append("null");
256        } else {
257            sb.append(value.typeName());
258            sb.append(' ');
259            sb.append(value.toHuman());
260        }
261
262        return sb.toString();
263    }
264
265    /**
266     * Helper method to return a literal bits comment string.
267     *
268     * @param value the value
269     * @param width the width of the constant, in bits (used for displaying
270     * the uninterpreted bits; one of: {@code 4 8 16 32 64}
271     * @return {@code non-null;} the comment
272     */
273    protected static String literalBitsComment(CstLiteralBits value,
274            int width) {
275        StringBuffer sb = new StringBuffer(20);
276
277        sb.append("#");
278
279        long bits;
280
281        if (value instanceof CstLiteral64) {
282            bits = ((CstLiteral64) value).getLongBits();
283        } else {
284            bits = value.getIntBits();
285        }
286
287        switch (width) {
288            case 4:  sb.append(Hex.uNibble((int) bits)); break;
289            case 8:  sb.append(Hex.u1((int) bits));      break;
290            case 16: sb.append(Hex.u2((int) bits));      break;
291            case 32: sb.append(Hex.u4((int) bits));      break;
292            case 64: sb.append(Hex.u8(bits));            break;
293            default: {
294                throw new RuntimeException("shouldn't happen");
295            }
296        }
297
298        return sb.toString();
299    }
300
301    /**
302     * Helper method to return a branch address string.
303     *
304     * @param insn {@code non-null;} the instruction in question
305     * @return {@code non-null;} the string form of the instruction's
306     * branch target
307     */
308    protected static String branchString(DalvInsn insn) {
309        TargetInsn ti = (TargetInsn) insn;
310        int address = ti.getTargetAddress();
311
312        return (address == (char) address) ? Hex.u2(address) : Hex.u4(address);
313    }
314
315    /**
316     * Helper method to return the comment for a branch.
317     *
318     * @param insn {@code non-null;} the instruction in question
319     * @return {@code non-null;} the comment
320     */
321    protected static String branchComment(DalvInsn insn) {
322        TargetInsn ti = (TargetInsn) insn;
323        int offset = ti.getTargetOffset();
324
325        return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset);
326    }
327
328    /**
329     * Helper method to return the constant string for a {@link CstInsn}
330     * in human form.
331     *
332     * @param insn {@code non-null;} a constant-bearing instruction
333     * @return {@code non-null;} the human string form of the contained
334     * constant
335     */
336    protected static String cstString(DalvInsn insn) {
337        CstInsn ci = (CstInsn) insn;
338        Constant cst = ci.getConstant();
339
340        return cst instanceof CstString ? ((CstString) cst).toQuoted() : cst.toHuman();
341    }
342
343    /**
344     * Helper method to return an instruction comment for a constant.
345     *
346     * @param insn {@code non-null;} a constant-bearing instruction
347     * @return {@code non-null;} comment string representing the constant
348     */
349    protected static String cstComment(DalvInsn insn) {
350        CstInsn ci = (CstInsn) insn;
351
352        if (! ci.hasIndex()) {
353            return "";
354        }
355
356        StringBuilder sb = new StringBuilder(20);
357        int index = ci.getIndex();
358
359        sb.append(ci.getConstant().typeName());
360        sb.append('@');
361
362        if (index < 65536) {
363            sb.append(Hex.u2(index));
364        } else {
365            sb.append(Hex.u4(index));
366        }
367
368        return sb.toString();
369    }
370
371    /**
372     * Helper method to determine if a signed int value fits in a nibble.
373     *
374     * @param value the value in question
375     * @return {@code true} iff it's in the range -8..+7
376     */
377    protected static boolean signedFitsInNibble(int value) {
378        return (value >= -8) && (value <= 7);
379    }
380
381    /**
382     * Helper method to determine if an unsigned int value fits in a nibble.
383     *
384     * @param value the value in question
385     * @return {@code true} iff it's in the range 0..0xf
386     */
387    protected static boolean unsignedFitsInNibble(int value) {
388        return value == (value & 0xf);
389    }
390
391    /**
392     * Helper method to determine if a signed int value fits in a byte.
393     *
394     * @param value the value in question
395     * @return {@code true} iff it's in the range -0x80..+0x7f
396     */
397    protected static boolean signedFitsInByte(int value) {
398        return (byte) value == value;
399    }
400
401    /**
402     * Helper method to determine if an unsigned int value fits in a byte.
403     *
404     * @param value the value in question
405     * @return {@code true} iff it's in the range 0..0xff
406     */
407    protected static boolean unsignedFitsInByte(int value) {
408        return value == (value & 0xff);
409    }
410
411    /**
412     * Helper method to determine if a signed int value fits in a short.
413     *
414     * @param value the value in question
415     * @return {@code true} iff it's in the range -0x8000..+0x7fff
416     */
417    protected static boolean signedFitsInShort(int value) {
418        return (short) value == value;
419    }
420
421    /**
422     * Helper method to determine if an unsigned int value fits in a short.
423     *
424     * @param value the value in question
425     * @return {@code true} iff it's in the range 0..0xffff
426     */
427    protected static boolean unsignedFitsInShort(int value) {
428        return value == (value & 0xffff);
429    }
430
431    /**
432     * Helper method to determine if a list of registers are sequential,
433     * including degenerate cases for empty or single-element lists.
434     *
435     * @param list {@code non-null;} the list of registers
436     * @return {@code true} iff the list is sequentially ordered
437     */
438    protected static boolean isRegListSequential(RegisterSpecList list) {
439        int sz = list.size();
440
441        if (sz < 2) {
442            return true;
443        }
444
445        int first = list.get(0).getReg();
446        int next = first;
447
448        for (int i = 0; i < sz; i++) {
449            RegisterSpec one = list.get(i);
450            if (one.getReg() != next) {
451                return false;
452            }
453            next += one.getCategory();
454        }
455
456        return true;
457    }
458
459    /**
460     * Helper method to extract the callout-argument index from an
461     * appropriate instruction.
462     *
463     * @param insn {@code non-null;} the instruction
464     * @return {@code >= 0;} the callout argument index
465     */
466    protected static int argIndex(DalvInsn insn) {
467        int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue();
468
469        if (arg < 0) {
470            throw new IllegalArgumentException("bogus insn");
471        }
472
473        return arg;
474    }
475
476    /**
477     * Helper method to combine an opcode and a second byte of data into
478     * the appropriate form for emitting into a code buffer.
479     *
480     * @param insn {@code non-null;} the instruction containing the opcode
481     * @param arg {@code 0..255;} arbitrary other byte value
482     * @return combined value
483     */
484    protected static short opcodeUnit(DalvInsn insn, int arg) {
485        if ((arg & 0xff) != arg) {
486            throw new IllegalArgumentException("arg out of range 0..255");
487        }
488
489        int opcode = insn.getOpcode().getOpcode();
490
491        if ((opcode & 0xff) != opcode) {
492            throw new IllegalArgumentException("opcode out of range 0..255");
493        }
494
495        return (short) (opcode | (arg << 8));
496    }
497
498    /**
499     * Helper method to get an extended (16-bit) opcode out of an
500     * instruction, returning it as a code unit. The opcode
501     * <i>must</i> be an extended opcode.
502     *
503     * @param insn {@code non-null;} the instruction containing the
504     * extended opcode
505     * @return the opcode as a code unit
506     */
507    protected static short opcodeUnit(DalvInsn insn) {
508        int opcode = insn.getOpcode().getOpcode();
509
510        if ((opcode < 0xff) || (opcode > 0xffff)) {
511            throw new IllegalArgumentException(
512                "extended opcode out of range 255..65535");
513        }
514
515        return (short) opcode;
516    }
517
518    /**
519     * Helper method to combine two bytes into a code unit.
520     *
521     * @param low {@code 0..255;} low byte
522     * @param high {@code 0..255;} high byte
523     * @return combined value
524     */
525    protected static short codeUnit(int low, int high) {
526        if ((low & 0xff) != low) {
527            throw new IllegalArgumentException("low out of range 0..255");
528        }
529
530        if ((high & 0xff) != high) {
531            throw new IllegalArgumentException("high out of range 0..255");
532        }
533
534        return (short) (low | (high << 8));
535    }
536
537    /**
538     * Helper method to combine four nibbles into a code unit.
539     *
540     * @param n0 {@code 0..15;} low nibble
541     * @param n1 {@code 0..15;} medium-low nibble
542     * @param n2 {@code 0..15;} medium-high nibble
543     * @param n3 {@code 0..15;} high nibble
544     * @return combined value
545     */
546    protected static short codeUnit(int n0, int n1, int n2, int n3) {
547        if ((n0 & 0xf) != n0) {
548            throw new IllegalArgumentException("n0 out of range 0..15");
549        }
550
551        if ((n1 & 0xf) != n1) {
552            throw new IllegalArgumentException("n1 out of range 0..15");
553        }
554
555        if ((n2 & 0xf) != n2) {
556            throw new IllegalArgumentException("n2 out of range 0..15");
557        }
558
559        if ((n3 & 0xf) != n3) {
560            throw new IllegalArgumentException("n3 out of range 0..15");
561        }
562
563        return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12));
564    }
565
566    /**
567     * Helper method to combine two nibbles into a byte.
568     *
569     * @param low {@code 0..15;} low nibble
570     * @param high {@code 0..15;} high nibble
571     * @return {@code 0..255;} combined value
572     */
573    protected static int makeByte(int low, int high) {
574        if ((low & 0xf) != low) {
575            throw new IllegalArgumentException("low out of range 0..15");
576        }
577
578        if ((high & 0xf) != high) {
579            throw new IllegalArgumentException("high out of range 0..15");
580        }
581
582        return low | (high << 4);
583    }
584
585    /**
586     * Writes one code unit to the given output destination.
587     *
588     * @param out {@code non-null;} where to write to
589     * @param c0 code unit to write
590     */
591    protected static void write(AnnotatedOutput out, short c0) {
592        out.writeShort(c0);
593    }
594
595    /**
596     * Writes two code units to the given output destination.
597     *
598     * @param out {@code non-null;} where to write to
599     * @param c0 code unit to write
600     * @param c1 code unit to write
601     */
602    protected static void write(AnnotatedOutput out, short c0, short c1) {
603        out.writeShort(c0);
604        out.writeShort(c1);
605    }
606
607    /**
608     * Writes three code units to the given output destination.
609     *
610     * @param out {@code non-null;} where to write to
611     * @param c0 code unit to write
612     * @param c1 code unit to write
613     * @param c2 code unit to write
614     */
615    protected static void write(AnnotatedOutput out, short c0, short c1,
616            short c2) {
617        out.writeShort(c0);
618        out.writeShort(c1);
619        out.writeShort(c2);
620    }
621
622    /**
623     * Writes four code units to the given output destination.
624     *
625     * @param out {@code non-null;} where to write to
626     * @param c0 code unit to write
627     * @param c1 code unit to write
628     * @param c2 code unit to write
629     * @param c3 code unit to write
630     */
631    protected static void write(AnnotatedOutput out, short c0, short c1,
632            short c2, short c3) {
633        out.writeShort(c0);
634        out.writeShort(c1);
635        out.writeShort(c2);
636        out.writeShort(c3);
637    }
638
639    /**
640     * Writes five code units to the given output destination.
641     *
642     * @param out {@code non-null;} where to write to
643     * @param c0 code unit to write
644     * @param c1 code unit to write
645     * @param c2 code unit to write
646     * @param c3 code unit to write
647     * @param c4 code unit to write
648     */
649    protected static void write(AnnotatedOutput out, short c0, short c1,
650            short c2, short c3, short c4) {
651        out.writeShort(c0);
652        out.writeShort(c1);
653        out.writeShort(c2);
654        out.writeShort(c3);
655        out.writeShort(c4);
656    }
657
658    /**
659     * Writes three code units to the given output destination, where the
660     * second and third are represented as single <code>int</code> and emitted
661     * in little-endian order.
662     *
663     * @param out {@code non-null;} where to write to
664     * @param c0 code unit to write
665     * @param c1c2 code unit pair to write
666     */
667    protected static void write(AnnotatedOutput out, short c0, int c1c2) {
668        write(out, c0, (short) c1c2, (short) (c1c2 >> 16));
669    }
670
671    /**
672     * Writes four code units to the given output destination, where the
673     * second and third are represented as single <code>int</code> and emitted
674     * in little-endian order.
675     *
676     * @param out {@code non-null;} where to write to
677     * @param c0 code unit to write
678     * @param c1c2 code unit pair to write
679     * @param c3 code unit to write
680     */
681    protected static void write(AnnotatedOutput out, short c0, int c1c2,
682            short c3) {
683        write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3);
684    }
685
686    /**
687     * Writes five code units to the given output destination, where the
688     * second and third are represented as single <code>int</code> and emitted
689     * in little-endian order.
690     *
691     * @param out {@code non-null;} where to write to
692     * @param c0 code unit to write
693     * @param c1c2 code unit pair to write
694     * @param c3 code unit to write
695     * @param c4 code unit to write
696     */
697    protected static void write(AnnotatedOutput out, short c0, int c1c2,
698            short c3, short c4) {
699        write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3, c4);
700    }
701
702    /**
703     * Writes five code units to the given output destination, where the
704     * second through fifth are represented as single <code>long</code>
705     * and emitted in little-endian order.
706     *
707     * @param out {@code non-null;} where to write to
708     * @param c0 code unit to write
709     * @param c1c2c3c4 code unit quad to write
710     */
711    protected static void write(AnnotatedOutput out, short c0, long c1c2c3c4) {
712        write(out, c0, (short) c1c2c3c4, (short) (c1c2c3c4 >> 16),
713                (short) (c1c2c3c4 >> 32), (short) (c1c2c3c4 >> 48));
714    }
715}
716