InsnFormat.java revision dd79e4e11fa20d6677b70ce6618a8653a1f3520d
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.util.AnnotatedOutput;
27import com.android.dx.util.Hex;
28
29import java.util.BitSet;
30
31/**
32 * Base class for all instruction format handlers. Instruction format
33 * handlers know how to translate {@link DalvInsn} instances into
34 * streams of code units, as well as human-oriented listing strings
35 * representing such translations.
36 */
37public abstract class InsnFormat {
38    /**
39     * flag to enable/disable the new extended opcode formats; meant as a
40     * temporary measure until VM support for the salient opcodes is
41     * added. TODO: Remove this declaration when the VM can deal.
42     */
43    public static boolean ALLOW_EXTENDED_OPCODES = false;
44
45    /**
46     * Returns the string form, suitable for inclusion in a listing
47     * dump, of the given instruction. The instruction must be of this
48     * instance's format for proper operation.
49     *
50     * @param insn {@code non-null;} the instruction
51     * @param noteIndices whether to include an explicit notation of
52     * constant pool indices
53     * @return {@code non-null;} the string form
54     */
55    public final String listingString(DalvInsn insn, boolean noteIndices) {
56        String op = insn.getOpcode().getName();
57        String arg = insnArgString(insn);
58        String comment = insnCommentString(insn, noteIndices);
59        StringBuilder sb = new StringBuilder(100);
60
61        sb.append(op);
62
63        if (arg.length() != 0) {
64            sb.append(' ');
65            sb.append(arg);
66        }
67
68        if (comment.length() != 0) {
69            sb.append(" // ");
70            sb.append(comment);
71        }
72
73        return sb.toString();
74    }
75
76    /**
77     * Returns the string form of the arguments to the given instruction.
78     * The instruction must be of this instance's format. If the instruction
79     * has no arguments, then the result should be {@code ""}, not
80     * {@code null}.
81     *
82     * <p>Subclasses must override this method.</p>
83     *
84     * @param insn {@code non-null;} the instruction
85     * @return {@code non-null;} the string form
86     */
87    public abstract String insnArgString(DalvInsn insn);
88
89    /**
90     * Returns the associated comment for the given instruction, if any.
91     * The instruction must be of this instance's format. If the instruction
92     * has no comment, then the result should be {@code ""}, not
93     * {@code null}.
94     *
95     * <p>Subclasses must override this method.</p>
96     *
97     * @param insn {@code non-null;} the instruction
98     * @param noteIndices whether to include an explicit notation of
99     * constant pool indices
100     * @return {@code non-null;} the string form
101     */
102    public abstract String insnCommentString(DalvInsn insn,
103            boolean noteIndices);
104
105    /**
106     * Gets the code size of instructions that use this format. The
107     * size is a number of 16-bit code units, not bytes. This should
108     * throw an exception if this format is of variable size.
109     *
110     * @return {@code >= 0;} the instruction length in 16-bit code units
111     */
112    public abstract int codeSize();
113
114    /**
115     * Returns whether or not the given instruction's arguments will
116     * fit in this instance's format. This includes such things as
117     * counting register arguments, checking register ranges, and
118     * making sure that additional arguments are of appropriate types
119     * and are in-range. If this format has a branch target but the
120     * instruction's branch offset is unknown, this method will simply
121     * not check the offset.
122     *
123     * <p>Subclasses must override this method.</p>
124     *
125     * @param insn {@code non-null;} the instruction to check
126     * @return {@code true} iff the instruction's arguments are
127     * appropriate for this instance, or {@code false} if not
128     */
129    public abstract boolean isCompatible(DalvInsn insn);
130
131    /**
132     * Returns which of a given instruction's registers will fit in
133     * this instance's format.
134     *
135     * <p>The default implementation of this method always returns
136     * an empty BitSet. Subclasses must override this method if they
137     * have registers.</p>
138     *
139     * @param insn {@code non-null;} the instruction to check
140     * @return {@code non-null;} a BitSet flagging registers in the
141     * register list that are compatible to this format
142     */
143    public BitSet compatibleRegs(DalvInsn insn) {
144        return new BitSet();
145    }
146
147    /**
148     * Returns whether or not the given instruction's branch offset will
149     * fit in this instance's format. This always returns {@code false}
150     * for formats that don't include a branch offset.
151     *
152     * <p>The default implementation of this method always returns
153     * {@code false}. Subclasses must override this method if they
154     * include branch offsets.</p>
155     *
156     * @param insn {@code non-null;} the instruction to check
157     * @return {@code true} iff the instruction's branch offset is
158     * appropriate for this instance, or {@code false} if not
159     */
160    public boolean branchFits(TargetInsn insn) {
161        return false;
162    }
163
164    /**
165     * Writes the code units for the given instruction to the given
166     * output destination. The instruction must be of this instance's format.
167     *
168     * <p>Subclasses must override this method.</p>
169     *
170     * @param out {@code non-null;} the output destination to write to
171     * @param insn {@code non-null;} the instruction to write
172     */
173    public abstract void writeTo(AnnotatedOutput out, DalvInsn insn);
174
175    /**
176     * Helper method to return a register list string.
177     *
178     * @param list {@code non-null;} the list of registers
179     * @return {@code non-null;} the string form
180     */
181    protected static String regListString(RegisterSpecList list) {
182        int sz = list.size();
183        StringBuffer sb = new StringBuffer(sz * 5 + 2);
184
185        sb.append('{');
186
187        for (int i = 0; i < sz; i++) {
188            if (i != 0) {
189                sb.append(", ");
190            }
191            sb.append(list.get(i).regString());
192        }
193
194        sb.append('}');
195
196        return sb.toString();
197    }
198
199    /**
200     * Helper method to return a register range string.
201     *
202     * @param list {@code non-null;} the list of registers (which must be
203     * sequential)
204     * @return {@code non-null;} the string form
205     */
206    protected static String regRangeString(RegisterSpecList list) {
207        int size = list.size();
208        StringBuilder sb = new StringBuilder(30);
209
210        sb.append("{");
211
212        switch (size) {
213            case 0: {
214                // Nothing to do.
215                break;
216            }
217            case 1: {
218                sb.append(list.get(0).regString());
219                break;
220            }
221            default: {
222                RegisterSpec lastReg = list.get(size - 1);
223                if (lastReg.getCategory() == 2) {
224                    /*
225                     * Add one to properly represent a list-final
226                     * category-2 register.
227                     */
228                    lastReg = lastReg.withOffset(1);
229                }
230
231                sb.append(list.get(0).regString());
232                sb.append("..");
233                sb.append(lastReg.regString());
234            }
235        }
236
237        sb.append("}");
238
239        return sb.toString();
240    }
241
242    /**
243     * Helper method to return a literal bits argument string.
244     *
245     * @param value the value
246     * @return {@code non-null;} the string form
247     */
248    protected static String literalBitsString(CstLiteralBits value) {
249        StringBuffer sb = new StringBuffer(100);
250
251        sb.append('#');
252
253        if (value instanceof CstKnownNull) {
254            sb.append("null");
255        } else {
256            sb.append(value.typeName());
257            sb.append(' ');
258            sb.append(value.toHuman());
259        }
260
261        return sb.toString();
262    }
263
264    /**
265     * Helper method to return a literal bits comment string.
266     *
267     * @param value the value
268     * @param width the width of the constant, in bits (used for displaying
269     * the uninterpreted bits; one of: {@code 4 8 16 32 64}
270     * @return {@code non-null;} the comment
271     */
272    protected static String literalBitsComment(CstLiteralBits value,
273            int width) {
274        StringBuffer sb = new StringBuffer(20);
275
276        sb.append("#");
277
278        long bits;
279
280        if (value instanceof CstLiteral64) {
281            bits = ((CstLiteral64) value).getLongBits();
282        } else {
283            bits = value.getIntBits();
284        }
285
286        switch (width) {
287            case 4:  sb.append(Hex.uNibble((int) bits)); break;
288            case 8:  sb.append(Hex.u1((int) bits));      break;
289            case 16: sb.append(Hex.u2((int) bits));      break;
290            case 32: sb.append(Hex.u4((int) bits));      break;
291            case 64: sb.append(Hex.u8(bits));            break;
292            default: {
293                throw new RuntimeException("shouldn't happen");
294            }
295        }
296
297        return sb.toString();
298    }
299
300    /**
301     * Helper method to return a branch address string.
302     *
303     * @param insn {@code non-null;} the instruction in question
304     * @return {@code non-null;} the string form of the instruction's
305     * branch target
306     */
307    protected static String branchString(DalvInsn insn) {
308        TargetInsn ti = (TargetInsn) insn;
309        int address = ti.getTargetAddress();
310
311        return (address == (char) address) ? Hex.u2(address) : Hex.u4(address);
312    }
313
314    /**
315     * Helper method to return the comment for a branch.
316     *
317     * @param insn {@code non-null;} the instruction in question
318     * @return {@code non-null;} the comment
319     */
320    protected static String branchComment(DalvInsn insn) {
321        TargetInsn ti = (TargetInsn) insn;
322        int offset = ti.getTargetOffset();
323
324        return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset);
325    }
326
327    /**
328     * Helper method to return the constant string for a {@link CstInsn}
329     * in human form.
330     *
331     * @param insn {@code non-null;} a constant-bearing instruction
332     * @return {@code non-null;} the human string form of the contained
333     * constant
334     */
335    protected static String cstString(DalvInsn insn) {
336        CstInsn ci = (CstInsn) insn;
337        Constant cst = ci.getConstant();
338
339        return cst.toHuman();
340    }
341
342    /**
343     * Helper method to return an instruction comment for a constant.
344     *
345     * @param insn {@code non-null;} a constant-bearing instruction
346     * @return {@code non-null;} comment string representing the constant
347     */
348    protected static String cstComment(DalvInsn insn) {
349        CstInsn ci = (CstInsn) insn;
350
351        if (! ci.hasIndex()) {
352            return "";
353        }
354
355        StringBuilder sb = new StringBuilder(20);
356        int index = ci.getIndex();
357
358        sb.append(ci.getConstant().typeName());
359        sb.append('@');
360
361        if (index < 65536) {
362            sb.append(Hex.u2(index));
363        } else {
364            sb.append(Hex.u4(index));
365        }
366
367        return sb.toString();
368    }
369
370    /**
371     * Helper method to determine if a signed int value fits in a nibble.
372     *
373     * @param value the value in question
374     * @return {@code true} iff it's in the range -8..+7
375     */
376    protected static boolean signedFitsInNibble(int value) {
377        return (value >= -8) && (value <= 7);
378    }
379
380    /**
381     * Helper method to determine if an unsigned int value fits in a nibble.
382     *
383     * @param value the value in question
384     * @return {@code true} iff it's in the range 0..0xf
385     */
386    protected static boolean unsignedFitsInNibble(int value) {
387        return value == (value & 0xf);
388    }
389
390    /**
391     * Helper method to determine if a signed int value fits in a byte.
392     *
393     * @param value the value in question
394     * @return {@code true} iff it's in the range -0x80..+0x7f
395     */
396    protected static boolean signedFitsInByte(int value) {
397        return (byte) value == value;
398    }
399
400    /**
401     * Helper method to determine if an unsigned int value fits in a byte.
402     *
403     * @param value the value in question
404     * @return {@code true} iff it's in the range 0..0xff
405     */
406    protected static boolean unsignedFitsInByte(int value) {
407        return value == (value & 0xff);
408    }
409
410    /**
411     * Helper method to determine if a signed int value fits in a short.
412     *
413     * @param value the value in question
414     * @return {@code true} iff it's in the range -0x8000..+0x7fff
415     */
416    protected static boolean signedFitsInShort(int value) {
417        return (short) value == value;
418    }
419
420    /**
421     * Helper method to determine if an unsigned int value fits in a short.
422     *
423     * @param value the value in question
424     * @return {@code true} iff it's in the range 0..0xffff
425     */
426    protected static boolean unsignedFitsInShort(int value) {
427        return value == (value & 0xffff);
428    }
429
430    /**
431     * Helper method to determine if a list of registers are sequential,
432     * including degenerate cases for empty or single-element lists.
433     *
434     * @param list {@code non-null;} the list of registers
435     * @return {@code true} iff the list is sequentially ordered
436     */
437    protected static boolean isRegListSequential(RegisterSpecList list) {
438        int sz = list.size();
439
440        if (sz < 2) {
441            return true;
442        }
443
444        int first = list.get(0).getReg();
445        int next = first;
446
447        for (int i = 0; i < sz; i++) {
448            RegisterSpec one = list.get(i);
449            if (one.getReg() != next) {
450                return false;
451            }
452            next += one.getCategory();
453        }
454
455        return true;
456    }
457
458    /**
459     * Helper method to extract the callout-argument index from an
460     * appropriate instruction.
461     *
462     * @param insn {@code non-null;} the instruction
463     * @return {@code >= 0;} the callout argument index
464     */
465    protected static int argIndex(DalvInsn insn) {
466        int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue();
467
468        if (arg < 0) {
469            throw new IllegalArgumentException("bogus insn");
470        }
471
472        return arg;
473    }
474
475    /**
476     * Helper method to combine an opcode and a second byte of data into
477     * the appropriate form for emitting into a code buffer.
478     *
479     * @param insn {@code non-null;} the instruction containing the opcode
480     * @param arg {@code 0..255;} arbitrary other byte value
481     * @return combined value
482     */
483    protected static short opcodeUnit(DalvInsn insn, int arg) {
484        if ((arg & 0xff) != arg) {
485            throw new IllegalArgumentException("arg out of range 0..255");
486        }
487
488        int opcode = insn.getOpcode().getOpcode();
489
490        if ((opcode & 0xff) != opcode) {
491            throw new IllegalArgumentException("opcode out of range 0..255");
492        }
493
494        return (short) (opcode | (arg << 8));
495    }
496
497    /**
498     * Helper method to get an extended (16-bit) opcode out of an
499     * instruction, returning it as a code unit. The opcode
500     * <i>must</i> be an extended opcode.
501     *
502     * @param insn {@code non-null;} the instruction containing the
503     * extended opcode
504     * @return the opcode as a code unit
505     */
506    protected static short opcodeUnit(DalvInsn insn) {
507        int opcode = insn.getOpcode().getOpcode();
508
509        if ((opcode < 0xff) || (opcode > 0xffff)) {
510            throw new IllegalArgumentException(
511                "extended opcode out of range 255..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