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.CstInteger;
22 import com.android.dx.rop.cst.CstKnownNull;
23 import com.android.dx.rop.cst.CstLiteral64;
24 import com.android.dx.rop.cst.CstLiteralBits;
25 import com.android.dx.util.AnnotatedOutput;
26 import com.android.dx.util.Hex;
27 import java.util.BitSet;
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  */
35 public 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 final boolean ALLOW_EXTENDED_OPCODES = true;
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      */
listingString(DalvInsn insn, boolean noteIndices)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      */
insnArgString(DalvInsn insn)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      */
insnCommentString(DalvInsn insn, boolean noteIndices)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      */
codeSize()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      */
isCompatible(DalvInsn insn)127     public abstract boolean isCompatible(DalvInsn insn);
128 
129     /**
130      * Returns which of a given instruction's registers will fit in
131      * this instance's format.
132      *
133      * <p>The default implementation of this method always returns
134      * an empty BitSet. Subclasses must override this method if they
135      * have registers.</p>
136      *
137      * @param insn {@code non-null;} the instruction to check
138      * @return {@code non-null;} a BitSet flagging registers in the
139      * register list that are compatible to this format
140      */
compatibleRegs(DalvInsn insn)141     public BitSet compatibleRegs(DalvInsn insn) {
142         return new BitSet();
143     }
144 
145     /**
146      * Returns whether or not the given instruction's branch offset will
147      * fit in this instance's format. This always returns {@code false}
148      * for formats that don't include a branch offset.
149      *
150      * <p>The default implementation of this method always returns
151      * {@code false}. Subclasses must override this method if they
152      * include branch offsets.</p>
153      *
154      * @param insn {@code non-null;} the instruction to check
155      * @return {@code true} iff the instruction's branch offset is
156      * appropriate for this instance, or {@code false} if not
157      */
branchFits(TargetInsn insn)158     public boolean branchFits(TargetInsn insn) {
159         return false;
160     }
161 
162     /**
163      * Writes the code units for the given instruction to the given
164      * output destination. The instruction must be of this instance's format.
165      *
166      * <p>Subclasses must override this method.</p>
167      *
168      * @param out {@code non-null;} the output destination to write to
169      * @param insn {@code non-null;} the instruction to write
170      */
writeTo(AnnotatedOutput out, DalvInsn insn)171     public abstract void writeTo(AnnotatedOutput out, DalvInsn insn);
172 
173     /**
174      * Helper method to return a register list string.
175      *
176      * @param list {@code non-null;} the list of registers
177      * @return {@code non-null;} the string form
178      */
regListString(RegisterSpecList list)179     protected static String regListString(RegisterSpecList list) {
180         int sz = list.size();
181         StringBuilder sb = new StringBuilder(sz * 5 + 2);
182 
183         sb.append('{');
184 
185         for (int i = 0; i < sz; i++) {
186             if (i != 0) {
187                 sb.append(", ");
188             }
189             sb.append(list.get(i).regString());
190         }
191 
192         sb.append('}');
193 
194         return sb.toString();
195     }
196 
197     /**
198      * Helper method to return a register range string.
199      *
200      * @param list {@code non-null;} the list of registers (which must be
201      * sequential)
202      * @return {@code non-null;} the string form
203      */
regRangeString(RegisterSpecList list)204     protected static String regRangeString(RegisterSpecList list) {
205         int size = list.size();
206         StringBuilder sb = new StringBuilder(30);
207 
208         sb.append("{");
209 
210         switch (size) {
211             case 0: {
212                 // Nothing to do.
213                 break;
214             }
215             case 1: {
216                 sb.append(list.get(0).regString());
217                 break;
218             }
219             default: {
220                 RegisterSpec lastReg = list.get(size - 1);
221                 if (lastReg.getCategory() == 2) {
222                     /*
223                      * Add one to properly represent a list-final
224                      * category-2 register.
225                      */
226                     lastReg = lastReg.withOffset(1);
227                 }
228 
229                 sb.append(list.get(0).regString());
230                 sb.append("..");
231                 sb.append(lastReg.regString());
232             }
233         }
234 
235         sb.append("}");
236 
237         return sb.toString();
238     }
239 
240     /**
241      * Helper method to return a literal bits argument string.
242      *
243      * @param value the value
244      * @return {@code non-null;} the string form
245      */
literalBitsString(CstLiteralBits value)246     protected static String literalBitsString(CstLiteralBits value) {
247         StringBuilder sb = new StringBuilder(100);
248 
249         sb.append('#');
250 
251         if (value instanceof CstKnownNull) {
252             sb.append("null");
253         } else {
254             sb.append(value.typeName());
255             sb.append(' ');
256             sb.append(value.toHuman());
257         }
258 
259         return sb.toString();
260     }
261 
262     /**
263      * Helper method to return a literal bits comment string.
264      *
265      * @param value the value
266      * @param width the width of the constant, in bits (used for displaying
267      * the uninterpreted bits; one of: {@code 4 8 16 32 64}
268      * @return {@code non-null;} the comment
269      */
literalBitsComment(CstLiteralBits value, int width)270     protected static String literalBitsComment(CstLiteralBits value,
271             int width) {
272         StringBuilder sb = new StringBuilder(20);
273 
274         sb.append("#");
275 
276         long bits;
277 
278         if (value instanceof CstLiteral64) {
279             bits = ((CstLiteral64) value).getLongBits();
280         } else {
281             bits = value.getIntBits();
282         }
283 
284         switch (width) {
285             case 4:  sb.append(Hex.uNibble((int) bits)); break;
286             case 8:  sb.append(Hex.u1((int) bits));      break;
287             case 16: sb.append(Hex.u2((int) bits));      break;
288             case 32: sb.append(Hex.u4((int) bits));      break;
289             case 64: sb.append(Hex.u8(bits));            break;
290             default: {
291                 throw new RuntimeException("shouldn't happen");
292             }
293         }
294 
295         return sb.toString();
296     }
297 
298     /**
299      * Helper method to return a branch address string.
300      *
301      * @param insn {@code non-null;} the instruction in question
302      * @return {@code non-null;} the string form of the instruction's
303      * branch target
304      */
branchString(DalvInsn insn)305     protected static String branchString(DalvInsn insn) {
306         TargetInsn ti = (TargetInsn) insn;
307         int address = ti.getTargetAddress();
308 
309         return (address == (char) address) ? Hex.u2(address) : Hex.u4(address);
310     }
311 
312     /**
313      * Helper method to return the comment for a branch.
314      *
315      * @param insn {@code non-null;} the instruction in question
316      * @return {@code non-null;} the comment
317      */
branchComment(DalvInsn insn)318     protected static String branchComment(DalvInsn insn) {
319         TargetInsn ti = (TargetInsn) insn;
320         int offset = ti.getTargetOffset();
321 
322         return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset);
323     }
324 
325     /**
326      * Helper method to determine if a signed int value fits in a nibble.
327      *
328      * @param value the value in question
329      * @return {@code true} iff it's in the range -8..+7
330      */
signedFitsInNibble(int value)331     protected static boolean signedFitsInNibble(int value) {
332         return (value >= -8) && (value <= 7);
333     }
334 
335     /**
336      * Helper method to determine if an unsigned int value fits in a nibble.
337      *
338      * @param value the value in question
339      * @return {@code true} iff it's in the range 0..0xf
340      */
unsignedFitsInNibble(int value)341     protected static boolean unsignedFitsInNibble(int value) {
342         return value == (value & 0xf);
343     }
344 
345     /**
346      * Helper method to determine if a signed int value fits in a byte.
347      *
348      * @param value the value in question
349      * @return {@code true} iff it's in the range -0x80..+0x7f
350      */
signedFitsInByte(int value)351     protected static boolean signedFitsInByte(int value) {
352         return (byte) value == value;
353     }
354 
355     /**
356      * Helper method to determine if an unsigned int value fits in a byte.
357      *
358      * @param value the value in question
359      * @return {@code true} iff it's in the range 0..0xff
360      */
unsignedFitsInByte(int value)361     protected static boolean unsignedFitsInByte(int value) {
362         return value == (value & 0xff);
363     }
364 
365     /**
366      * Helper method to determine if a signed int value fits in a short.
367      *
368      * @param value the value in question
369      * @return {@code true} iff it's in the range -0x8000..+0x7fff
370      */
signedFitsInShort(int value)371     protected static boolean signedFitsInShort(int value) {
372         return (short) value == value;
373     }
374 
375     /**
376      * Helper method to determine if an unsigned int value fits in a short.
377      *
378      * @param value the value in question
379      * @return {@code true} iff it's in the range 0..0xffff
380      */
unsignedFitsInShort(int value)381     protected static boolean unsignedFitsInShort(int value) {
382         return value == (value & 0xffff);
383     }
384 
385     /**
386      * Helper method to determine if a list of registers are sequential,
387      * including degenerate cases for empty or single-element lists.
388      *
389      * @param list {@code non-null;} the list of registers
390      * @return {@code true} iff the list is sequentially ordered
391      */
isRegListSequential(RegisterSpecList list)392     protected static boolean isRegListSequential(RegisterSpecList list) {
393         int sz = list.size();
394 
395         if (sz < 2) {
396             return true;
397         }
398 
399         int first = list.get(0).getReg();
400         int next = first;
401 
402         for (int i = 0; i < sz; i++) {
403             RegisterSpec one = list.get(i);
404             if (one.getReg() != next) {
405                 return false;
406             }
407             next += one.getCategory();
408         }
409 
410         return true;
411     }
412 
413     /**
414      * Helper method to extract the callout-argument index from an
415      * appropriate instruction.
416      *
417      * @param insn {@code non-null;} the instruction
418      * @return {@code >= 0;} the callout argument index
419      */
argIndex(DalvInsn insn)420     protected static int argIndex(DalvInsn insn) {
421         int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue();
422 
423         if (arg < 0) {
424             throw new IllegalArgumentException("bogus insn");
425         }
426 
427         return arg;
428     }
429 
430     /**
431      * Helper method to combine an opcode and a second byte of data into
432      * the appropriate form for emitting into a code buffer.
433      *
434      * @param insn {@code non-null;} the instruction containing the opcode
435      * @param arg {@code 0..255;} arbitrary other byte value
436      * @return combined value
437      */
opcodeUnit(DalvInsn insn, int arg)438     protected static short opcodeUnit(DalvInsn insn, int arg) {
439         if ((arg & 0xff) != arg) {
440             throw new IllegalArgumentException("arg out of range 0..255");
441         }
442 
443         int opcode = insn.getOpcode().getOpcode();
444 
445         if ((opcode & 0xff) != opcode) {
446             throw new IllegalArgumentException("opcode out of range 0..255");
447         }
448 
449         return (short) (opcode | (arg << 8));
450     }
451 
452     /**
453      * Helper method to get an extended (16-bit) opcode out of an
454      * instruction, returning it as a code unit. The opcode
455      * <i>must</i> be an extended opcode.
456      *
457      * @param insn {@code non-null;} the instruction containing the
458      * extended opcode
459      * @return the opcode as a code unit
460      */
opcodeUnit(DalvInsn insn)461     protected static short opcodeUnit(DalvInsn insn) {
462         int opcode = insn.getOpcode().getOpcode();
463 
464         if ((opcode < 0x100) || (opcode > 0xffff)) {
465             throw new IllegalArgumentException("opcode out of range 0..65535");
466         }
467 
468         return (short) opcode;
469     }
470 
471     /**
472      * Helper method to combine two bytes into a code unit.
473      *
474      * @param low {@code 0..255;} low byte
475      * @param high {@code 0..255;} high byte
476      * @return combined value
477      */
codeUnit(int low, int high)478     protected static short codeUnit(int low, int high) {
479         if ((low & 0xff) != low) {
480             throw new IllegalArgumentException("low out of range 0..255");
481         }
482 
483         if ((high & 0xff) != high) {
484             throw new IllegalArgumentException("high out of range 0..255");
485         }
486 
487         return (short) (low | (high << 8));
488     }
489 
490     /**
491      * Helper method to combine four nibbles into a code unit.
492      *
493      * @param n0 {@code 0..15;} low nibble
494      * @param n1 {@code 0..15;} medium-low nibble
495      * @param n2 {@code 0..15;} medium-high nibble
496      * @param n3 {@code 0..15;} high nibble
497      * @return combined value
498      */
codeUnit(int n0, int n1, int n2, int n3)499     protected static short codeUnit(int n0, int n1, int n2, int n3) {
500         if ((n0 & 0xf) != n0) {
501             throw new IllegalArgumentException("n0 out of range 0..15");
502         }
503 
504         if ((n1 & 0xf) != n1) {
505             throw new IllegalArgumentException("n1 out of range 0..15");
506         }
507 
508         if ((n2 & 0xf) != n2) {
509             throw new IllegalArgumentException("n2 out of range 0..15");
510         }
511 
512         if ((n3 & 0xf) != n3) {
513             throw new IllegalArgumentException("n3 out of range 0..15");
514         }
515 
516         return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12));
517     }
518 
519     /**
520      * Helper method to combine two nibbles into a byte.
521      *
522      * @param low {@code 0..15;} low nibble
523      * @param high {@code 0..15;} high nibble
524      * @return {@code 0..255;} combined value
525      */
makeByte(int low, int high)526     protected static int makeByte(int low, int high) {
527         if ((low & 0xf) != low) {
528             throw new IllegalArgumentException("low out of range 0..15");
529         }
530 
531         if ((high & 0xf) != high) {
532             throw new IllegalArgumentException("high out of range 0..15");
533         }
534 
535         return low | (high << 4);
536     }
537 
538     /**
539      * Writes one code unit to the given output destination.
540      *
541      * @param out {@code non-null;} where to write to
542      * @param c0 code unit to write
543      */
write(AnnotatedOutput out, short c0)544     protected static void write(AnnotatedOutput out, short c0) {
545         out.writeShort(c0);
546     }
547 
548     /**
549      * Writes two code units to the given output destination.
550      *
551      * @param out {@code non-null;} where to write to
552      * @param c0 code unit to write
553      * @param c1 code unit to write
554      */
write(AnnotatedOutput out, short c0, short c1)555     protected static void write(AnnotatedOutput out, short c0, short c1) {
556         out.writeShort(c0);
557         out.writeShort(c1);
558     }
559 
560     /**
561      * Writes three code units to the given output destination.
562      *
563      * @param out {@code non-null;} where to write to
564      * @param c0 code unit to write
565      * @param c1 code unit to write
566      * @param c2 code unit to write
567      */
write(AnnotatedOutput out, short c0, short c1, short c2)568     protected static void write(AnnotatedOutput out, short c0, short c1,
569             short c2) {
570         out.writeShort(c0);
571         out.writeShort(c1);
572         out.writeShort(c2);
573     }
574 
575     /**
576      * Writes four code units to the given output destination.
577      *
578      * @param out {@code non-null;} where to write to
579      * @param c0 code unit to write
580      * @param c1 code unit to write
581      * @param c2 code unit to write
582      * @param c3 code unit to write
583      */
write(AnnotatedOutput out, short c0, short c1, short c2, short c3)584     protected static void write(AnnotatedOutput out, short c0, short c1,
585             short c2, short c3) {
586         out.writeShort(c0);
587         out.writeShort(c1);
588         out.writeShort(c2);
589         out.writeShort(c3);
590     }
591 
592     /**
593      * Writes five code units to the given output destination.
594      *
595      * @param out {@code non-null;} where to write to
596      * @param c0 code unit to write
597      * @param c1 code unit to write
598      * @param c2 code unit to write
599      * @param c3 code unit to write
600      * @param c4 code unit to write
601      */
write(AnnotatedOutput out, short c0, short c1, short c2, short c3, short c4)602     protected static void write(AnnotatedOutput out, short c0, short c1,
603             short c2, short c3, short c4) {
604         out.writeShort(c0);
605         out.writeShort(c1);
606         out.writeShort(c2);
607         out.writeShort(c3);
608         out.writeShort(c4);
609     }
610 
611     /**
612      * Writes three code units to the given output destination, where the
613      * second and third are represented as single <code>int</code> and emitted
614      * in little-endian order.
615      *
616      * @param out {@code non-null;} where to write to
617      * @param c0 code unit to write
618      * @param c1c2 code unit pair to write
619      */
write(AnnotatedOutput out, short c0, int c1c2)620     protected static void write(AnnotatedOutput out, short c0, int c1c2) {
621         write(out, c0, (short) c1c2, (short) (c1c2 >> 16));
622     }
623 
624     /**
625      * Writes four code units to the given output destination, where the
626      * second and third are represented as single <code>int</code> and emitted
627      * in little-endian order.
628      *
629      * @param out {@code non-null;} where to write to
630      * @param c0 code unit to write
631      * @param c1c2 code unit pair to write
632      * @param c3 code unit to write
633      */
write(AnnotatedOutput out, short c0, int c1c2, short c3)634     protected static void write(AnnotatedOutput out, short c0, int c1c2,
635             short c3) {
636         write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3);
637     }
638 
639     /**
640      * Writes five 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      * @param c3 code unit to write
648      * @param c4 code unit to write
649      */
write(AnnotatedOutput out, short c0, int c1c2, short c3, short c4)650     protected static void write(AnnotatedOutput out, short c0, int c1c2,
651             short c3, short c4) {
652         write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3, c4);
653     }
654 
655     /**
656      * Writes five code units to the given output destination, where the
657      * second through fifth are represented as single <code>long</code>
658      * and emitted in little-endian order.
659      *
660      * @param out {@code non-null;} where to write to
661      * @param c0 code unit to write
662      * @param c1c2c3c4 code unit quad to write
663      */
write(AnnotatedOutput out, short c0, long c1c2c3c4)664     protected static void write(AnnotatedOutput out, short c0, long c1c2c3c4) {
665         write(out, c0, (short) c1c2c3c4, (short) (c1c2c3c4 >> 16),
666                 (short) (c1c2c3c4 >> 32), (short) (c1c2c3c4 >> 48));
667     }
668 }
669