1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dexgen.dex.code;
18
19import com.android.dexgen.rop.code.RegisterSpecList;
20import com.android.dexgen.rop.cst.Constant;
21import com.android.dexgen.rop.cst.CstInteger;
22import com.android.dexgen.rop.cst.CstKnownNull;
23import com.android.dexgen.rop.cst.CstLiteral64;
24import com.android.dexgen.rop.cst.CstLiteralBits;
25import com.android.dexgen.util.AnnotatedOutput;
26import 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 */
34public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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