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.dx.dex.code;
18
19import com.android.dx.rop.code.RegisterSpec;
20import com.android.dx.rop.code.RegisterSpecList;
21import com.android.dx.rop.code.SourcePosition;
22import com.android.dx.util.AnnotatedOutput;
23import com.android.dx.util.Hex;
24import com.android.dx.util.TwoColumnOutput;
25
26/**
27 * Base class for Dalvik instructions.
28 */
29public abstract class DalvInsn {
30    /**
31     * the actual output address of this instance, if known, or
32     * <code>-1</code> 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    /** non-null; source position */
40    private final SourcePosition position;
41
42    /** 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 non-null; source position information
49     * @param dest non-null; destination register
50     * @param src non-null; source register
51     * @return non-null; an appropriately-constructed instance
52     */
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</code>).
79     *
80     * <p><b>Note:</b> In the unlikely event that an instruction takes
81     * absolutely no registers (e.g., a <code>nop</code> 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 non-null; source position
88     * @param registers non-null; register list, including a
89     * result register if appropriate (that is, registers may be either
90     * ins and outs)
91     */
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
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     */
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</code> if it has not yet been set.
155     *
156     * @see #setAddress
157     *
158     * @return &gt;= 0; the output address
159     */
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 non-null; the opcode
172     */
173    public final Dop getOpcode() {
174        return opcode;
175    }
176
177    /**
178     * Gets the source position.
179     *
180     * @return non-null; the source position
181     */
182    public final SourcePosition getPosition() {
183        return position;
184    }
185
186    /**
187     * Gets the register list for this instruction.
188     *
189     * @return non-null; the registers
190     */
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()</code>.
199     *
200     * @return <code>true</code> iff this opcode uses a result register
201     */
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 &gt;= 0; the minimum distinct register requirement
214     */
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 null-ok; the prefix, if any
235     */
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 null-ok; the suffix, if any
259     */
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</code> (storing
272     * the result, if any, in register <code>0</code> 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 non-null; the replacement
281     */
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 non-null; the identifier
293     */
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</code>
305     * if this instance should not appear in a listing.
306     *
307     * @param prefix non-null; prefix before the address; each follow-on
308     * line will be indented to match as well
309     * @param width &gt;= 0; the width of the output or <code>0</code> for
310     * unlimited width
311     * @param noteIndices whether to include an explicit notation of
312     * constant pool indices
313     * @return null-ok; the string form or <code>null</code> if this
314     * instance should not appear in a listing
315     */
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 &gt;= 0; the output address
335     */
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 &gt;= 0; the next address
351     */
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 &gt;= 0; the code size of this instruction
360     */
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 non-null; where to write to
368     */
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 non-null; the new opcode
376     * @return non-null; an appropriately-constructed instance
377     */
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 non-null; an appropriately-constructed instance
387     */
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 non-null; new register list
396     * @return non-null; an appropriately-constructed instance
397     */
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 null-ok; the string version of any arguments or
405     * <code>null</code> if there are none
406     */
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</code> 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 null-ok; the listing string
420     */
421    protected abstract String listingString0(boolean noteIndices);
422}
423