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
26import java.util.BitSet;
27
28/**
29 * Base class for Dalvik instructions.
30 */
31public 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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
449    protected abstract String listingString0(boolean noteIndices);
450}
451