• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.dx.dex.code;
18 
19 import com.android.dx.rop.code.RegisterSpec;
20 import com.android.dx.rop.code.RegisterSpecList;
21 import com.android.dx.rop.cst.Constant;
22 import com.android.dx.rop.cst.CstInteger;
23 import com.android.dx.rop.cst.CstKnownNull;
24 import com.android.dx.rop.cst.CstLiteral64;
25 import com.android.dx.rop.cst.CstLiteralBits;
26 import com.android.dx.rop.cst.CstString;
27 import com.android.dx.util.AnnotatedOutput;
28 import com.android.dx.util.Hex;
29 import 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  */
37 public 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 = true;
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      */
listingString(DalvInsn insn, boolean noteIndices)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      */
insnArgString(DalvInsn insn)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      */
insnCommentString(DalvInsn insn, boolean noteIndices)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      */
codeSize()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      */
isCompatible(DalvInsn insn)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      */
compatibleRegs(DalvInsn insn)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      */
branchFits(TargetInsn insn)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      */
writeTo(AnnotatedOutput out, DalvInsn insn)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      */
regListString(RegisterSpecList list)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      */
regRangeString(RegisterSpecList list)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      */
literalBitsString(CstLiteralBits value)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      */
literalBitsComment(CstLiteralBits value, int width)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      */
branchString(DalvInsn insn)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      */
branchComment(DalvInsn insn)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      */
cstString(DalvInsn insn)335     protected static String cstString(DalvInsn insn) {
336         CstInsn ci = (CstInsn) insn;
337         Constant cst = ci.getConstant();
338 
339         return cst instanceof CstString ? ((CstString) cst).toQuoted() : 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      */
cstComment(DalvInsn insn)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      */
signedFitsInNibble(int value)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      */
unsignedFitsInNibble(int value)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      */
signedFitsInByte(int value)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      */
unsignedFitsInByte(int value)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      */
signedFitsInShort(int value)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      */
unsignedFitsInShort(int value)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      */
isRegListSequential(RegisterSpecList list)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      */
argIndex(DalvInsn insn)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      */
opcodeUnit(DalvInsn insn, int arg)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      */
opcodeUnit(DalvInsn insn)506     protected static short opcodeUnit(DalvInsn insn) {
507         int opcode = insn.getOpcode().getOpcode();
508 
509         if ((opcode < 0x100) || (opcode > 0xffff)) {
510             throw new IllegalArgumentException("opcode out of range 0..65535");
511         }
512 
513         return (short) opcode;
514     }
515 
516     /**
517      * Helper method to combine two bytes into a code unit.
518      *
519      * @param low {@code 0..255;} low byte
520      * @param high {@code 0..255;} high byte
521      * @return combined value
522      */
codeUnit(int low, int high)523     protected static short codeUnit(int low, int high) {
524         if ((low & 0xff) != low) {
525             throw new IllegalArgumentException("low out of range 0..255");
526         }
527 
528         if ((high & 0xff) != high) {
529             throw new IllegalArgumentException("high out of range 0..255");
530         }
531 
532         return (short) (low | (high << 8));
533     }
534 
535     /**
536      * Helper method to combine four nibbles into a code unit.
537      *
538      * @param n0 {@code 0..15;} low nibble
539      * @param n1 {@code 0..15;} medium-low nibble
540      * @param n2 {@code 0..15;} medium-high nibble
541      * @param n3 {@code 0..15;} high nibble
542      * @return combined value
543      */
codeUnit(int n0, int n1, int n2, int n3)544     protected static short codeUnit(int n0, int n1, int n2, int n3) {
545         if ((n0 & 0xf) != n0) {
546             throw new IllegalArgumentException("n0 out of range 0..15");
547         }
548 
549         if ((n1 & 0xf) != n1) {
550             throw new IllegalArgumentException("n1 out of range 0..15");
551         }
552 
553         if ((n2 & 0xf) != n2) {
554             throw new IllegalArgumentException("n2 out of range 0..15");
555         }
556 
557         if ((n3 & 0xf) != n3) {
558             throw new IllegalArgumentException("n3 out of range 0..15");
559         }
560 
561         return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12));
562     }
563 
564     /**
565      * Helper method to combine two nibbles into a byte.
566      *
567      * @param low {@code 0..15;} low nibble
568      * @param high {@code 0..15;} high nibble
569      * @return {@code 0..255;} combined value
570      */
makeByte(int low, int high)571     protected static int makeByte(int low, int high) {
572         if ((low & 0xf) != low) {
573             throw new IllegalArgumentException("low out of range 0..15");
574         }
575 
576         if ((high & 0xf) != high) {
577             throw new IllegalArgumentException("high out of range 0..15");
578         }
579 
580         return low | (high << 4);
581     }
582 
583     /**
584      * Writes one code unit to the given output destination.
585      *
586      * @param out {@code non-null;} where to write to
587      * @param c0 code unit to write
588      */
write(AnnotatedOutput out, short c0)589     protected static void write(AnnotatedOutput out, short c0) {
590         out.writeShort(c0);
591     }
592 
593     /**
594      * Writes two code units to the given output destination.
595      *
596      * @param out {@code non-null;} where to write to
597      * @param c0 code unit to write
598      * @param c1 code unit to write
599      */
write(AnnotatedOutput out, short c0, short c1)600     protected static void write(AnnotatedOutput out, short c0, short c1) {
601         out.writeShort(c0);
602         out.writeShort(c1);
603     }
604 
605     /**
606      * Writes three code units to the given output destination.
607      *
608      * @param out {@code non-null;} where to write to
609      * @param c0 code unit to write
610      * @param c1 code unit to write
611      * @param c2 code unit to write
612      */
write(AnnotatedOutput out, short c0, short c1, short c2)613     protected static void write(AnnotatedOutput out, short c0, short c1,
614             short c2) {
615         out.writeShort(c0);
616         out.writeShort(c1);
617         out.writeShort(c2);
618     }
619 
620     /**
621      * Writes four 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      */
write(AnnotatedOutput out, short c0, short c1, short c2, short c3)629     protected static void write(AnnotatedOutput out, short c0, short c1,
630             short c2, short c3) {
631         out.writeShort(c0);
632         out.writeShort(c1);
633         out.writeShort(c2);
634         out.writeShort(c3);
635     }
636 
637     /**
638      * Writes five code units to the given output destination.
639      *
640      * @param out {@code non-null;} where to write to
641      * @param c0 code unit to write
642      * @param c1 code unit to write
643      * @param c2 code unit to write
644      * @param c3 code unit to write
645      * @param c4 code unit to write
646      */
write(AnnotatedOutput out, short c0, short c1, short c2, short c3, short c4)647     protected static void write(AnnotatedOutput out, short c0, short c1,
648             short c2, short c3, short c4) {
649         out.writeShort(c0);
650         out.writeShort(c1);
651         out.writeShort(c2);
652         out.writeShort(c3);
653         out.writeShort(c4);
654     }
655 
656     /**
657      * Writes three code units to the given output destination, where the
658      * second and third are represented as single <code>int</code> and emitted
659      * in little-endian order.
660      *
661      * @param out {@code non-null;} where to write to
662      * @param c0 code unit to write
663      * @param c1c2 code unit pair to write
664      */
write(AnnotatedOutput out, short c0, int c1c2)665     protected static void write(AnnotatedOutput out, short c0, int c1c2) {
666         write(out, c0, (short) c1c2, (short) (c1c2 >> 16));
667     }
668 
669     /**
670      * Writes four code units to the given output destination, where the
671      * second and third are represented as single <code>int</code> and emitted
672      * in little-endian order.
673      *
674      * @param out {@code non-null;} where to write to
675      * @param c0 code unit to write
676      * @param c1c2 code unit pair to write
677      * @param c3 code unit to write
678      */
write(AnnotatedOutput out, short c0, int c1c2, short c3)679     protected static void write(AnnotatedOutput out, short c0, int c1c2,
680             short c3) {
681         write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3);
682     }
683 
684     /**
685      * Writes five code units to the given output destination, where the
686      * second and third are represented as single <code>int</code> and emitted
687      * in little-endian order.
688      *
689      * @param out {@code non-null;} where to write to
690      * @param c0 code unit to write
691      * @param c1c2 code unit pair to write
692      * @param c3 code unit to write
693      * @param c4 code unit to write
694      */
write(AnnotatedOutput out, short c0, int c1c2, short c3, short c4)695     protected static void write(AnnotatedOutput out, short c0, int c1c2,
696             short c3, short c4) {
697         write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3, c4);
698     }
699 
700     /**
701      * Writes five code units to the given output destination, where the
702      * second through fifth are represented as single <code>long</code>
703      * and emitted in little-endian order.
704      *
705      * @param out {@code non-null;} where to write to
706      * @param c0 code unit to write
707      * @param c1c2c3c4 code unit quad to write
708      */
write(AnnotatedOutput out, short c0, long c1c2c3c4)709     protected static void write(AnnotatedOutput out, short c0, long c1c2c3c4) {
710         write(out, c0, (short) c1c2c3c4, (short) (c1c2c3c4 >> 16),
711                 (short) (c1c2c3c4 >> 32), (short) (c1c2c3c4 >> 48));
712     }
713 }
714