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 
17 package com.android.dx.cf.code;
18 
19 import com.android.dx.rop.code.LocalItem;
20 import com.android.dx.rop.code.RegisterSpec;
21 import com.android.dx.rop.cst.Constant;
22 import com.android.dx.rop.type.Prototype;
23 import com.android.dx.rop.type.StdTypeList;
24 import com.android.dx.rop.type.Type;
25 import com.android.dx.rop.type.TypeBearer;
26 import 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  */
35 public 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      */
BaseMachine(Prototype prototype)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} */
getPrototype()99     public Prototype getPrototype() {
100         return prototype;
101     }
102 
103     /** {@inheritDoc} */
clearArgs()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} */
popArgs(Frame frame, int count)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} */
popArgs(Frame frame, Prototype prototype)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 
popArgs(Frame frame, Type type)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} */
popArgs(Frame frame, Type type1, Type type2)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} */
popArgs(Frame frame, Type type1, Type type2, Type type3)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} */
localArg(Frame frame, int idx)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} */
localInfo(boolean local)217     public final void localInfo(boolean local) {
218         localInfo = local;
219     }
220 
221     /** {@inheritDoc} */
auxType(Type type)222     public final void auxType(Type type) {
223         auxType = type;
224     }
225 
226     /** {@inheritDoc} */
auxIntArg(int value)227     public final void auxIntArg(int value) {
228         auxInt = value;
229     }
230 
231     /** {@inheritDoc} */
auxCstArg(Constant cst)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} */
auxTargetArg(int target)241     public final void auxTargetArg(int target) {
242         auxTarget = target;
243     }
244 
245     /** {@inheritDoc} */
auxSwitchArg(SwitchList cases)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} */
auxInitValues(ArrayList<Constant> initValues)255     public final void auxInitValues(ArrayList<Constant> initValues) {
256         auxInitValues = initValues;
257     }
258 
259     /** {@inheritDoc} */
localTarget(int idx, Type type, LocalItem local)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      */
argCount()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      */
argWidth()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      */
arg(int n)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      */
getAuxType()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      */
getAuxInt()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      */
getAuxCst()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      */
getAuxTarget()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      */
getAuxCases()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      */
getInitValues()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      */
getLocalIndex()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      */
getLocalInfo()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      */
getLocalTarget(boolean isMove)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      */
clearResult()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      */
setResult(TypeBearer result)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      */
addResult(TypeBearer result)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      */
resultCount()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      */
resultWidth()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      */
result(int n)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      */
storeResults(Frame frame)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      */
throwLocalMismatch(TypeBearer found, TypeBearer local)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