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