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