InstructionCodec.java revision ab35b50311951feea3782151dd5422ee944685c2
1/*
2 * Copyright (C) 2011 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.io.instructions;
18
19import com.android.dx.io.IndexType;
20import com.android.dx.io.OpcodeInfo;
21import com.android.dx.io.Opcodes;
22import com.android.dx.util.DexException;
23import com.android.dx.util.Hex;
24
25import java.io.EOFException;
26
27/**
28 * Representation of an instruction format, which knows how to decode into
29 * and encode from instances of {@link DecodedInstruction}.
30 */
31public enum InstructionCodec {
32    FORMAT_00X() {
33        @Override public DecodedInstruction decode(int opcodeUnit,
34                CodeInput in) throws EOFException {
35            return new ZeroRegisterDecodedInstruction(
36                    this, opcodeUnit, 0, null,
37                    0, 0L);
38        }
39
40        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
41            out.write(insn.getOpcodeUnit());
42        }
43    },
44
45    FORMAT_10X() {
46        @Override public DecodedInstruction decode(int opcodeUnit,
47                CodeInput in) throws EOFException {
48            int opcode = byte0(opcodeUnit);
49            int literal = byte1(opcodeUnit); // should be zero
50            return new ZeroRegisterDecodedInstruction(
51                    this, opcode, 0, null,
52                    0, literal);
53        }
54
55        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
56            out.write(insn.getOpcodeUnit());
57        }
58    },
59
60    FORMAT_12X() {
61        @Override public DecodedInstruction decode(int opcodeUnit,
62                CodeInput in) throws EOFException {
63            int opcode = byte0(opcodeUnit);
64            int a = nibble2(opcodeUnit);
65            int b = nibble3(opcodeUnit);
66            return new TwoRegisterDecodedInstruction(
67                    this, opcode, 0, null,
68                    0, 0L,
69                    a, b);
70        }
71
72        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
73            out.write(
74                    codeUnit(insn.getOpcodeUnit(),
75                             makeByte(insn.getA(), insn.getB())));
76        }
77    },
78
79    FORMAT_11N() {
80        @Override public DecodedInstruction decode(int opcodeUnit,
81                CodeInput in) throws EOFException {
82            int opcode = byte0(opcodeUnit);
83            int a = nibble2(opcodeUnit);
84            int literal = (nibble3(opcodeUnit) << 28) >> 28; // sign-extend
85            return new OneRegisterDecodedInstruction(
86                    this, opcode, 0, null,
87                    0, literal,
88                    a);
89        }
90
91        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
92            out.write(
93                    codeUnit(insn.getOpcodeUnit(),
94                             makeByte(insn.getA(), insn.getLiteralNibble())));
95        }
96    },
97
98    FORMAT_11X() {
99        @Override public DecodedInstruction decode(int opcodeUnit,
100                CodeInput in) throws EOFException {
101            int opcode = byte0(opcodeUnit);
102            int a = byte1(opcodeUnit);
103            return new OneRegisterDecodedInstruction(
104                    this, opcode, 0, null,
105                    0, 0L,
106                    a);
107        }
108
109        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
110            out.write(codeUnit(insn.getOpcode(), insn.getA()));
111        }
112    },
113
114    FORMAT_10T() {
115        @Override public DecodedInstruction decode(int opcodeUnit,
116                CodeInput in) throws EOFException {
117            int baseAddress = in.cursor() - 1;
118            int opcode = byte0(opcodeUnit);
119            int target = (byte) byte1(opcodeUnit); // sign-extend
120            return new ZeroRegisterDecodedInstruction(
121                    this, opcode, 0, null,
122                    baseAddress + target, 0L);
123        }
124
125        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
126            int relativeTarget = insn.getTargetByte(out.cursor());
127            out.write(codeUnit(insn.getOpcode(), relativeTarget));
128        }
129    },
130
131    FORMAT_20T() {
132        @Override public DecodedInstruction decode(int opcodeUnit,
133                CodeInput in) throws EOFException {
134            int baseAddress = in.cursor() - 1;
135            int opcode = byte0(opcodeUnit);
136            int literal = byte1(opcodeUnit); // should be zero
137            int target = (short) in.read(); // sign-extend
138            return new ZeroRegisterDecodedInstruction(
139                    this, opcode, 0, null,
140                    baseAddress + target, literal);
141        }
142
143        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
144            short relativeTarget = insn.getTargetUnit(out.cursor());
145            out.write(insn.getOpcodeUnit(), relativeTarget);
146        }
147    },
148
149    FORMAT_20BC() {
150        @Override public DecodedInstruction decode(int opcodeUnit,
151                CodeInput in) throws EOFException {
152            // Note: We use the literal field to hold the decoded AA value.
153            int opcode = byte0(opcodeUnit);
154            int literal = byte1(opcodeUnit);
155            int index = in.read();
156            return new ZeroRegisterDecodedInstruction(
157                    this, opcode, index, IndexType.VARIES,
158                    0, literal);
159        }
160
161        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
162            out.write(
163                    codeUnit(insn.getOpcode(), insn.getLiteralByte()),
164                    insn.getIndexUnit());
165        }
166    },
167
168    FORMAT_22X() {
169        @Override public DecodedInstruction decode(int opcodeUnit,
170                CodeInput in) throws EOFException {
171            int opcode = byte0(opcodeUnit);
172            int a = byte1(opcodeUnit);
173            int b = in.read();
174            return new TwoRegisterDecodedInstruction(
175                    this, opcode, 0, null,
176                    0, 0L,
177                    a, b);
178        }
179
180        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
181            out.write(
182                    codeUnit(insn.getOpcode(), insn.getA()),
183                    insn.getBUnit());
184        }
185    },
186
187    FORMAT_21T() {
188        @Override public DecodedInstruction decode(int opcodeUnit,
189                CodeInput in) throws EOFException {
190            int baseAddress = in.cursor() - 1;
191            int opcode = byte0(opcodeUnit);
192            int a = byte1(opcodeUnit);
193            int target = (short) in.read(); // sign-extend
194            return new OneRegisterDecodedInstruction(
195                    this, opcode, 0, null,
196                    baseAddress + target, 0L,
197                    a);
198        }
199
200        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
201            short relativeTarget = insn.getTargetUnit(out.cursor());
202            out.write(codeUnit(insn.getOpcode(), insn.getA()), relativeTarget);
203        }
204    },
205
206    FORMAT_21S() {
207        @Override public DecodedInstruction decode(int opcodeUnit,
208                CodeInput in) throws EOFException {
209            int opcode = byte0(opcodeUnit);
210            int a = byte1(opcodeUnit);
211            int literal = (short) in.read(); // sign-extend
212            return new OneRegisterDecodedInstruction(
213                    this, opcode, 0, null,
214                    0, literal,
215                    a);
216        }
217
218        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
219            out.write(
220                    codeUnit(insn.getOpcode(), insn.getA()),
221                    insn.getLiteralUnit());
222        }
223    },
224
225    FORMAT_21H() {
226        @Override public DecodedInstruction decode(int opcodeUnit,
227                CodeInput in) throws EOFException {
228            int opcode = byte0(opcodeUnit);
229            int a = byte1(opcodeUnit);
230            long literal = (short) in.read(); // sign-extend
231
232            /*
233             * Format 21h decodes differently depending on the opcode,
234             * because the "signed hat" might represent either a 32-
235             * or 64- bit value.
236             */
237            literal <<= (opcode == Opcodes.CONST_HIGH16) ? 16 : 48;
238
239            return new OneRegisterDecodedInstruction(
240                    this, opcode, 0, null,
241                    0, literal,
242                    a);
243        }
244
245        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
246            // See above.
247            int opcode = insn.getOpcode();
248            int shift = (opcode == Opcodes.CONST_HIGH16) ? 16 : 48;
249            short literal = (short) (insn.getLiteral() >> shift);
250
251            out.write(codeUnit(opcode, insn.getA()), literal);
252        }
253    },
254
255    FORMAT_21C() {
256        @Override public DecodedInstruction decode(int opcodeUnit,
257                CodeInput in) throws EOFException {
258            int opcode = byte0(opcodeUnit);
259            int a = byte1(opcodeUnit);
260            int index = in.read();
261            IndexType indexType = OpcodeInfo.getIndexType(opcode);
262            return new OneRegisterDecodedInstruction(
263                    this, opcode, index, indexType,
264                    0, 0L,
265                    a);
266        }
267
268        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
269            out.write(
270                    codeUnit(insn.getOpcode(), insn.getA()),
271                    insn.getIndexUnit());
272        }
273    },
274
275    FORMAT_23X() {
276        @Override public DecodedInstruction decode(int opcodeUnit,
277                CodeInput in) throws EOFException {
278            int opcode = byte0(opcodeUnit);
279            int a = byte1(opcodeUnit);
280            int bc = in.read();
281            int b = byte0(bc);
282            int c = byte1(bc);
283            return new ThreeRegisterDecodedInstruction(
284                    this, opcode, 0, null,
285                    0, 0L,
286                    a, b, c);
287        }
288
289        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
290            out.write(
291                    codeUnit(insn.getOpcode(), insn.getA()),
292                    codeUnit(insn.getB(), insn.getC()));
293        }
294    },
295
296    FORMAT_22B() {
297        @Override public DecodedInstruction decode(int opcodeUnit,
298                CodeInput in) throws EOFException {
299            int opcode = byte0(opcodeUnit);
300            int a = byte1(opcodeUnit);
301            int bc = in.read();
302            int b = byte0(bc);
303            int literal = (byte) byte1(bc); // sign-extend
304            return new TwoRegisterDecodedInstruction(
305                    this, opcode, 0, null,
306                    0, literal,
307                    a, b);
308        }
309
310        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
311            out.write(
312                    codeUnit(insn.getOpcode(), insn.getA()),
313                    codeUnit(insn.getB(),
314                             insn.getLiteralByte()));
315        }
316    },
317
318    FORMAT_22T() {
319        @Override public DecodedInstruction decode(int opcodeUnit,
320                CodeInput in) throws EOFException {
321            int baseAddress = in.cursor() - 1;
322            int opcode = byte0(opcodeUnit);
323            int a = nibble2(opcodeUnit);
324            int b = nibble3(opcodeUnit);
325            int target = (short) in.read(); // sign-extend
326            return new TwoRegisterDecodedInstruction(
327                    this, opcode, 0, null,
328                    baseAddress + target, 0L,
329                    a, b);
330        }
331
332        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
333            short relativeTarget = insn.getTargetUnit(out.cursor());
334            out.write(
335                    codeUnit(insn.getOpcode(),
336                             makeByte(insn.getA(), insn.getB())),
337                    relativeTarget);
338        }
339    },
340
341    FORMAT_22S() {
342        @Override public DecodedInstruction decode(int opcodeUnit,
343                CodeInput in) throws EOFException {
344            int opcode = byte0(opcodeUnit);
345            int a = nibble2(opcodeUnit);
346            int b = nibble3(opcodeUnit);
347            int literal = (short) in.read(); // sign-extend
348            return new TwoRegisterDecodedInstruction(
349                    this, opcode, 0, null,
350                    0, literal,
351                    a, b);
352        }
353
354        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
355            out.write(
356                    codeUnit(insn.getOpcode(),
357                             makeByte(insn.getA(), insn.getB())),
358                    insn.getLiteralUnit());
359        }
360    },
361
362    FORMAT_22C() {
363        @Override public DecodedInstruction decode(int opcodeUnit,
364                CodeInput in) throws EOFException {
365            int opcode = byte0(opcodeUnit);
366            int a = nibble2(opcodeUnit);
367            int b = nibble3(opcodeUnit);
368            int index = in.read();
369            IndexType indexType = OpcodeInfo.getIndexType(opcode);
370            return new TwoRegisterDecodedInstruction(
371                    this, opcode, index, indexType,
372                    0, 0L,
373                    a, b);
374        }
375
376        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
377            out.write(
378                    codeUnit(insn.getOpcode(),
379                             makeByte(insn.getA(), insn.getB())),
380                    insn.getIndexUnit());
381        }
382    },
383
384    FORMAT_22CS() {
385        @Override public DecodedInstruction decode(int opcodeUnit,
386                CodeInput in) throws EOFException {
387            int opcode = byte0(opcodeUnit);
388            int a = nibble2(opcodeUnit);
389            int b = nibble3(opcodeUnit);
390            int index = in.read();
391            return new TwoRegisterDecodedInstruction(
392                    this, opcode, index, IndexType.FIELD_OFFSET,
393                    0, 0L,
394                    a, b);
395        }
396
397        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
398            out.write(
399                    codeUnit(insn.getOpcode(),
400                             makeByte(insn.getA(), insn.getB())),
401                    insn.getIndexUnit());
402        }
403    },
404
405    FORMAT_30T() {
406        @Override public DecodedInstruction decode(int opcodeUnit,
407                CodeInput in) throws EOFException {
408            int baseAddress = in.cursor() - 1;
409            int opcode = byte0(opcodeUnit);
410            int literal = byte1(opcodeUnit); // should be zero
411            int target = in.readInt();
412            return new ZeroRegisterDecodedInstruction(
413                    this, opcode, 0, null,
414                    baseAddress + target, literal);
415        }
416
417        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
418            int relativeTarget = insn.getTarget(out.cursor());
419            out.write(insn.getOpcodeUnit(),
420                    unit0(relativeTarget), unit1(relativeTarget));
421        }
422    },
423
424    FORMAT_32X() {
425        @Override public DecodedInstruction decode(int opcodeUnit,
426                CodeInput in) throws EOFException {
427            int opcode = byte0(opcodeUnit);
428            int literal = byte1(opcodeUnit); // should be zero
429            int a = in.read();
430            int b = in.read();
431            return new TwoRegisterDecodedInstruction(
432                    this, opcode, 0, null,
433                    0, literal,
434                    a, b);
435        }
436
437        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
438            out.write(insn.getOpcodeUnit(), insn.getAUnit(), insn.getBUnit());
439        }
440    },
441
442    FORMAT_31I() {
443        @Override public DecodedInstruction decode(int opcodeUnit,
444                CodeInput in) throws EOFException {
445            int opcode = byte0(opcodeUnit);
446            int a = byte1(opcodeUnit);
447            int literal = in.readInt();
448            return new OneRegisterDecodedInstruction(
449                    this, opcode, 0, null,
450                    0, literal,
451                    a);
452        }
453
454        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
455            int literal = insn.getLiteralInt();
456            out.write(
457                    codeUnit(insn.getOpcode(), insn.getA()),
458                    unit0(literal),
459                    unit1(literal));
460        }
461    },
462
463    FORMAT_31T() {
464        @Override public DecodedInstruction decode(int opcodeUnit,
465                CodeInput in) throws EOFException {
466            int baseAddress = in.cursor() - 1;
467            int opcode = byte0(opcodeUnit);
468            int a = byte1(opcodeUnit);
469            int target = baseAddress + in.readInt();
470
471            /*
472             * Switch instructions need to "forward" their addresses to their
473             * payload target instructions.
474             */
475            switch (opcode) {
476                case Opcodes.PACKED_SWITCH:
477                case Opcodes.SPARSE_SWITCH: {
478                    in.setBaseAddress(target, baseAddress);
479                    break;
480                }
481            }
482
483            return new OneRegisterDecodedInstruction(
484                    this, opcode, 0, null,
485                    target, 0L,
486                    a);
487        }
488
489        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
490            int relativeTarget = insn.getTarget(out.cursor());
491            out.write(
492                    codeUnit(insn.getOpcode(), insn.getA()),
493                    unit0(relativeTarget), unit1(relativeTarget));
494        }
495    },
496
497    FORMAT_31C() {
498        @Override public DecodedInstruction decode(int opcodeUnit,
499                CodeInput in) throws EOFException {
500            int opcode = byte0(opcodeUnit);
501            int a = byte1(opcodeUnit);
502            int index = in.readInt();
503            IndexType indexType = OpcodeInfo.getIndexType(opcode);
504            return new OneRegisterDecodedInstruction(
505                    this, opcode, index, indexType,
506                    0, 0L,
507                    a);
508        }
509
510        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
511            int index = insn.getIndex();
512            out.write(
513                    codeUnit(insn.getOpcode(), insn.getA()),
514                    unit0(index),
515                    unit1(index));
516        }
517    },
518
519    FORMAT_35C() {
520        @Override public DecodedInstruction decode(int opcodeUnit,
521                CodeInput in) throws EOFException {
522            return decodeRegisterList(this, opcodeUnit, in);
523        }
524
525        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
526            encodeRegisterList(insn, out);
527        }
528    },
529
530    FORMAT_35MS() {
531        @Override public DecodedInstruction decode(int opcodeUnit,
532                CodeInput in) throws EOFException {
533            return decodeRegisterList(this, opcodeUnit, in);
534        }
535
536        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
537            encodeRegisterList(insn, out);
538        }
539    },
540
541    FORMAT_35MI() {
542        @Override public DecodedInstruction decode(int opcodeUnit,
543                CodeInput in) throws EOFException {
544            return decodeRegisterList(this, opcodeUnit, in);
545        }
546
547        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
548            encodeRegisterList(insn, out);
549        }
550    },
551
552    FORMAT_3RC() {
553        @Override public DecodedInstruction decode(int opcodeUnit,
554                CodeInput in) throws EOFException {
555            return decodeRegisterRange(this, opcodeUnit, in);
556        }
557
558        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
559            encodeRegisterRange(insn, out);
560        }
561    },
562
563    FORMAT_3RMS() {
564        @Override public DecodedInstruction decode(int opcodeUnit,
565                CodeInput in) throws EOFException {
566            return decodeRegisterRange(this, opcodeUnit, in);
567        }
568
569        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
570            encodeRegisterRange(insn, out);
571        }
572    },
573
574    FORMAT_3RMI() {
575        @Override public DecodedInstruction decode(int opcodeUnit,
576                CodeInput in) throws EOFException {
577            return decodeRegisterRange(this, opcodeUnit, in);
578        }
579
580        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
581            encodeRegisterRange(insn, out);
582        }
583    },
584
585    FORMAT_51L() {
586        @Override public DecodedInstruction decode(int opcodeUnit,
587                CodeInput in) throws EOFException {
588            int opcode = byte0(opcodeUnit);
589            int a = byte1(opcodeUnit);
590            long literal = in.readLong();
591            return new OneRegisterDecodedInstruction(
592                    this, opcode, 0, null,
593                    0, literal,
594                    a);
595        }
596
597        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
598            long literal = insn.getLiteral();
599            out.write(
600                    codeUnit(insn.getOpcode(), insn.getA()),
601                    unit0(literal),
602                    unit1(literal),
603                    unit2(literal),
604                    unit3(literal));
605        }
606    },
607
608    FORMAT_PACKED_SWITCH_PAYLOAD() {
609        @Override public DecodedInstruction decode(int opcodeUnit,
610                CodeInput in) throws EOFException {
611            int baseAddress = in.baseAddressForCursor() - 1; // already read opcode
612            int size = in.read();
613            int firstKey = in.readInt();
614            int[] targets = new int[size];
615
616            for (int i = 0; i < size; i++) {
617                targets[i] = baseAddress + in.readInt();
618            }
619
620            return new PackedSwitchPayloadDecodedInstruction(
621                    this, opcodeUnit, firstKey, targets);
622        }
623
624        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
625            PackedSwitchPayloadDecodedInstruction payload =
626                (PackedSwitchPayloadDecodedInstruction) insn;
627            int[] targets = payload.getTargets();
628            int baseAddress = out.baseAddressForCursor();
629
630            out.write(payload.getOpcodeUnit());
631            out.write(asUnsignedUnit(targets.length));
632            out.writeInt(payload.getFirstKey());
633
634            for (int target : targets) {
635                out.writeInt(target - baseAddress);
636            }
637        }
638    },
639
640    FORMAT_SPARSE_SWITCH_PAYLOAD() {
641        @Override public DecodedInstruction decode(int opcodeUnit,
642                CodeInput in) throws EOFException {
643            int baseAddress = in.baseAddressForCursor() - 1; // already read opcode
644            int size = in.read();
645            int[] keys = new int[size];
646            int[] targets = new int[size];
647
648            for (int i = 0; i < size; i++) {
649                keys[i] = in.readInt();
650            }
651
652            for (int i = 0; i < size; i++) {
653                targets[i] = baseAddress + in.readInt();
654            }
655
656            return new SparseSwitchPayloadDecodedInstruction(
657                    this, opcodeUnit, keys, targets);
658        }
659
660        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
661            SparseSwitchPayloadDecodedInstruction payload =
662                (SparseSwitchPayloadDecodedInstruction) insn;
663            int[] keys = payload.getKeys();
664            int[] targets = payload.getTargets();
665            int baseAddress = out.baseAddressForCursor();
666
667            out.write(payload.getOpcodeUnit());
668            out.write(asUnsignedUnit(targets.length));
669
670            for (int key : keys) {
671                out.writeInt(key);
672            }
673
674            for (int target : targets) {
675                out.writeInt(target - baseAddress);
676            }
677        }
678    },
679
680    FORMAT_FILL_ARRAY_DATA_PAYLOAD() {
681        @Override public DecodedInstruction decode(int opcodeUnit,
682                CodeInput in) throws EOFException {
683            int elementWidth = in.read();
684            int size = in.readInt();
685
686            switch (elementWidth) {
687                case 1: {
688                    byte[] array = new byte[size];
689                    boolean even = true;
690                    for (int i = 0, value = 0; i < size; i++, even = !even) {
691                        if (even) {
692                            value = in.read();
693                        }
694                        array[i] = (byte) (value & 0xff);
695                        value >>= 8;
696                    }
697                    return new FillArrayDataPayloadDecodedInstruction(
698                            this, opcodeUnit, array);
699                }
700                case 2: {
701                    short[] array = new short[size];
702                    for (int i = 0; i < size; i++) {
703                        array[i] = (short) in.read();
704                    }
705                    return new FillArrayDataPayloadDecodedInstruction(
706                            this, opcodeUnit, array);
707                }
708                case 4: {
709                    int[] array = new int[size];
710                    for (int i = 0; i < size; i++) {
711                        array[i] = in.readInt();
712                    }
713                    return new FillArrayDataPayloadDecodedInstruction(
714                            this, opcodeUnit, array);
715                }
716                case 8: {
717                    long[] array = new long[size];
718                    for (int i = 0; i < size; i++) {
719                        array[i] = in.readLong();
720                    }
721                    return new FillArrayDataPayloadDecodedInstruction(
722                            this, opcodeUnit, array);
723                }
724            }
725
726            throw new DexException("bogus element_width: "
727                    + Hex.u2(elementWidth));
728        }
729
730        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
731            FillArrayDataPayloadDecodedInstruction payload =
732                (FillArrayDataPayloadDecodedInstruction) insn;
733            short elementWidth = payload.getElementWidthUnit();
734            Object data = payload.getData();
735
736            out.write(payload.getOpcodeUnit());
737            out.write(elementWidth);
738            out.writeInt(payload.getSize());
739
740            switch (elementWidth) {
741                case 1: out.write((byte[]) data);  break;
742                case 2: out.write((short[]) data); break;
743                case 4: out.write((int[]) data);   break;
744                case 8: out.write((long[]) data);  break;
745                default: {
746                    throw new DexException("bogus element_width: "
747                            + Hex.u2(elementWidth));
748                }
749            }
750        }
751    };
752
753    /**
754     * Decodes an instruction specified by the given opcode unit, reading
755     * any required additional code units from the given input source.
756     */
757    public abstract DecodedInstruction decode(int opcodeUnit, CodeInput in)
758        throws EOFException;
759
760    /**
761     * Encodes the given instruction.
762     */
763    public abstract void encode(DecodedInstruction insn, CodeOutput out);
764
765    /**
766     * Helper method that decodes any of the register-list formats.
767     */
768    private static DecodedInstruction decodeRegisterList(
769            InstructionCodec format, int opcodeUnit, CodeInput in)
770            throws EOFException {
771        int opcode = byte0(opcodeUnit);
772        int e = nibble2(opcodeUnit);
773        int registerCount = nibble3(opcodeUnit);
774        int index = in.read();
775        int abcd = in.read();
776        int a = nibble0(abcd);
777        int b = nibble1(abcd);
778        int c = nibble2(abcd);
779        int d = nibble3(abcd);
780        IndexType indexType = OpcodeInfo.getIndexType(opcode);
781
782        // TODO: Having to switch like this is less than ideal.
783        switch (registerCount) {
784            case 0:
785                return new ZeroRegisterDecodedInstruction(
786                        format, opcode, index, indexType,
787                        0, 0L);
788            case 1:
789                return new OneRegisterDecodedInstruction(
790                        format, opcode, index, indexType,
791                        0, 0L,
792                        a);
793            case 2:
794                return new TwoRegisterDecodedInstruction(
795                        format, opcode, index, indexType,
796                        0, 0L,
797                        a, b);
798            case 3:
799                return new ThreeRegisterDecodedInstruction(
800                        format, opcode, index, indexType,
801                        0, 0L,
802                        a, b, c);
803            case 4:
804                return new FourRegisterDecodedInstruction(
805                        format, opcode, index, indexType,
806                        0, 0L,
807                        a, b, c, d);
808            case 5:
809                return new FiveRegisterDecodedInstruction(
810                        format, opcode, index, indexType,
811                        0, 0L,
812                        a, b, c, d, e);
813        }
814
815        throw new DexException("bogus registerCount: "
816                + Hex.uNibble(registerCount));
817    }
818
819    /**
820     * Helper method that encodes any of the register-list formats.
821     */
822    private static void encodeRegisterList(DecodedInstruction insn,
823            CodeOutput out) {
824        out.write(codeUnit(insn.getOpcode(),
825                        makeByte(insn.getE(), insn.getRegisterCount())),
826                insn.getIndexUnit(),
827                codeUnit(insn.getA(), insn.getB(), insn.getC(), insn.getD()));
828    }
829
830    /**
831     * Helper method that decodes any of the three-unit register-range formats.
832     */
833    private static DecodedInstruction decodeRegisterRange(
834            InstructionCodec format, int opcodeUnit, CodeInput in)
835            throws EOFException {
836        int opcode = byte0(opcodeUnit);
837        int registerCount = byte1(opcodeUnit);
838        int index = in.read();
839        int a = in.read();
840        IndexType indexType = OpcodeInfo.getIndexType(opcode);
841        return new RegisterRangeDecodedInstruction(
842                format, opcode, index, indexType,
843                0, 0L,
844                a, registerCount);
845    }
846
847    /**
848     * Helper method that encodes any of the three-unit register-range formats.
849     */
850    private static void encodeRegisterRange(DecodedInstruction insn,
851            CodeOutput out) {
852        out.write(codeUnit(insn.getOpcode(), insn.getRegisterCount()),
853                insn.getIndexUnit(),
854                insn.getAUnit());
855    }
856
857    private static short codeUnit(int lowByte, int highByte) {
858        if ((lowByte & ~0xff) != 0) {
859            throw new IllegalArgumentException("bogus lowByte");
860        }
861
862        if ((highByte & ~0xff) != 0) {
863            throw new IllegalArgumentException("bogus highByte");
864        }
865
866        return (short) (lowByte | (highByte << 8));
867    }
868
869    private static short codeUnit(int nibble0, int nibble1, int nibble2,
870            int nibble3) {
871        if ((nibble0 & ~0xf) != 0) {
872            throw new IllegalArgumentException("bogus nibble0");
873        }
874
875        if ((nibble1 & ~0xf) != 0) {
876            throw new IllegalArgumentException("bogus nibble1");
877        }
878
879        if ((nibble2 & ~0xf) != 0) {
880            throw new IllegalArgumentException("bogus nibble2");
881        }
882
883        if ((nibble3 & ~0xf) != 0) {
884            throw new IllegalArgumentException("bogus nibble3");
885        }
886
887        return (short) (nibble0 | (nibble1 << 4)
888                | (nibble2 << 8) | (nibble3 << 12));
889    }
890
891    private static int makeByte(int lowNibble, int highNibble) {
892        if ((lowNibble & ~0xf) != 0) {
893            throw new IllegalArgumentException("bogus lowNibble");
894        }
895
896        if ((highNibble & ~0xf) != 0) {
897            throw new IllegalArgumentException("bogus highNibble");
898        }
899
900        return lowNibble | (highNibble << 4);
901    }
902
903    private static short asUnsignedUnit(int value) {
904        if ((value & ~0xffff) != 0) {
905            throw new IllegalArgumentException("bogus unsigned code unit");
906        }
907
908        return (short) value;
909    }
910
911    private static short unit0(int value) {
912        return (short) value;
913    }
914
915    private static short unit1(int value) {
916        return (short) (value >> 16);
917    }
918
919    private static short unit0(long value) {
920        return (short) value;
921    }
922
923    private static short unit1(long value) {
924        return (short) (value >> 16);
925    }
926
927    private static short unit2(long value) {
928        return (short) (value >> 32);
929    }
930
931    private static short unit3(long value) {
932        return (short) (value >> 48);
933    }
934
935    private static int byte0(int value) {
936        return value & 0xff;
937    }
938
939    private static int byte1(int value) {
940        return (value >> 8) & 0xff;
941    }
942
943    private static int byte2(int value) {
944        return (value >> 16) & 0xff;
945    }
946
947    private static int byte3(int value) {
948        return value >>> 24;
949    }
950
951    private static int nibble0(int value) {
952        return value & 0xf;
953    }
954
955    private static int nibble1(int value) {
956        return (value >> 4) & 0xf;
957    }
958
959    private static int nibble2(int value) {
960        return (value >> 8) & 0xf;
961    }
962
963    private static int nibble3(int value) {
964        return (value >> 12) & 0xf;
965    }
966}
967