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.code.SourcePosition;
22 import com.android.dx.util.AnnotatedOutput;
23 import com.android.dx.util.Hex;
24 import com.android.dx.util.TwoColumnOutput;
25 
26 import java.util.BitSet;
27 
28 /**
29  * Base class for Dalvik instructions.
30  */
31 public abstract class DalvInsn {
32     /**
33      * the actual output address of this instance, if known, or
34      * {@code -1} if not
35      */
36     private int address;
37 
38     /** the opcode; one of the constants from {@link Dops} */
39     private final Dop opcode;
40 
41     /** {@code non-null;} source position */
42     private final SourcePosition position;
43 
44     /** {@code non-null;} list of register arguments */
45     private final RegisterSpecList registers;
46 
47     /**
48      * Makes a move instruction, appropriate and ideal for the given arguments.
49      *
50      * @param position {@code non-null;} source position information
51      * @param dest {@code non-null;} destination register
52      * @param src {@code non-null;} source register
53      * @return {@code non-null;} an appropriately-constructed instance
54      */
makeMove(SourcePosition position, RegisterSpec dest, RegisterSpec src)55     public static SimpleInsn makeMove(SourcePosition position,
56             RegisterSpec dest, RegisterSpec src) {
57         boolean category1 = dest.getCategory() == 1;
58         boolean reference = dest.getType().isReference();
59         int destReg = dest.getReg();
60         int srcReg = src.getReg();
61         Dop opcode;
62 
63         if ((srcReg | destReg) < 16) {
64             opcode = reference ? Dops.MOVE_OBJECT :
65                 (category1 ? Dops.MOVE : Dops.MOVE_WIDE);
66         } else if (destReg < 256) {
67             opcode = reference ? Dops.MOVE_OBJECT_FROM16 :
68                 (category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16);
69         } else {
70             opcode = reference ? Dops.MOVE_OBJECT_16 :
71                 (category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16);
72         }
73 
74         return new SimpleInsn(opcode, position,
75                               RegisterSpecList.make(dest, src));
76     }
77 
78     /**
79      * Constructs an instance. The output address of this instance is initially
80      * unknown ({@code -1}).
81      *
82      * <p><b>Note:</b> In the unlikely event that an instruction takes
83      * absolutely no registers (e.g., a {@code nop} or a
84      * no-argument no-result static method call), then the given
85      * register list may be passed as {@link
86      * RegisterSpecList#EMPTY}.</p>
87      *
88      * @param opcode the opcode; one of the constants from {@link Dops}
89      * @param position {@code non-null;} source position
90      * @param registers {@code non-null;} register list, including a
91      * result register if appropriate (that is, registers may be either
92      * ins and outs)
93      */
DalvInsn(Dop opcode, SourcePosition position, RegisterSpecList registers)94     public DalvInsn(Dop opcode, SourcePosition position,
95                     RegisterSpecList registers) {
96         if (opcode == null) {
97             throw new NullPointerException("opcode == null");
98         }
99 
100         if (position == null) {
101             throw new NullPointerException("position == null");
102         }
103 
104         if (registers == null) {
105             throw new NullPointerException("registers == null");
106         }
107 
108         this.address = -1;
109         this.opcode = opcode;
110         this.position = position;
111         this.registers = registers;
112     }
113 
114     /** {@inheritDoc} */
115     @Override
toString()116     public final String toString() {
117         StringBuffer sb = new StringBuffer(100);
118 
119         sb.append(identifierString());
120         sb.append(' ');
121         sb.append(position);
122 
123         sb.append(": ");
124         sb.append(opcode.getName());
125 
126         boolean needComma = false;
127         if (registers.size() != 0) {
128             sb.append(registers.toHuman(" ", ", ", null));
129             needComma = true;
130         }
131 
132         String extra = argString();
133         if (extra != null) {
134             if (needComma) {
135                 sb.append(',');
136             }
137             sb.append(' ');
138             sb.append(extra);
139         }
140 
141         return sb.toString();
142     }
143 
144     /**
145      * Gets whether the address of this instruction is known.
146      *
147      * @see #getAddress
148      * @see #setAddress
149      */
hasAddress()150     public final boolean hasAddress() {
151         return (address >= 0);
152     }
153 
154     /**
155      * Gets the output address of this instruction, if it is known. This throws
156      * a {@code RuntimeException} if it has not yet been set.
157      *
158      * @see #setAddress
159      *
160      * @return {@code >= 0;} the output address
161      */
getAddress()162     public final int getAddress() {
163         if (address < 0) {
164             throw new RuntimeException("address not yet known");
165         }
166 
167         return address;
168     }
169 
170     /**
171      * Gets the opcode.
172      *
173      * @return {@code non-null;} the opcode
174      */
getOpcode()175     public final Dop getOpcode() {
176         return opcode;
177     }
178 
179     /**
180      * Gets the source position.
181      *
182      * @return {@code non-null;} the source position
183      */
getPosition()184     public final SourcePosition getPosition() {
185         return position;
186     }
187 
188     /**
189      * Gets the register list for this instruction.
190      *
191      * @return {@code non-null;} the registers
192      */
getRegisters()193     public final RegisterSpecList getRegisters() {
194         return registers;
195     }
196 
197     /**
198      * Returns whether this instance's opcode uses a result register.
199      * This method is a convenient shorthand for
200      * {@code getOpcode().hasResult()}.
201      *
202      * @return {@code true} iff this opcode uses a result register
203      */
hasResult()204     public final boolean hasResult() {
205         return opcode.hasResult();
206     }
207 
208     /**
209      * Gets the minimum distinct registers required for this instruction.
210      * Uses the given BitSet to determine which registers require
211      * replacement, and ignores registers that are already compatible.
212      * This assumes that the result (if any) can share registers with the
213      * sources (if any), that each source register is unique, and that
214      * (to be explicit here) category-2 values take up two consecutive
215      * registers.
216      *
217      * @param compatRegs {@code non-null;} set of compatible registers
218      * @return {@code >= 0;} the minimum distinct register requirement
219      */
getMinimumRegisterRequirement(BitSet compatRegs)220     public final int getMinimumRegisterRequirement(BitSet compatRegs) {
221         boolean hasResult = hasResult();
222         int regSz = registers.size();
223         int resultRequirement = 0;
224         int sourceRequirement = 0;
225 
226         if (hasResult && !compatRegs.get(0)) {
227             resultRequirement = registers.get(0).getCategory();
228         }
229 
230         for (int i = hasResult ? 1 : 0; i < regSz; i++) {
231             if (!compatRegs.get(i)) {
232                 sourceRequirement += registers.get(i).getCategory();
233             }
234         }
235 
236         return Math.max(sourceRequirement, resultRequirement);
237     }
238 
239     /**
240      * Gets the instruction that is equivalent to this one, except that
241      * it uses sequential registers starting at {@code 0} (storing
242      * the result, if any, in register {@code 0} as well).
243      *
244      * @return {@code non-null;} the replacement
245      */
getLowRegVersion()246     public DalvInsn getLowRegVersion() {
247         RegisterSpecList regs =
248             registers.withExpandedRegisters(0, hasResult(), null);
249         return withRegisters(regs);
250     }
251 
252     /**
253      * Gets the instruction prefix required, if any, to use in an expanded
254      * version of this instance. Will not generate moves for registers
255      * marked compatible to the format by the given BitSet.
256      *
257      * @see #expandedVersion
258      *
259      * @param compatRegs {@code non-null;} set of compatible registers
260      * @return {@code null-ok;} the prefix, if any
261      */
expandedPrefix(BitSet compatRegs)262     public DalvInsn expandedPrefix(BitSet compatRegs) {
263         RegisterSpecList regs = registers;
264         boolean firstBit = compatRegs.get(0);
265 
266         if (hasResult()) compatRegs.set(0);
267 
268         regs = regs.subset(compatRegs);
269 
270         if (hasResult()) compatRegs.set(0, firstBit);
271 
272         if (regs.size() == 0) return null;
273 
274         return new HighRegisterPrefix(position, regs);
275     }
276 
277     /**
278      * Gets the instruction suffix required, if any, to use in an expanded
279      * version of this instance. Will not generate a move for a register
280      * marked compatible to the format by the given BitSet.
281      *
282      * @see #expandedVersion
283      *
284      * @param compatRegs {@code non-null;} set of compatible registers
285      * @return {@code null-ok;} the suffix, if any
286      */
expandedSuffix(BitSet compatRegs)287     public DalvInsn expandedSuffix(BitSet compatRegs) {
288         if (hasResult() && !compatRegs.get(0)) {
289             RegisterSpec r = registers.get(0);
290             return makeMove(position, r, r.withReg(0));
291         } else {
292             return null;
293         }
294     }
295 
296     /**
297      * Gets the instruction that is equivalent to this one, except that
298      * it replaces incompatible registers with sequential registers
299      * starting at {@code 0} (storing the result, if any, in register
300      * {@code 0} as well). The sequence of instructions from
301      * {@link #expandedPrefix} and {@link #expandedSuffix} (if non-null)
302      * surrounding the result of a call to this method are the expanded
303      * transformation of this instance, and it is guaranteed that the
304      * number of low registers used will be the number returned by
305      * {@link #getMinimumRegisterRequirement}.
306      *
307      * @param compatRegs {@code non-null;} set of compatible registers
308      * @return {@code non-null;} the replacement
309      */
expandedVersion(BitSet compatRegs)310     public DalvInsn expandedVersion(BitSet compatRegs) {
311         RegisterSpecList regs =
312             registers.withExpandedRegisters(0, hasResult(), compatRegs);
313         return withRegisters(regs);
314     }
315 
316     /**
317      * Gets the short identifier for this instruction. This is its
318      * address, if assigned, or its identity hashcode if not.
319      *
320      * @return {@code non-null;} the identifier
321      */
identifierString()322     public final String identifierString() {
323         if (address != -1) {
324             return String.format("%04x", address);
325         }
326 
327         return Hex.u4(System.identityHashCode(this));
328     }
329 
330     /**
331      * Returns the string form of this instance suitable for inclusion in
332      * a human-oriented listing dump. This method will return {@code null}
333      * if this instance should not appear in a listing.
334      *
335      * @param prefix {@code non-null;} prefix before the address; each follow-on
336      * line will be indented to match as well
337      * @param width {@code >= 0;} the width of the output or {@code 0} for
338      * unlimited width
339      * @param noteIndices whether to include an explicit notation of
340      * constant pool indices
341      * @return {@code null-ok;} the string form or {@code null} if this
342      * instance should not appear in a listing
343      */
listingString(String prefix, int width, boolean noteIndices)344     public final String listingString(String prefix, int width,
345             boolean noteIndices) {
346         String insnPerSe = listingString0(noteIndices);
347 
348         if (insnPerSe == null) {
349             return null;
350         }
351 
352         String addr = prefix + identifierString() + ": ";
353         int w1 = addr.length();
354         int w2 = (width == 0) ? insnPerSe.length() : (width - w1);
355 
356         return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2);
357     }
358 
359     /**
360      * Sets the output address.
361      *
362      * @param address {@code >= 0;} the output address
363      */
setAddress(int address)364     public final void setAddress(int address) {
365         if (address < 0) {
366             throw new IllegalArgumentException("address < 0");
367         }
368 
369         this.address = address;
370     }
371 
372     /**
373      * Gets the address immediately after this instance. This is only
374      * calculable if this instance's address is known, and it is equal
375      * to the address plus the length of the instruction format of this
376      * instance's opcode.
377      *
378      * @return {@code >= 0;} the next address
379      */
getNextAddress()380     public final int getNextAddress() {
381         return getAddress() + codeSize();
382     }
383 
384     /**
385      * Gets the size of this instruction, in 16-bit code units.
386      *
387      * @return {@code >= 0;} the code size of this instruction
388      */
codeSize()389     public abstract int codeSize();
390 
391     /**
392      * Writes this instance to the given output. This method should
393      * never annotate the output.
394      *
395      * @param out {@code non-null;} where to write to
396      */
writeTo(AnnotatedOutput out)397     public abstract void writeTo(AnnotatedOutput out);
398 
399     /**
400      * Returns an instance that is just like this one, except that its
401      * opcode is replaced by the one given, and its address is reset.
402      *
403      * @param opcode {@code non-null;} the new opcode
404      * @return {@code non-null;} an appropriately-constructed instance
405      */
withOpcode(Dop opcode)406     public abstract DalvInsn withOpcode(Dop opcode);
407 
408     /**
409      * Returns an instance that is just like this one, except that all
410      * register references have been offset by the given delta, and its
411      * address is reset.
412      *
413      * @param delta the amount to offset register references by
414      * @return {@code non-null;} an appropriately-constructed instance
415      */
withRegisterOffset(int delta)416     public abstract DalvInsn withRegisterOffset(int delta);
417 
418     /**
419      * Returns an instance that is just like this one, except that the
420      * register list is replaced by the given one, and its address is
421      * reset.
422      *
423      * @param registers {@code non-null;} new register list
424      * @return {@code non-null;} an appropriately-constructed instance
425      */
withRegisters(RegisterSpecList registers)426     public abstract DalvInsn withRegisters(RegisterSpecList registers);
427 
428     /**
429      * Gets the string form for any arguments to this instance. Subclasses
430      * must override this.
431      *
432      * @return {@code null-ok;} the string version of any arguments or
433      * {@code null} if there are none
434      */
argString()435     protected abstract String argString();
436 
437     /**
438      * Helper for {@link #listingString}, which returns the string
439      * form of this instance suitable for inclusion in a
440      * human-oriented listing dump, not including the instruction
441      * address and without respect for any output formatting. This
442      * method should return {@code null} if this instance should
443      * not appear in a listing.
444      *
445      * @param noteIndices whether to include an explicit notation of
446      * constant pool indices
447      * @return {@code null-ok;} the listing string
448      */
listingString0(boolean noteIndices)449     protected abstract String listingString0(boolean noteIndices);
450 }
451