InsnFormat.java revision ab35b50311951feea3782151dd5422ee944685c2
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 < 0x100) || (opcode > 0xffff)) {
511            throw new IllegalArgumentException("opcode out of range 0..65535");
512        }
513
514        return (short) opcode;
515    }
516
517    /**
518     * Helper method to combine two bytes into a code unit.
519     *
520     * @param low {@code 0..255;} low byte
521     * @param high {@code 0..255;} high byte
522     * @return combined value
523     */
524    protected static short codeUnit(int low, int high) {
525        if ((low & 0xff) != low) {
526            throw new IllegalArgumentException("low out of range 0..255");
527        }
528
529        if ((high & 0xff) != high) {
530            throw new IllegalArgumentException("high out of range 0..255");
531        }
532
533        return (short) (low | (high << 8));
534    }
535
536    /**
537     * Helper method to combine four nibbles into a code unit.
538     *
539     * @param n0 {@code 0..15;} low nibble
540     * @param n1 {@code 0..15;} medium-low nibble
541     * @param n2 {@code 0..15;} medium-high nibble
542     * @param n3 {@code 0..15;} high nibble
543     * @return combined value
544     */
545    protected static short codeUnit(int n0, int n1, int n2, int n3) {
546        if ((n0 & 0xf) != n0) {
547            throw new IllegalArgumentException("n0 out of range 0..15");
548        }
549
550        if ((n1 & 0xf) != n1) {
551            throw new IllegalArgumentException("n1 out of range 0..15");
552        }
553
554        if ((n2 & 0xf) != n2) {
555            throw new IllegalArgumentException("n2 out of range 0..15");
556        }
557
558        if ((n3 & 0xf) != n3) {
559            throw new IllegalArgumentException("n3 out of range 0..15");
560        }
561
562        return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12));
563    }
564
565    /**
566     * Helper method to combine two nibbles into a byte.
567     *
568     * @param low {@code 0..15;} low nibble
569     * @param high {@code 0..15;} high nibble
570     * @return {@code 0..255;} combined value
571     */
572    protected static int makeByte(int low, int high) {
573        if ((low & 0xf) != low) {
574            throw new IllegalArgumentException("low out of range 0..15");
575        }
576
577        if ((high & 0xf) != high) {
578            throw new IllegalArgumentException("high out of range 0..15");
579        }
580
581        return low | (high << 4);
582    }
583
584    /**
585     * Writes one code unit to the given output destination.
586     *
587     * @param out {@code non-null;} where to write to
588     * @param c0 code unit to write
589     */
590    protected static void write(AnnotatedOutput out, short c0) {
591        out.writeShort(c0);
592    }
593
594    /**
595     * Writes two code units to the given output destination.
596     *
597     * @param out {@code non-null;} where to write to
598     * @param c0 code unit to write
599     * @param c1 code unit to write
600     */
601    protected static void write(AnnotatedOutput out, short c0, short c1) {
602        out.writeShort(c0);
603        out.writeShort(c1);
604    }
605
606    /**
607     * Writes three code units to the given output destination.
608     *
609     * @param out {@code non-null;} where to write to
610     * @param c0 code unit to write
611     * @param c1 code unit to write
612     * @param c2 code unit to write
613     */
614    protected static void write(AnnotatedOutput out, short c0, short c1,
615            short c2) {
616        out.writeShort(c0);
617        out.writeShort(c1);
618        out.writeShort(c2);
619    }
620
621    /**
622     * Writes four code units to the given output destination.
623     *
624     * @param out {@code non-null;} where to write to
625     * @param c0 code unit to write
626     * @param c1 code unit to write
627     * @param c2 code unit to write
628     * @param c3 code unit to write
629     */
630    protected static void write(AnnotatedOutput out, short c0, short c1,
631            short c2, short c3) {
632        out.writeShort(c0);
633        out.writeShort(c1);
634        out.writeShort(c2);
635        out.writeShort(c3);
636    }
637
638    /**
639     * Writes five code units to the given output destination.
640     *
641     * @param out {@code non-null;} where to write to
642     * @param c0 code unit to write
643     * @param c1 code unit to write
644     * @param c2 code unit to write
645     * @param c3 code unit to write
646     * @param c4 code unit to write
647     */
648    protected static void write(AnnotatedOutput out, short c0, short c1,
649            short c2, short c3, short c4) {
650        out.writeShort(c0);
651        out.writeShort(c1);
652        out.writeShort(c2);
653        out.writeShort(c3);
654        out.writeShort(c4);
655    }
656
657    /**
658     * Writes three code units to the given output destination, where the
659     * second and third are represented as single <code>int</code> and emitted
660     * in little-endian order.
661     *
662     * @param out {@code non-null;} where to write to
663     * @param c0 code unit to write
664     * @param c1c2 code unit pair to write
665     */
666    protected static void write(AnnotatedOutput out, short c0, int c1c2) {
667        write(out, c0, (short) c1c2, (short) (c1c2 >> 16));
668    }
669
670    /**
671     * Writes four code units to the given output destination, where the
672     * second and third are represented as single <code>int</code> and emitted
673     * in little-endian order.
674     *
675     * @param out {@code non-null;} where to write to
676     * @param c0 code unit to write
677     * @param c1c2 code unit pair to write
678     * @param c3 code unit to write
679     */
680    protected static void write(AnnotatedOutput out, short c0, int c1c2,
681            short c3) {
682        write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3);
683    }
684
685    /**
686     * Writes five code units to the given output destination, where the
687     * second and third are represented as single <code>int</code> and emitted
688     * in little-endian order.
689     *
690     * @param out {@code non-null;} where to write to
691     * @param c0 code unit to write
692     * @param c1c2 code unit pair to write
693     * @param c3 code unit to write
694     * @param c4 code unit to write
695     */
696    protected static void write(AnnotatedOutput out, short c0, int c1c2,
697            short c3, short c4) {
698        write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3, c4);
699    }
700
701    /**
702     * Writes five code units to the given output destination, where the
703     * second through fifth are represented as single <code>long</code>
704     * and emitted in little-endian order.
705     *
706     * @param out {@code non-null;} where to write to
707     * @param c0 code unit to write
708     * @param c1c2c3c4 code unit quad to write
709     */
710    protected static void write(AnnotatedOutput out, short c0, long c1c2c3c4) {
711        write(out, c0, (short) c1c2c3c4, (short) (c1c2c3c4 >> 16),
712                (short) (c1c2c3c4 >> 32), (short) (c1c2c3c4 >> 48));
713    }
714}
715