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