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