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_33X() {
609        @Override public DecodedInstruction decode(int opcodeUnit,
610                CodeInput in) throws EOFException {
611            int ab = in.read();
612            int a = byte0(ab);
613            int b = byte1(ab);
614            int c = in.read();
615            return new ThreeRegisterDecodedInstruction(
616                    this, opcodeUnit, 0, null,
617                    0, 0L,
618                    a, b, c);
619        }
620
621        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
622            out.write(
623                    insn.getOpcodeUnit(),
624                    codeUnit(insn.getA(), insn.getB()),
625                    insn.getCUnit());
626        }
627    },
628
629    FORMAT_32S() {
630        @Override public DecodedInstruction decode(int opcodeUnit,
631                CodeInput in) throws EOFException {
632            int ab = in.read();
633            int a = byte0(ab);
634            int b = byte1(ab);
635            int literal = (short) in.read(); // sign-extend
636            return new TwoRegisterDecodedInstruction(
637                    this, opcodeUnit, 0, null,
638                    0, literal,
639                    a, b);
640        }
641
642        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
643            out.write(
644                    insn.getOpcodeUnit(),
645                    codeUnit(insn.getA(), insn.getB()),
646                    insn.getLiteralUnit());
647        }
648    },
649
650    FORMAT_40SC() {
651        @Override public DecodedInstruction decode(int opcodeUnit,
652                CodeInput in) throws EOFException {
653            // Note: We use the literal field to hold the decoded AA value.
654            int index = in.readInt();
655            int literal = in.read();
656            return new ZeroRegisterDecodedInstruction(
657                    this, opcodeUnit, index, IndexType.VARIES,
658                    0, literal);
659        }
660
661        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
662            int index = insn.getIndex();
663            out.write(
664                    insn.getOpcodeUnit(),
665                    unit0(index),
666                    unit1(index),
667                    insn.getLiteralUnit());
668        }
669    },
670
671    FORMAT_41C() {
672        @Override public DecodedInstruction decode(int opcodeUnit,
673                CodeInput in) throws EOFException {
674            int index = in.readInt();
675            int a = in.read();
676            IndexType indexType = OpcodeInfo.getIndexType(opcodeUnit);
677            return new OneRegisterDecodedInstruction(
678                    this, opcodeUnit, index, indexType,
679                    0, 0L,
680                    a);
681        }
682
683        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
684            int index = insn.getIndex();
685            out.write(
686                    insn.getOpcodeUnit(),
687                    unit0(index),
688                    unit1(index),
689                    insn.getAUnit());
690        }
691    },
692
693    FORMAT_52C() {
694        @Override public DecodedInstruction decode(int opcodeUnit,
695                CodeInput in) throws EOFException {
696            int index = in.readInt();
697            int a = in.read();
698            int b = in.read();
699            IndexType indexType = OpcodeInfo.getIndexType(opcodeUnit);
700            return new TwoRegisterDecodedInstruction(
701                    this, opcodeUnit, index, indexType,
702                    0, 0L,
703                    a, b);
704        }
705
706        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
707            int index = insn.getIndex();
708            out.write(
709                    insn.getOpcodeUnit(),
710                    unit0(index),
711                    unit1(index),
712                    insn.getAUnit(),
713                    insn.getBUnit());
714        }
715    },
716
717    FORMAT_5RC() {
718        @Override public DecodedInstruction decode(int opcodeUnit,
719                CodeInput in) throws EOFException {
720            int index = in.readInt();
721            int registerCount = in.read();
722            int a = in.read();
723            IndexType indexType = OpcodeInfo.getIndexType(opcodeUnit);
724            return new RegisterRangeDecodedInstruction(
725                    this, opcodeUnit, index, indexType,
726                    0, 0L,
727                    a, registerCount);
728        }
729
730        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
731            int index = insn.getIndex();
732            out.write(
733                    insn.getOpcodeUnit(),
734                    unit0(index),
735                    unit1(index),
736                    insn.getRegisterCountUnit(),
737                    insn.getAUnit());
738        }
739    },
740
741    FORMAT_PACKED_SWITCH_PAYLOAD() {
742        @Override public DecodedInstruction decode(int opcodeUnit,
743                CodeInput in) throws EOFException {
744            int baseAddress = in.baseAddressForCursor() - 1; // already read opcode
745            int size = in.read();
746            int firstKey = in.readInt();
747            int[] targets = new int[size];
748
749            for (int i = 0; i < size; i++) {
750                targets[i] = baseAddress + in.readInt();
751            }
752
753            return new PackedSwitchPayloadDecodedInstruction(
754                    this, opcodeUnit, firstKey, targets);
755        }
756
757        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
758            PackedSwitchPayloadDecodedInstruction payload =
759                (PackedSwitchPayloadDecodedInstruction) insn;
760            int[] targets = payload.getTargets();
761            int baseAddress = out.baseAddressForCursor();
762
763            out.write(payload.getOpcodeUnit());
764            out.write(asUnsignedUnit(targets.length));
765            out.writeInt(payload.getFirstKey());
766
767            for (int target : targets) {
768                out.writeInt(target - baseAddress);
769            }
770        }
771    },
772
773    FORMAT_SPARSE_SWITCH_PAYLOAD() {
774        @Override public DecodedInstruction decode(int opcodeUnit,
775                CodeInput in) throws EOFException {
776            int baseAddress = in.baseAddressForCursor() - 1; // already read opcode
777            int size = in.read();
778            int[] keys = new int[size];
779            int[] targets = new int[size];
780
781            for (int i = 0; i < size; i++) {
782                keys[i] = in.readInt();
783            }
784
785            for (int i = 0; i < size; i++) {
786                targets[i] = baseAddress + in.readInt();
787            }
788
789            return new SparseSwitchPayloadDecodedInstruction(
790                    this, opcodeUnit, keys, targets);
791        }
792
793        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
794            SparseSwitchPayloadDecodedInstruction payload =
795                (SparseSwitchPayloadDecodedInstruction) insn;
796            int[] keys = payload.getKeys();
797            int[] targets = payload.getTargets();
798            int baseAddress = out.baseAddressForCursor();
799
800            out.write(payload.getOpcodeUnit());
801            out.write(asUnsignedUnit(targets.length));
802
803            for (int key : keys) {
804                out.writeInt(key);
805            }
806
807            for (int target : targets) {
808                out.writeInt(target - baseAddress);
809            }
810        }
811    },
812
813    FORMAT_FILL_ARRAY_DATA_PAYLOAD() {
814        @Override public DecodedInstruction decode(int opcodeUnit,
815                CodeInput in) throws EOFException {
816            int elementWidth = in.read();
817            int size = in.readInt();
818
819            switch (elementWidth) {
820                case 1: {
821                    byte[] array = new byte[size];
822                    boolean even = true;
823                    for (int i = 0, value = 0; i < size; i++, even = !even) {
824                        if (even) {
825                            value = in.read();
826                        }
827                        array[i] = (byte) (value & 0xff);
828                        value >>= 8;
829                    }
830                    return new FillArrayDataPayloadDecodedInstruction(
831                            this, opcodeUnit, array);
832                }
833                case 2: {
834                    short[] array = new short[size];
835                    for (int i = 0; i < size; i++) {
836                        array[i] = (short) in.read();
837                    }
838                    return new FillArrayDataPayloadDecodedInstruction(
839                            this, opcodeUnit, array);
840                }
841                case 4: {
842                    int[] array = new int[size];
843                    for (int i = 0; i < size; i++) {
844                        array[i] = in.readInt();
845                    }
846                    return new FillArrayDataPayloadDecodedInstruction(
847                            this, opcodeUnit, array);
848                }
849                case 8: {
850                    long[] array = new long[size];
851                    for (int i = 0; i < size; i++) {
852                        array[i] = in.readLong();
853                    }
854                    return new FillArrayDataPayloadDecodedInstruction(
855                            this, opcodeUnit, array);
856                }
857            }
858
859            throw new DexException("bogus element_width: "
860                    + Hex.u2(elementWidth));
861        }
862
863        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
864            FillArrayDataPayloadDecodedInstruction payload =
865                (FillArrayDataPayloadDecodedInstruction) insn;
866            short elementWidth = payload.getElementWidthUnit();
867            Object data = payload.getData();
868
869            out.write(payload.getOpcodeUnit());
870            out.write(elementWidth);
871            out.writeInt(payload.getSize());
872
873            switch (elementWidth) {
874                case 1: out.write((byte[]) data);  break;
875                case 2: out.write((short[]) data); break;
876                case 4: out.write((int[]) data);   break;
877                case 8: out.write((long[]) data);  break;
878                default: {
879                    throw new DexException("bogus element_width: "
880                            + Hex.u2(elementWidth));
881                }
882            }
883        }
884    };
885
886    /**
887     * Decodes an instruction specified by the given opcode unit, reading
888     * any required additional code units from the given input source.
889     */
890    public abstract DecodedInstruction decode(int opcodeUnit, CodeInput in)
891        throws EOFException;
892
893    /**
894     * Encodes the given instruction.
895     */
896    public abstract void encode(DecodedInstruction insn, CodeOutput out);
897
898    /**
899     * Helper method that decodes any of the register-list formats.
900     */
901    private static DecodedInstruction decodeRegisterList(
902            InstructionCodec format, int opcodeUnit, CodeInput in)
903            throws EOFException {
904        int opcode = byte0(opcodeUnit);
905        int e = nibble2(opcodeUnit);
906        int registerCount = nibble3(opcodeUnit);
907        int index = in.read();
908        int abcd = in.read();
909        int a = nibble0(abcd);
910        int b = nibble1(abcd);
911        int c = nibble2(abcd);
912        int d = nibble3(abcd);
913        IndexType indexType = OpcodeInfo.getIndexType(opcode);
914
915        // TODO: Having to switch like this is less than ideal.
916        switch (registerCount) {
917            case 0:
918                return new ZeroRegisterDecodedInstruction(
919                        format, opcode, index, indexType,
920                        0, 0L);
921            case 1:
922                return new OneRegisterDecodedInstruction(
923                        format, opcode, index, indexType,
924                        0, 0L,
925                        a);
926            case 2:
927                return new TwoRegisterDecodedInstruction(
928                        format, opcode, index, indexType,
929                        0, 0L,
930                        a, b);
931            case 3:
932                return new ThreeRegisterDecodedInstruction(
933                        format, opcode, index, indexType,
934                        0, 0L,
935                        a, b, c);
936            case 4:
937                return new FourRegisterDecodedInstruction(
938                        format, opcode, index, indexType,
939                        0, 0L,
940                        a, b, c, d);
941            case 5:
942                return new FiveRegisterDecodedInstruction(
943                        format, opcode, index, indexType,
944                        0, 0L,
945                        a, b, c, d, e);
946        }
947
948        throw new DexException("bogus registerCount: "
949                + Hex.uNibble(registerCount));
950    }
951
952    /**
953     * Helper method that encodes any of the register-list formats.
954     */
955    private static void encodeRegisterList(DecodedInstruction insn,
956            CodeOutput out) {
957        out.write(codeUnit(insn.getOpcode(),
958                        makeByte(insn.getE(), insn.getRegisterCount())),
959                insn.getIndexUnit(),
960                codeUnit(insn.getA(), insn.getB(), insn.getC(), insn.getD()));
961    }
962
963    /**
964     * Helper method that decodes any of the three-unit register-range formats.
965     */
966    private static DecodedInstruction decodeRegisterRange(
967            InstructionCodec format, int opcodeUnit, CodeInput in)
968            throws EOFException {
969        int opcode = byte0(opcodeUnit);
970        int registerCount = byte1(opcodeUnit);
971        int index = in.read();
972        int a = in.read();
973        IndexType indexType = OpcodeInfo.getIndexType(opcode);
974        return new RegisterRangeDecodedInstruction(
975                format, opcode, index, indexType,
976                0, 0L,
977                a, registerCount);
978    }
979
980    /**
981     * Helper method that encodes any of the three-unit register-range formats.
982     */
983    private static void encodeRegisterRange(DecodedInstruction insn,
984            CodeOutput out) {
985        out.write(codeUnit(insn.getOpcode(), insn.getRegisterCount()),
986                insn.getIndexUnit(),
987                insn.getAUnit());
988    }
989
990    private static short codeUnit(int lowByte, int highByte) {
991        if ((lowByte & ~0xff) != 0) {
992            throw new IllegalArgumentException("bogus lowByte");
993        }
994
995        if ((highByte & ~0xff) != 0) {
996            throw new IllegalArgumentException("bogus highByte");
997        }
998
999        return (short) (lowByte | (highByte << 8));
1000    }
1001
1002    private static short codeUnit(int nibble0, int nibble1, int nibble2,
1003            int nibble3) {
1004        if ((nibble0 & ~0xf) != 0) {
1005            throw new IllegalArgumentException("bogus nibble0");
1006        }
1007
1008        if ((nibble1 & ~0xf) != 0) {
1009            throw new IllegalArgumentException("bogus nibble1");
1010        }
1011
1012        if ((nibble2 & ~0xf) != 0) {
1013            throw new IllegalArgumentException("bogus nibble2");
1014        }
1015
1016        if ((nibble3 & ~0xf) != 0) {
1017            throw new IllegalArgumentException("bogus nibble3");
1018        }
1019
1020        return (short) (nibble0 | (nibble1 << 4)
1021                | (nibble2 << 8) | (nibble3 << 12));
1022    }
1023
1024    private static int makeByte(int lowNibble, int highNibble) {
1025        if ((lowNibble & ~0xf) != 0) {
1026            throw new IllegalArgumentException("bogus lowNibble");
1027        }
1028
1029        if ((highNibble & ~0xf) != 0) {
1030            throw new IllegalArgumentException("bogus highNibble");
1031        }
1032
1033        return lowNibble | (highNibble << 4);
1034    }
1035
1036    private static short asUnsignedUnit(int value) {
1037        if ((value & ~0xffff) != 0) {
1038            throw new IllegalArgumentException("bogus unsigned code unit");
1039        }
1040
1041        return (short) value;
1042    }
1043
1044    private static short unit0(int value) {
1045        return (short) value;
1046    }
1047
1048    private static short unit1(int value) {
1049        return (short) (value >> 16);
1050    }
1051
1052    private static short unit0(long value) {
1053        return (short) value;
1054    }
1055
1056    private static short unit1(long value) {
1057        return (short) (value >> 16);
1058    }
1059
1060    private static short unit2(long value) {
1061        return (short) (value >> 32);
1062    }
1063
1064    private static short unit3(long value) {
1065        return (short) (value >> 48);
1066    }
1067
1068    private static int byte0(int value) {
1069        return value & 0xff;
1070    }
1071
1072    private static int byte1(int value) {
1073        return (value >> 8) & 0xff;
1074    }
1075
1076    private static int byte2(int value) {
1077        return (value >> 16) & 0xff;
1078    }
1079
1080    private static int byte3(int value) {
1081        return value >>> 24;
1082    }
1083
1084    private static int nibble0(int value) {
1085        return value & 0xf;
1086    }
1087
1088    private static int nibble1(int value) {
1089        return (value >> 4) & 0xf;
1090    }
1091
1092    private static int nibble2(int value) {
1093        return (value >> 8) & 0xf;
1094    }
1095
1096    private static int nibble3(int value) {
1097        return (value >> 12) & 0xf;
1098    }
1099}
1100