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