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