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.rop.code.LocalItem;
20import com.android.dx.rop.code.RegisterSpec;
21import com.android.dx.rop.cst.Constant;
22import com.android.dx.rop.type.Prototype;
23import com.android.dx.rop.type.StdTypeList;
24import com.android.dx.rop.type.Type;
25import com.android.dx.rop.type.TypeBearer;
26import java.util.ArrayList;
27
28/**
29 * Base implementation of {@link Machine}.
30 *
31 * <p><b>Note:</b> For the most part, the documentation for this class
32 * ignores the distinction between {@link Type} and {@link
33 * TypeBearer}.</p>
34 */
35public abstract class BaseMachine implements Machine {
36    /* {@code non-null;} the prototype for the associated method */
37    private final Prototype prototype;
38
39    /** {@code non-null;} primary arguments */
40    private TypeBearer[] args;
41
42    /** {@code >= 0;} number of primary arguments */
43    private int argCount;
44
45    /** {@code null-ok;} type of the operation, if salient */
46    private Type auxType;
47
48    /** auxiliary {@code int} argument */
49    private int auxInt;
50
51    /** {@code null-ok;} auxiliary constant argument */
52    private Constant auxCst;
53
54    /** auxiliary branch target argument */
55    private int auxTarget;
56
57    /** {@code null-ok;} auxiliary switch cases argument */
58    private SwitchList auxCases;
59
60    /** {@code null-ok;} auxiliary initial value list for newarray */
61    private ArrayList<Constant> auxInitValues;
62
63    /** {@code >= -1;} last local accessed */
64    private int localIndex;
65
66    /** specifies if local has info in the local variable table */
67    private boolean localInfo;
68
69    /** {@code null-ok;} local target spec, if salient and calculated */
70    private RegisterSpec localTarget;
71
72    /** {@code non-null;} results */
73    private TypeBearer[] results;
74
75    /**
76     * {@code >= -1;} count of the results, or {@code -1} if no results
77     * have been set
78     */
79    private int resultCount;
80
81    /**
82     * Constructs an instance.
83     *
84     * @param prototype {@code non-null;} the prototype for the
85     * associated method
86     */
87    public BaseMachine(Prototype prototype) {
88        if (prototype == null) {
89            throw new NullPointerException("prototype == null");
90        }
91
92        this.prototype = prototype;
93        args = new TypeBearer[10];
94        results = new TypeBearer[6];
95        clearArgs();
96    }
97
98    /** {@inheritDoc} */
99    public Prototype getPrototype() {
100        return prototype;
101    }
102
103    /** {@inheritDoc} */
104    public final void clearArgs() {
105        argCount = 0;
106        auxType = null;
107        auxInt = 0;
108        auxCst = null;
109        auxTarget = 0;
110        auxCases = null;
111        auxInitValues = null;
112        localIndex = -1;
113        localInfo = false;
114        localTarget = null;
115        resultCount = -1;
116    }
117
118    /** {@inheritDoc} */
119    public final void popArgs(Frame frame, int count) {
120        ExecutionStack stack = frame.getStack();
121
122        clearArgs();
123
124        if (count > args.length) {
125            // Grow args, and add a little extra room to grow even more.
126            args = new TypeBearer[count + 10];
127        }
128
129        for (int i = count - 1; i >= 0; i--) {
130            args[i] = stack.pop();
131        }
132
133        argCount = count;
134    }
135
136    /** {@inheritDoc} */
137    public void popArgs(Frame frame, Prototype prototype) {
138        StdTypeList types = prototype.getParameterTypes();
139        int size = types.size();
140
141        // Use the above method to do the actual popping...
142        popArgs(frame, size);
143
144        // ...and then verify the popped types.
145
146        for (int i = 0; i < size; i++) {
147            if (! Merger.isPossiblyAssignableFrom(types.getType(i), args[i])) {
148                throw new SimException("at stack depth " + (size - 1 - i) +
149                        ", expected type " + types.getType(i).toHuman() +
150                        " but found " + args[i].getType().toHuman());
151            }
152        }
153    }
154
155    public final void popArgs(Frame frame, Type type) {
156        // Use the above method to do the actual popping...
157        popArgs(frame, 1);
158
159        // ...and then verify the popped type.
160        if (! Merger.isPossiblyAssignableFrom(type, args[0])) {
161            throw new SimException("expected type " + type.toHuman() +
162                    " but found " + args[0].getType().toHuman());
163        }
164    }
165
166    /** {@inheritDoc} */
167    public final void popArgs(Frame frame, Type type1, Type type2) {
168        // Use the above method to do the actual popping...
169        popArgs(frame, 2);
170
171        // ...and then verify the popped types.
172
173        if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
174            throw new SimException("expected type " + type1.toHuman() +
175                    " but found " + args[0].getType().toHuman());
176        }
177
178        if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
179            throw new SimException("expected type " + type2.toHuman() +
180                    " but found " + args[1].getType().toHuman());
181        }
182    }
183
184    /** {@inheritDoc} */
185    public final void popArgs(Frame frame, Type type1, Type type2,
186            Type type3) {
187        // Use the above method to do the actual popping...
188        popArgs(frame, 3);
189
190        // ...and then verify the popped types.
191
192        if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
193            throw new SimException("expected type " + type1.toHuman() +
194                    " but found " + args[0].getType().toHuman());
195        }
196
197        if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
198            throw new SimException("expected type " + type2.toHuman() +
199                    " but found " + args[1].getType().toHuman());
200        }
201
202        if (! Merger.isPossiblyAssignableFrom(type3, args[2])) {
203            throw new SimException("expected type " + type3.toHuman() +
204                    " but found " + args[2].getType().toHuman());
205        }
206    }
207
208    /** {@inheritDoc} */
209    public final void localArg(Frame frame, int idx) {
210        clearArgs();
211        args[0] = frame.getLocals().get(idx);
212        argCount = 1;
213        localIndex = idx;
214    }
215
216    /** {@inheritDoc} */
217    public final void localInfo(boolean local) {
218        localInfo = local;
219    }
220
221    /** {@inheritDoc} */
222    public final void auxType(Type type) {
223        auxType = type;
224    }
225
226    /** {@inheritDoc} */
227    public final void auxIntArg(int value) {
228        auxInt = value;
229    }
230
231    /** {@inheritDoc} */
232    public final void auxCstArg(Constant cst) {
233        if (cst == null) {
234            throw new NullPointerException("cst == null");
235        }
236
237        auxCst = cst;
238    }
239
240    /** {@inheritDoc} */
241    public final void auxTargetArg(int target) {
242        auxTarget = target;
243    }
244
245    /** {@inheritDoc} */
246    public final void auxSwitchArg(SwitchList cases) {
247        if (cases == null) {
248            throw new NullPointerException("cases == null");
249        }
250
251        auxCases = cases;
252    }
253
254    /** {@inheritDoc} */
255    public final void auxInitValues(ArrayList<Constant> initValues) {
256        auxInitValues = initValues;
257    }
258
259    /** {@inheritDoc} */
260    public final void localTarget(int idx, Type type, LocalItem local) {
261        localTarget = RegisterSpec.makeLocalOptional(idx, type, local);
262    }
263
264    /**
265     * Gets the number of primary arguments.
266     *
267     * @return {@code >= 0;} the number of primary arguments
268     */
269    protected final int argCount() {
270        return argCount;
271    }
272
273    /**
274     * Gets the width of the arguments (where a category-2 value counts as
275     * two).
276     *
277     * @return {@code >= 0;} the argument width
278     */
279    protected final int argWidth() {
280        int result = 0;
281
282        for (int i = 0; i < argCount; i++) {
283            result += args[i].getType().getCategory();
284        }
285
286        return result;
287    }
288
289    /**
290     * Gets the {@code n}th primary argument.
291     *
292     * @param n {@code >= 0, < argCount();} which argument
293     * @return {@code non-null;} the indicated argument
294     */
295    protected final TypeBearer arg(int n) {
296        if (n >= argCount) {
297            throw new IllegalArgumentException("n >= argCount");
298        }
299
300        try {
301            return args[n];
302        } catch (ArrayIndexOutOfBoundsException ex) {
303            // Translate the exception.
304            throw new IllegalArgumentException("n < 0");
305        }
306    }
307
308    /**
309     * Gets the type auxiliary argument.
310     *
311     * @return {@code null-ok;} the salient type
312     */
313    protected final Type getAuxType() {
314        return auxType;
315    }
316
317    /**
318     * Gets the {@code int} auxiliary argument.
319     *
320     * @return the argument value
321     */
322    protected final int getAuxInt() {
323        return auxInt;
324    }
325
326    /**
327     * Gets the constant auxiliary argument.
328     *
329     * @return {@code null-ok;} the argument value
330     */
331    protected final Constant getAuxCst() {
332        return auxCst;
333    }
334
335    /**
336     * Gets the branch target auxiliary argument.
337     *
338     * @return the argument value
339     */
340    protected final int getAuxTarget() {
341        return auxTarget;
342    }
343
344    /**
345     * Gets the switch cases auxiliary argument.
346     *
347     * @return {@code null-ok;} the argument value
348     */
349    protected final SwitchList getAuxCases() {
350        return auxCases;
351    }
352
353    /**
354     * Gets the init values auxiliary argument.
355     *
356     * @return {@code null-ok;} the argument value
357     */
358    protected final ArrayList<Constant> getInitValues() {
359        return auxInitValues;
360    }
361    /**
362     * Gets the last local index accessed.
363     *
364     * @return {@code >= -1;} the salient local index or {@code -1} if none
365     * was set since the last time {@link #clearArgs} was called
366     */
367    protected final int getLocalIndex() {
368        return localIndex;
369    }
370
371    /**
372     * Gets whether the loaded local has info in the local variable table.
373     *
374     * @return {@code true} if local arg has info in the local variable table
375     */
376    protected final boolean getLocalInfo() {
377        return localInfo;
378    }
379
380    /**
381     * Gets the target local register spec of the current operation, if any.
382     * The local target spec is the combination of the values indicated
383     * by a previous call to {@link #localTarget} with the type of what
384     * should be the sole result set by a call to {@link #setResult} (or
385     * the combination {@link #clearResult} then {@link #addResult}.
386     *
387     * @param isMove {@code true} if the operation being performed on the
388     * local is a move. This will cause constant values to be propagated
389     * to the returned local
390     * @return {@code null-ok;} the salient register spec or {@code null} if no
391     * local target was set since the last time {@link #clearArgs} was
392     * called
393     */
394    protected final RegisterSpec getLocalTarget(boolean isMove) {
395        if (localTarget == null) {
396            return null;
397        }
398
399        if (resultCount != 1) {
400            throw new SimException("local target with " +
401                    ((resultCount == 0) ? "no" : "multiple") + " results");
402        }
403
404        TypeBearer result = results[0];
405        Type resultType = result.getType();
406        Type localType = localTarget.getType();
407
408        if (resultType == localType) {
409            /*
410             * If this is to be a move operation and the result is a
411             * known value, make the returned localTarget embody that
412             * value.
413             */
414            if (isMove) {
415                return localTarget.withType(result);
416            } else {
417                return localTarget;
418            }
419        }
420
421        if (! Merger.isPossiblyAssignableFrom(localType, resultType)) {
422            // The result and local types are inconsistent. Complain!
423            throwLocalMismatch(resultType, localType);
424            return null;
425        }
426
427        if (localType == Type.OBJECT) {
428            /*
429             * The result type is more specific than the local type,
430             * so use that instead.
431             */
432            localTarget = localTarget.withType(result);
433        }
434
435        return localTarget;
436    }
437
438    /**
439     * Clears the results.
440     */
441    protected final void clearResult() {
442        resultCount = 0;
443    }
444
445    /**
446     * Sets the results list to be the given single value.
447     *
448     * <p><b>Note:</b> If there is more than one result value, the
449     * others may be added by using {@link #addResult}.</p>
450     *
451     * @param result {@code non-null;} result value
452     */
453    protected final void setResult(TypeBearer result) {
454        if (result == null) {
455            throw new NullPointerException("result == null");
456        }
457
458        results[0] = result;
459        resultCount = 1;
460    }
461
462    /**
463     * Adds an additional element to the list of results.
464     *
465     * @see #setResult
466     *
467     * @param result {@code non-null;} result value
468     */
469    protected final void addResult(TypeBearer result) {
470        if (result == null) {
471            throw new NullPointerException("result == null");
472        }
473
474        results[resultCount] = result;
475        resultCount++;
476    }
477
478    /**
479     * Gets the count of results. This throws an exception if results were
480     * never set. (Explicitly clearing the results counts as setting them.)
481     *
482     * @return {@code >= 0;} the count
483     */
484    protected final int resultCount() {
485        if (resultCount < 0) {
486            throw new SimException("results never set");
487        }
488
489        return resultCount;
490    }
491
492    /**
493     * Gets the width of the results (where a category-2 value counts as
494     * two).
495     *
496     * @return {@code >= 0;} the result width
497     */
498    protected final int resultWidth() {
499        int width = 0;
500
501        for (int i = 0; i < resultCount; i++) {
502            width += results[i].getType().getCategory();
503        }
504
505        return width;
506    }
507
508    /**
509     * Gets the {@code n}th result value.
510     *
511     * @param n {@code >= 0, < resultCount();} which result
512     * @return {@code non-null;} the indicated result value
513     */
514    protected final TypeBearer result(int n) {
515        if (n >= resultCount) {
516            throw new IllegalArgumentException("n >= resultCount");
517        }
518
519        try {
520            return results[n];
521        } catch (ArrayIndexOutOfBoundsException ex) {
522            // Translate the exception.
523            throw new IllegalArgumentException("n < 0");
524        }
525    }
526
527    /**
528     * Stores the results of the latest operation into the given frame. If
529     * there is a local target (see {@link #localTarget}), then the sole
530     * result is stored to that target; otherwise any results are pushed
531     * onto the stack.
532     *
533     * @param frame {@code non-null;} frame to operate on
534     */
535    protected final void storeResults(Frame frame) {
536        if (resultCount < 0) {
537            throw new SimException("results never set");
538        }
539
540        if (resultCount == 0) {
541            // Nothing to do.
542            return;
543        }
544
545        if (localTarget != null) {
546            /*
547             * Note: getLocalTarget() doesn't necessarily return
548             * localTarget directly.
549             */
550            frame.getLocals().set(getLocalTarget(false));
551        } else {
552            ExecutionStack stack = frame.getStack();
553            for (int i = 0; i < resultCount; i++) {
554                if (localInfo) {
555                    stack.setLocal();
556                }
557                stack.push(results[i]);
558            }
559        }
560    }
561
562    /**
563     * Throws an exception that indicates a mismatch in local variable
564     * types.
565     *
566     * @param found {@code non-null;} the encountered type
567     * @param local {@code non-null;} the local variable's claimed type
568     */
569    public static void throwLocalMismatch(TypeBearer found,
570            TypeBearer local) {
571        throw new SimException("local variable type mismatch: " +
572                "attempt to set or access a value of type " +
573                found.toHuman() +
574                " using a local variable of type " +
575                local.toHuman() +
576                ". This is symptomatic of .class transformation tools " +
577                "that ignore local variable information.");
578    }
579}
580