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