Simulator.java revision d5370b6315ee137744746479003c9d07185f34f0
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.cf.code;
18
19import com.android.dex.DexFormat;
20import com.android.dx.dex.DexOptions;
21import com.android.dx.rop.code.LocalItem;
22import com.android.dx.rop.cst.Constant;
23import com.android.dx.rop.cst.CstCallSiteRef;
24import com.android.dx.rop.cst.CstFieldRef;
25import com.android.dx.rop.cst.CstInteger;
26import com.android.dx.rop.cst.CstInterfaceMethodRef;
27import com.android.dx.rop.cst.CstInvokeDynamic;
28import com.android.dx.rop.cst.CstMethodRef;
29import com.android.dx.rop.cst.CstType;
30import com.android.dx.rop.type.Prototype;
31import com.android.dx.rop.type.Type;
32import com.android.dx.util.Hex;
33import java.util.ArrayList;
34
35/**
36 * Class which knows how to simulate the effects of executing bytecode.
37 *
38 * <p><b>Note:</b> This class is not thread-safe. If multiple threads
39 * need to use a single instance, they must synchronize access explicitly
40 * between themselves.</p>
41 */
42public class Simulator {
43    /**
44     * {@code non-null;} canned error message for local variable
45     * table mismatches
46     */
47    private static final String LOCAL_MISMATCH_ERROR =
48        "This is symptomatic of .class transformation tools that ignore " +
49        "local variable information.";
50
51    /** {@code non-null;} machine to use when simulating */
52    private final Machine machine;
53
54    /** {@code non-null;} array of bytecode */
55    private final BytecodeArray code;
56
57    /** {@code non-null;} local variable information */
58    private final LocalVariableList localVariables;
59
60    /** {@code non-null;} visitor instance to use */
61    private final SimVisitor visitor;
62
63    /** {@code non-null;} options for dex output */
64    private final DexOptions dexOptions;
65
66    /**
67     * Constructs an instance.
68     *
69     * @param machine {@code non-null;} machine to use when simulating
70     * @param method {@code non-null;} method data to use
71     * @param dexOptions {@code non-null;} options for dex output
72     */
73    public Simulator(Machine machine, ConcreteMethod method, DexOptions dexOptions) {
74        if (machine == null) {
75            throw new NullPointerException("machine == null");
76        }
77
78        if (method == null) {
79            throw new NullPointerException("method == null");
80        }
81
82        this.machine = machine;
83        this.code = method.getCode();
84        this.localVariables = method.getLocalVariables();
85        this.visitor = new SimVisitor();
86        this.dexOptions = dexOptions;
87    }
88
89    /**
90     * Simulates the effect of executing the given basic block. This modifies
91     * the passed-in frame to represent the end result.
92     *
93     * @param bb {@code non-null;} the basic block
94     * @param frame {@code non-null;} frame to operate on
95     */
96    public void simulate(ByteBlock bb, Frame frame) {
97        int end = bb.getEnd();
98
99        visitor.setFrame(frame);
100
101        try {
102            for (int off = bb.getStart(); off < end; /*off*/) {
103                int length = code.parseInstruction(off, visitor);
104                visitor.setPreviousOffset(off);
105                off += length;
106            }
107        } catch (SimException ex) {
108            frame.annotate(ex);
109            throw ex;
110        }
111    }
112
113    /**
114     * Simulates the effect of the instruction at the given offset, by
115     * making appropriate calls on the given frame.
116     *
117     * @param offset {@code offset >= 0;} offset of the instruction to simulate
118     * @param frame {@code non-null;} frame to operate on
119     * @return the length of the instruction, in bytes
120     */
121    public int simulate(int offset, Frame frame) {
122        visitor.setFrame(frame);
123        return code.parseInstruction(offset, visitor);
124    }
125
126    /**
127     * Constructs an "illegal top-of-stack" exception, for the stack
128     * manipulation opcodes.
129     */
130    private static SimException illegalTos() {
131        return new SimException("stack mismatch: illegal " +
132                "top-of-stack for opcode");
133    }
134
135    /**
136     * Returns the required array type for an array load or store
137     * instruction, based on a given implied type and an observed
138     * actual array type.
139     *
140     * <p>The interesting cases here have to do with object arrays,
141     * <code>byte[]</code>s, <code>boolean[]</code>s, and
142     * known-nulls.</p>
143     *
144     * <p>In the case of arrays of objects, we want to narrow the type
145     * to the actual array present on the stack, as long as what is
146     * present is an object type. Similarly, due to a quirk of the
147     * original bytecode representation, the instructions for dealing
148     * with <code>byte[]</code> and <code>boolean[]</code> are
149     * undifferentiated, and we aim here to return whichever one was
150     * actually present on the stack.</p>
151     *
152     * <p>In the case where there is a known-null on the stack where
153     * an array is expected, our behavior depends on the implied type
154     * of the instruction. When the implied type is a reference, we
155     * don't attempt to infer anything, as we don't know the dimension
156     * of the null constant and thus any explicit inferred type could
157     * be wrong. When the implied type is a primitive, we fall back to
158     * the implied type of the instruction. Due to the quirk described
159     * above, this means that source code that uses
160     * <code>boolean[]</code> might get translated surprisingly -- but
161     * correctly -- into an instruction that specifies a
162     * <code>byte[]</code>. It will be correct, because should the
163     * code actually execute, it will necessarily throw a
164     * <code>NullPointerException</code>, and it won't matter what
165     * opcode variant is used to achieve that result.</p>
166     *
167     * @param impliedType {@code non-null;} type implied by the
168     * instruction; is <i>not</i> an array type
169     * @param foundArrayType {@code non-null;} type found on the
170     * stack; is either an array type or a known-null
171     * @return {@code non-null;} the array type that should be
172     * required in this context
173     */
174    private static Type requiredArrayTypeFor(Type impliedType,
175            Type foundArrayType) {
176        if (foundArrayType == Type.KNOWN_NULL) {
177            return impliedType.isReference()
178                ? Type.KNOWN_NULL
179                : impliedType.getArrayType();
180        }
181
182        if ((impliedType == Type.OBJECT)
183                && foundArrayType.isArray()
184                && foundArrayType.getComponentType().isReference()) {
185            return foundArrayType;
186        }
187
188        if ((impliedType == Type.BYTE)
189                && (foundArrayType == Type.BOOLEAN_ARRAY)) {
190            /*
191             * Per above, an instruction with implied byte[] is also
192             * allowed to be used on boolean[].
193             */
194            return Type.BOOLEAN_ARRAY;
195        }
196
197        return impliedType.getArrayType();
198    }
199
200    /**
201     * Bytecode visitor used during simulation.
202     */
203    private class SimVisitor implements BytecodeArray.Visitor {
204        /**
205         * {@code non-null;} machine instance to use (just to avoid excessive
206         * cross-object field access)
207         */
208        private final Machine machine;
209
210        /**
211         * {@code null-ok;} frame to use; set with each call to
212         * {@link Simulator#simulate}
213         */
214        private Frame frame;
215
216        /** offset of the previous bytecode */
217        private int previousOffset;
218
219        /**
220         * Constructs an instance.
221         */
222        public SimVisitor() {
223            this.machine = Simulator.this.machine;
224            this.frame = null;
225        }
226
227        /**
228         * Sets the frame to act on.
229         *
230         * @param frame {@code non-null;} the frame
231         */
232        public void setFrame(Frame frame) {
233            if (frame == null) {
234                throw new NullPointerException("frame == null");
235            }
236
237            this.frame = frame;
238        }
239
240        /** {@inheritDoc} */
241        public void visitInvalid(int opcode, int offset, int length) {
242            throw new SimException("invalid opcode " + Hex.u1(opcode));
243        }
244
245        /** {@inheritDoc} */
246        public void visitNoArgs(int opcode, int offset, int length,
247                Type type) {
248            switch (opcode) {
249                case ByteOps.NOP: {
250                    machine.clearArgs();
251                    break;
252                }
253                case ByteOps.INEG: {
254                    machine.popArgs(frame, type);
255                    break;
256                }
257                case ByteOps.I2L:
258                case ByteOps.I2F:
259                case ByteOps.I2D:
260                case ByteOps.I2B:
261                case ByteOps.I2C:
262                case ByteOps.I2S: {
263                    machine.popArgs(frame, Type.INT);
264                    break;
265                }
266                case ByteOps.L2I:
267                case ByteOps.L2F:
268                case ByteOps.L2D: {
269                    machine.popArgs(frame, Type.LONG);
270                    break;
271                }
272                case ByteOps.F2I:
273                case ByteOps.F2L:
274                case ByteOps.F2D: {
275                    machine.popArgs(frame, Type.FLOAT);
276                    break;
277                }
278                case ByteOps.D2I:
279                case ByteOps.D2L:
280                case ByteOps.D2F: {
281                    machine.popArgs(frame, Type.DOUBLE);
282                    break;
283                }
284                case ByteOps.RETURN: {
285                    machine.clearArgs();
286                    checkReturnType(Type.VOID);
287                    break;
288                }
289                case ByteOps.IRETURN: {
290                    Type checkType = type;
291                    if (type == Type.OBJECT) {
292                        /*
293                         * For an object return, use the best-known
294                         * type of the popped value.
295                         */
296                        checkType = frame.getStack().peekType(0);
297                    }
298                    machine.popArgs(frame, type);
299                    checkReturnType(checkType);
300                    break;
301                }
302                case ByteOps.POP: {
303                    Type peekType = frame.getStack().peekType(0);
304                    if (peekType.isCategory2()) {
305                        throw illegalTos();
306                    }
307                    machine.popArgs(frame, 1);
308                    break;
309                }
310                case ByteOps.ARRAYLENGTH: {
311                    Type arrayType = frame.getStack().peekType(0);
312                    if (!arrayType.isArrayOrKnownNull()) {
313                        throw new SimException("type mismatch: expected " +
314                                "array type but encountered " +
315                                arrayType.toHuman());
316                    }
317                    machine.popArgs(frame, Type.OBJECT);
318                    break;
319                }
320                case ByteOps.ATHROW:
321                case ByteOps.MONITORENTER:
322                case ByteOps.MONITOREXIT: {
323                    machine.popArgs(frame, Type.OBJECT);
324                    break;
325                }
326                case ByteOps.IALOAD: {
327                    /*
328                     * See comment on requiredArrayTypeFor() for explanation
329                     * about what's going on here.
330                     */
331                    Type foundArrayType = frame.getStack().peekType(1);
332                    Type requiredArrayType =
333                        requiredArrayTypeFor(type, foundArrayType);
334
335                    // Make type agree with the discovered requiredArrayType.
336                    type = (requiredArrayType == Type.KNOWN_NULL)
337                        ? Type.KNOWN_NULL
338                        : requiredArrayType.getComponentType();
339
340                    machine.popArgs(frame, requiredArrayType, Type.INT);
341                    break;
342                }
343                case ByteOps.IADD:
344                case ByteOps.ISUB:
345                case ByteOps.IMUL:
346                case ByteOps.IDIV:
347                case ByteOps.IREM:
348                case ByteOps.IAND:
349                case ByteOps.IOR:
350                case ByteOps.IXOR: {
351                    machine.popArgs(frame, type, type);
352                    break;
353                }
354                case ByteOps.ISHL:
355                case ByteOps.ISHR:
356                case ByteOps.IUSHR: {
357                    machine.popArgs(frame, type, Type.INT);
358                    break;
359                }
360                case ByteOps.LCMP: {
361                    machine.popArgs(frame, Type.LONG, Type.LONG);
362                    break;
363                }
364                case ByteOps.FCMPL:
365                case ByteOps.FCMPG: {
366                    machine.popArgs(frame, Type.FLOAT, Type.FLOAT);
367                    break;
368                }
369                case ByteOps.DCMPL:
370                case ByteOps.DCMPG: {
371                    machine.popArgs(frame, Type.DOUBLE, Type.DOUBLE);
372                    break;
373                }
374                case ByteOps.IASTORE: {
375                    /*
376                     * See comment on requiredArrayTypeFor() for
377                     * explanation about what's going on here. In
378                     * addition to that, the category 1 vs. 2 thing
379                     * below is to deal with the fact that, if the
380                     * element type is category 2, we have to skip
381                     * over one extra stack slot to find the array.
382                     */
383                    ExecutionStack stack = frame.getStack();
384                    int peekDepth = type.isCategory1() ? 2 : 3;
385                    Type foundArrayType = stack.peekType(peekDepth);
386                    boolean foundArrayLocal = stack.peekLocal(peekDepth);
387
388                    Type requiredArrayType =
389                        requiredArrayTypeFor(type, foundArrayType);
390
391                    /*
392                     * Make type agree with the discovered requiredArrayType
393                     * if it has local info.
394                     */
395                    if (foundArrayLocal) {
396                        type = (requiredArrayType == Type.KNOWN_NULL)
397                            ? Type.KNOWN_NULL
398                            : requiredArrayType.getComponentType();
399                    }
400
401                    machine.popArgs(frame, requiredArrayType, Type.INT, type);
402                    break;
403                }
404                case ByteOps.POP2:
405                case ByteOps.DUP2: {
406                    ExecutionStack stack = frame.getStack();
407                    int pattern;
408
409                    if (stack.peekType(0).isCategory2()) {
410                        // "form 2" in vmspec-2
411                        machine.popArgs(frame, 1);
412                        pattern = 0x11;
413                    } else if (stack.peekType(1).isCategory1()) {
414                        // "form 1"
415                        machine.popArgs(frame, 2);
416                        pattern = 0x2121;
417                    } else {
418                        throw illegalTos();
419                    }
420
421                    if (opcode == ByteOps.DUP2) {
422                        machine.auxIntArg(pattern);
423                    }
424                    break;
425                }
426                case ByteOps.DUP: {
427                    Type peekType = frame.getStack().peekType(0);
428
429                    if (peekType.isCategory2()) {
430                        throw illegalTos();
431                    }
432
433                    machine.popArgs(frame, 1);
434                    machine.auxIntArg(0x11);
435                    break;
436                }
437                case ByteOps.DUP_X1: {
438                    ExecutionStack stack = frame.getStack();
439
440                    if (!(stack.peekType(0).isCategory1() &&
441                          stack.peekType(1).isCategory1())) {
442                        throw illegalTos();
443                    }
444
445                    machine.popArgs(frame, 2);
446                    machine.auxIntArg(0x212);
447                    break;
448                }
449                case ByteOps.DUP_X2: {
450                    ExecutionStack stack = frame.getStack();
451
452                    if (stack.peekType(0).isCategory2()) {
453                        throw illegalTos();
454                    }
455
456                    if (stack.peekType(1).isCategory2()) {
457                        // "form 2" in vmspec-2
458                        machine.popArgs(frame, 2);
459                        machine.auxIntArg(0x212);
460                    } else if (stack.peekType(2).isCategory1()) {
461                        // "form 1"
462                        machine.popArgs(frame, 3);
463                        machine.auxIntArg(0x3213);
464                    } else {
465                        throw illegalTos();
466                    }
467                    break;
468                }
469                case ByteOps.DUP2_X1: {
470                    ExecutionStack stack = frame.getStack();
471
472                    if (stack.peekType(0).isCategory2()) {
473                        // "form 2" in vmspec-2
474                        if (stack.peekType(2).isCategory2()) {
475                            throw illegalTos();
476                        }
477                        machine.popArgs(frame, 2);
478                        machine.auxIntArg(0x212);
479                    } else {
480                        // "form 1"
481                        if (stack.peekType(1).isCategory2() ||
482                            stack.peekType(2).isCategory2()) {
483                            throw illegalTos();
484                        }
485                        machine.popArgs(frame, 3);
486                        machine.auxIntArg(0x32132);
487                    }
488                    break;
489                }
490                case ByteOps.DUP2_X2: {
491                    ExecutionStack stack = frame.getStack();
492
493                    if (stack.peekType(0).isCategory2()) {
494                        if (stack.peekType(2).isCategory2()) {
495                            // "form 4" in vmspec-2
496                            machine.popArgs(frame, 2);
497                            machine.auxIntArg(0x212);
498                        } else if (stack.peekType(3).isCategory1()) {
499                            // "form 2"
500                            machine.popArgs(frame, 3);
501                            machine.auxIntArg(0x3213);
502                        } else {
503                            throw illegalTos();
504                        }
505                    } else if (stack.peekType(1).isCategory1()) {
506                        if (stack.peekType(2).isCategory2()) {
507                            // "form 3"
508                            machine.popArgs(frame, 3);
509                            machine.auxIntArg(0x32132);
510                        } else if (stack.peekType(3).isCategory1()) {
511                            // "form 1"
512                            machine.popArgs(frame, 4);
513                            machine.auxIntArg(0x432143);
514                        } else {
515                            throw illegalTos();
516                        }
517                    } else {
518                        throw illegalTos();
519                    }
520                    break;
521                }
522                case ByteOps.SWAP: {
523                    ExecutionStack stack = frame.getStack();
524
525                    if (!(stack.peekType(0).isCategory1() &&
526                          stack.peekType(1).isCategory1())) {
527                        throw illegalTos();
528                    }
529
530                    machine.popArgs(frame, 2);
531                    machine.auxIntArg(0x12);
532                    break;
533                }
534                default: {
535                    visitInvalid(opcode, offset, length);
536                    return;
537                }
538            }
539
540            machine.auxType(type);
541            machine.run(frame, offset, opcode);
542        }
543
544        /**
545         * Checks whether the prototype is compatible with returning the
546         * given type, and throws if not.
547         *
548         * @param encountered {@code non-null;} the encountered return type
549         */
550        private void checkReturnType(Type encountered) {
551            Type returnType = machine.getPrototype().getReturnType();
552
553            /*
554             * Check to see if the prototype's return type is
555             * possibly assignable from the type we encountered. This
556             * takes care of all the salient cases (types are the same,
557             * they're compatible primitive types, etc.).
558             */
559            if (!Merger.isPossiblyAssignableFrom(returnType, encountered)) {
560                throw new SimException("return type mismatch: prototype " +
561                        "indicates " + returnType.toHuman() +
562                        ", but encountered type " + encountered.toHuman());
563            }
564        }
565
566        /** {@inheritDoc} */
567        public void visitLocal(int opcode, int offset, int length,
568                int idx, Type type, int value) {
569            /*
570             * Note that the "type" parameter is always the simplest
571             * type based on the original opcode, e.g., "int" for
572             * "iload" (per se) and "Object" for "aload". So, when
573             * possible, we replace the type with the one indicated in
574             * the local variable table, though we still need to check
575             * to make sure it's valid for the opcode.
576             *
577             * The reason we use (offset + length) for the localOffset
578             * for a store is because it is only after the store that
579             * the local type becomes valid. On the other hand, the
580             * type associated with a load is valid at the start of
581             * the instruction.
582             */
583            int localOffset =
584                (opcode == ByteOps.ISTORE) ? (offset + length) : offset;
585            LocalVariableList.Item local =
586                localVariables.pcAndIndexToLocal(localOffset, idx);
587            Type localType;
588
589            if (local != null) {
590                localType = local.getType();
591                if (localType.getBasicFrameType() !=
592                        type.getBasicFrameType()) {
593                    // wrong type, ignore local variable info
594                    local = null;
595                    localType = type;
596                }
597            } else {
598                localType = type;
599            }
600
601            switch (opcode) {
602                case ByteOps.ILOAD:
603                case ByteOps.RET: {
604                    machine.localArg(frame, idx);
605                    machine.localInfo(local != null);
606                    machine.auxType(type);
607                    break;
608                }
609                case ByteOps.ISTORE: {
610                    LocalItem item
611                            = (local == null) ? null : local.getLocalItem();
612                    machine.popArgs(frame, type);
613                    machine.auxType(type);
614                    machine.localTarget(idx, localType, item);
615                    break;
616                }
617                case ByteOps.IINC: {
618                    LocalItem item
619                            = (local == null) ? null : local.getLocalItem();
620                    machine.localArg(frame, idx);
621                    machine.localTarget(idx, localType, item);
622                    machine.auxType(type);
623                    machine.auxIntArg(value);
624                    machine.auxCstArg(CstInteger.make(value));
625                    break;
626                }
627                default: {
628                    visitInvalid(opcode, offset, length);
629                    return;
630                }
631            }
632
633            machine.run(frame, offset, opcode);
634        }
635
636        /** {@inheritDoc} */
637        public void visitConstant(int opcode, int offset, int length,
638                Constant cst, int value) {
639            switch (opcode) {
640                case ByteOps.ANEWARRAY: {
641                    machine.popArgs(frame, Type.INT);
642                    break;
643                }
644                case ByteOps.PUTSTATIC: {
645                    Type fieldType = ((CstFieldRef) cst).getType();
646                    machine.popArgs(frame, fieldType);
647                    break;
648                }
649                case ByteOps.GETFIELD:
650                case ByteOps.CHECKCAST:
651                case ByteOps.INSTANCEOF: {
652                    machine.popArgs(frame, Type.OBJECT);
653                    break;
654                }
655                case ByteOps.PUTFIELD: {
656                    Type fieldType = ((CstFieldRef) cst).getType();
657                    machine.popArgs(frame, Type.OBJECT, fieldType);
658                    break;
659                }
660                case ByteOps.INVOKEINTERFACE:
661                case ByteOps.INVOKEVIRTUAL:
662                case ByteOps.INVOKESPECIAL:
663                case ByteOps.INVOKESTATIC: {
664                    /*
665                     * Convert the interface method ref into a normal
666                     * method ref if necessary.
667                     */
668                    if (cst instanceof CstInterfaceMethodRef) {
669                        if (opcode != ByteOps.INVOKEINTERFACE) {
670                            if (!dexOptions.canUseDefaultInterfaceMethods()) {
671                                throw new SimException(
672                                    "default or static interface method used without " +
673                                    "--min-sdk-version >= " + DexFormat.API_DEFAULT_INTERFACE_METHODS);
674                            }
675                        }
676                        cst = ((CstInterfaceMethodRef) cst).toMethodRef();
677                    }
678
679                    /*
680                     * Check whether invoke-polymorphic is required and supported.
681                    */
682                    if (cst instanceof CstMethodRef) {
683                        CstMethodRef methodRef = (CstMethodRef) cst;
684                        if (methodRef.isSignaturePolymorphic()) {
685                            if (!dexOptions.canUseInvokePolymorphic()) {
686                                throw new SimException(
687                                    "signature-polymorphic method called without " +
688                                    "--min-sdk-version >= " + DexFormat.API_INVOKE_POLYMORPHIC);
689                            }
690                            if (opcode != ByteOps.INVOKEVIRTUAL) {
691                                throw new SimException(
692                                    "Unsupported signature polymorphic invocation (" +
693                                    ByteOps.opName(opcode) + ")");
694                            }
695                        }
696                    }
697
698                    /*
699                     * Get the instance or static prototype, and use it to
700                     * direct the machine.
701                     */
702                    boolean staticMethod = (opcode == ByteOps.INVOKESTATIC);
703                    Prototype prototype =
704                        ((CstMethodRef) cst).getPrototype(staticMethod);
705                    machine.popArgs(frame, prototype);
706                    break;
707                }
708                case ByteOps.INVOKEDYNAMIC: {
709                    if (!dexOptions.canUseInvokeCustom()) {
710                        throw new SimException(
711                            "invalid opcode " + Hex.u1(opcode) +
712                            " (invokedynamic requires --min-sdk-version >= " +
713                            DexFormat.API_INVOKE_POLYMORPHIC + ")");
714                    }
715                    CstInvokeDynamic invokeDynamicRef = (CstInvokeDynamic) cst;
716                    Prototype prototype = invokeDynamicRef.getPrototype();
717                    machine.popArgs(frame, prototype);
718                    // Change the constant to be associated with instruction to
719                    // a call site reference.
720                    cst = invokeDynamicRef.addReference();
721                    break;
722                }
723                case ByteOps.MULTIANEWARRAY: {
724                    /*
725                     * The "value" here is the count of dimensions to
726                     * create. Make a prototype of that many "int"
727                     * types, and tell the machine to pop them. This
728                     * isn't the most efficient way in the world to do
729                     * this, but then again, multianewarray is pretty
730                     * darn rare and so not worth much effort
731                     * optimizing for.
732                     */
733                    Prototype prototype =
734                        Prototype.internInts(Type.VOID, value);
735                    machine.popArgs(frame, prototype);
736                    break;
737                }
738                default: {
739                    machine.clearArgs();
740                    break;
741                }
742            }
743
744            machine.auxIntArg(value);
745            machine.auxCstArg(cst);
746            machine.run(frame, offset, opcode);
747        }
748
749        /** {@inheritDoc} */
750        public void visitBranch(int opcode, int offset, int length,
751                int target) {
752            switch (opcode) {
753                case ByteOps.IFEQ:
754                case ByteOps.IFNE:
755                case ByteOps.IFLT:
756                case ByteOps.IFGE:
757                case ByteOps.IFGT:
758                case ByteOps.IFLE: {
759                    machine.popArgs(frame, Type.INT);
760                    break;
761                }
762                case ByteOps.IFNULL:
763                case ByteOps.IFNONNULL: {
764                    machine.popArgs(frame, Type.OBJECT);
765                    break;
766                }
767                case ByteOps.IF_ICMPEQ:
768                case ByteOps.IF_ICMPNE:
769                case ByteOps.IF_ICMPLT:
770                case ByteOps.IF_ICMPGE:
771                case ByteOps.IF_ICMPGT:
772                case ByteOps.IF_ICMPLE: {
773                    machine.popArgs(frame, Type.INT, Type.INT);
774                    break;
775                }
776                case ByteOps.IF_ACMPEQ:
777                case ByteOps.IF_ACMPNE: {
778                    machine.popArgs(frame, Type.OBJECT, Type.OBJECT);
779                    break;
780                }
781                case ByteOps.GOTO:
782                case ByteOps.JSR:
783                case ByteOps.GOTO_W:
784                case ByteOps.JSR_W: {
785                    machine.clearArgs();
786                    break;
787                }
788                default: {
789                    visitInvalid(opcode, offset, length);
790                    return;
791                }
792            }
793
794            machine.auxTargetArg(target);
795            machine.run(frame, offset, opcode);
796        }
797
798        /** {@inheritDoc} */
799        public void visitSwitch(int opcode, int offset, int length,
800                SwitchList cases, int padding) {
801            machine.popArgs(frame, Type.INT);
802            machine.auxIntArg(padding);
803            machine.auxSwitchArg(cases);
804            machine.run(frame, offset, opcode);
805        }
806
807        /** {@inheritDoc} */
808        public void visitNewarray(int offset, int length, CstType type,
809                ArrayList<Constant> initValues) {
810            machine.popArgs(frame, Type.INT);
811            machine.auxInitValues(initValues);
812            machine.auxCstArg(type);
813            machine.run(frame, offset, ByteOps.NEWARRAY);
814        }
815
816        /** {@inheritDoc} */
817        public void setPreviousOffset(int offset) {
818            previousOffset = offset;
819        }
820
821        /** {@inheritDoc} */
822        public int getPreviousOffset() {
823            return previousOffset;
824        }
825    }
826}
827