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