• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.dexgen.rop.code;
18 
19 import com.android.dexgen.rop.cst.Constant;
20 import com.android.dexgen.rop.cst.CstUtf8;
21 import com.android.dexgen.rop.type.Type;
22 import com.android.dexgen.rop.type.TypeBearer;
23 import com.android.dexgen.util.ToHuman;
24 
25 import java.util.HashMap;
26 
27 /**
28  * Combination of a register number and a type, used as the sources and
29  * destinations of register-based operations.
30  */
31 public final class RegisterSpec
32         implements TypeBearer, ToHuman, Comparable<RegisterSpec> {
33     /** {@code non-null;} string to prefix register numbers with */
34     public static final String PREFIX = "v";
35 
36     /** {@code non-null;} intern table for instances */
37     private static final HashMap<Object, RegisterSpec> theInterns =
38         new HashMap<Object, RegisterSpec>(1000);
39 
40     /** {@code non-null;} common comparison instance used while interning */
41     private static final ForComparison theInterningItem = new ForComparison();
42 
43     /** {@code >= 0;} register number */
44     private final int reg;
45 
46     /** {@code non-null;} type loaded or stored */
47     private final TypeBearer type;
48 
49     /** {@code null-ok;} local variable info associated with this register, if any */
50     private final LocalItem local;
51 
52     /**
53      * Intern the given triple as an instance of this class.
54      *
55      * @param reg {@code >= 0;} the register number
56      * @param type {@code non-null;} the type (or possibly actual value) which
57      * is loaded from or stored to the indicated register
58      * @param local {@code null-ok;} the associated local variable, if any
59      * @return {@code non-null;} an appropriately-constructed instance
60      */
intern(int reg, TypeBearer type, LocalItem local)61     private static RegisterSpec intern(int reg, TypeBearer type,
62             LocalItem local) {
63         theInterningItem.set(reg, type, local);
64         RegisterSpec found = theInterns.get(theInterningItem);
65 
66         if (found != null) {
67             return found;
68         }
69 
70         found = theInterningItem.toRegisterSpec();
71         theInterns.put(found, found);
72         return found;
73     }
74 
75     /**
76      * Returns an instance for the given register number and type, with
77      * no variable info. This method is allowed to return shared
78      * instances (but doesn't necessarily do so).
79      *
80      * @param reg {@code >= 0;} the register number
81      * @param type {@code non-null;} the type (or possibly actual value) which
82      * is loaded from or stored to the indicated register
83      * @return {@code non-null;} an appropriately-constructed instance
84      */
make(int reg, TypeBearer type)85     public static RegisterSpec make(int reg, TypeBearer type) {
86         return intern(reg, type, null);
87     }
88 
89     /**
90      * Returns an instance for the given register number, type, and
91      * variable info. This method is allowed to return shared
92      * instances (but doesn't necessarily do so).
93      *
94      * @param reg {@code >= 0;} the register number
95      * @param type {@code non-null;} the type (or possibly actual value) which
96      * is loaded from or stored to the indicated register
97      * @param local {@code non-null;} the associated local variable
98      * @return {@code non-null;} an appropriately-constructed instance
99      */
make(int reg, TypeBearer type, LocalItem local)100     public static RegisterSpec make(int reg, TypeBearer type,
101             LocalItem local) {
102         if (local == null) {
103             throw new NullPointerException("local  == null");
104         }
105 
106         return intern(reg, type, local);
107     }
108 
109     /**
110      * Returns an instance for the given register number, type, and
111      * variable info. This method is allowed to return shared
112      * instances (but doesn't necessarily do so).
113      *
114      * @param reg {@code >= 0;} the register number
115      * @param type {@code non-null;} the type (or possibly actual value) which
116      * is loaded from or stored to the indicated register
117      * @param local {@code null-ok;} the associated variable info or null for
118      * none
119      * @return {@code non-null;} an appropriately-constructed instance
120      */
makeLocalOptional( int reg, TypeBearer type, LocalItem local)121     public static RegisterSpec makeLocalOptional(
122             int reg, TypeBearer type, LocalItem local) {
123 
124         return intern(reg, type, local);
125     }
126 
127     /**
128      * Gets the string form for the given register number.
129      *
130      * @param reg {@code >= 0;} the register number
131      * @return {@code non-null;} the string form
132      */
regString(int reg)133     public static String regString(int reg) {
134         return PREFIX + reg;
135     }
136 
137     /**
138      * Constructs an instance. This constructor is private. Use
139      * {@link #make}.
140      *
141      * @param reg {@code >= 0;} the register number
142      * @param type {@code non-null;} the type (or possibly actual value) which
143      * is loaded from or stored to the indicated register
144      * @param local {@code null-ok;} the associated local variable, if any
145      */
RegisterSpec(int reg, TypeBearer type, LocalItem local)146     private RegisterSpec(int reg, TypeBearer type, LocalItem local) {
147         if (reg < 0) {
148             throw new IllegalArgumentException("reg < 0");
149         }
150 
151         if (type == null) {
152             throw new NullPointerException("type == null");
153         }
154 
155         this.reg = reg;
156         this.type = type;
157         this.local = local;
158     }
159 
160     /** {@inheritDoc} */
161     @Override
equals(Object other)162     public boolean equals(Object other) {
163         if (!(other instanceof RegisterSpec)) {
164             if (other instanceof ForComparison) {
165                 ForComparison fc = (ForComparison) other;
166                 return equals(fc.reg, fc.type, fc.local);
167             }
168             return false;
169         }
170 
171         RegisterSpec spec = (RegisterSpec) other;
172         return equals(spec.reg, spec.type, spec.local);
173     }
174 
175     /**
176      * Like {@code equals}, but only consider the simple types of the
177      * registers. That is, this compares {@code getType()} on the types
178      * to ignore whatever arbitrary extra stuff might be carried around
179      * by an outer {@link TypeBearer}.
180      *
181      * @param other {@code null-ok;} spec to compare to
182      * @return {@code true} iff {@code this} and {@code other} are equal
183      * in the stated way
184      */
equalsUsingSimpleType(RegisterSpec other)185     public boolean equalsUsingSimpleType(RegisterSpec other) {
186         if (!matchesVariable(other)) {
187             return false;
188         }
189 
190         return (reg == other.reg);
191     }
192 
193     /**
194      * Like {@link #equalsUsingSimpleType} but ignoring the register number.
195      * This is useful to determine if two instances refer to the "same"
196      * local variable.
197      *
198      * @param other {@code null-ok;} spec to compare to
199      * @return {@code true} iff {@code this} and {@code other} are equal
200      * in the stated way
201      */
matchesVariable(RegisterSpec other)202     public boolean matchesVariable(RegisterSpec other) {
203         if (other == null) {
204             return false;
205         }
206 
207         return type.getType().equals(other.type.getType())
208             && ((local == other.local)
209                     || ((local != null) && local.equals(other.local)));
210     }
211 
212     /**
213      * Helper for {@link #equals} and {@link #ForComparison.equals},
214      * which actually does the test.
215      *
216      * @param reg value of the instance variable, for another instance
217      * @param type value of the instance variable, for another instance
218      * @param local value of the instance variable, for another instance
219      * @return whether this instance is equal to one with the given
220      * values
221      */
equals(int reg, TypeBearer type, LocalItem local)222     private boolean equals(int reg, TypeBearer type, LocalItem local) {
223         return (this.reg == reg)
224             && this.type.equals(type)
225             && ((this.local == local)
226                     || ((this.local != null) && this.local.equals(local)));
227     }
228 
229     /**
230      * Compares by (in priority order) register number, unwrapped type
231      * (that is types not {@link TypeBearer}s, and local info.
232      *
233      * @param other {@code non-null;} spec to compare to
234      * @return {@code -1..1;} standard result of comparison
235      */
compareTo(RegisterSpec other)236     public int compareTo(RegisterSpec other) {
237         if (this.reg < other.reg) {
238             return -1;
239         } else if (this.reg > other.reg) {
240             return 1;
241         }
242 
243         int compare = type.getType().compareTo(other.type.getType());
244 
245         if (compare != 0) {
246             return compare;
247         }
248 
249         if (this.local == null) {
250             return (other.local == null) ? 0 : -1;
251         } else if (other.local == null) {
252             return 1;
253         }
254 
255         return this.local.compareTo(other.local);
256     }
257 
258     /** {@inheritDoc} */
259     @Override
hashCode()260     public int hashCode() {
261         return hashCodeOf(reg, type, local);
262     }
263 
264     /**
265      * Helper for {@link #hashCode} and {@link #ForComparison.hashCode},
266      * which actually does the calculation.
267      *
268      * @param reg value of the instance variable
269      * @param type value of the instance variable
270      * @param local value of the instance variable
271      * @return the hash code
272      */
hashCodeOf(int reg, TypeBearer type, LocalItem local)273     private static int hashCodeOf(int reg, TypeBearer type, LocalItem local) {
274         int hash = (local != null) ? local.hashCode() : 0;
275 
276         hash = (hash * 31 + type.hashCode()) * 31 + reg;
277         return hash;
278     }
279 
280     /** {@inheritDoc} */
281     @Override
toString()282     public String toString() {
283         return toString0(false);
284     }
285 
286     /** {@inheritDoc} */
toHuman()287     public String toHuman() {
288         return toString0(true);
289     }
290 
291     /** {@inheritDoc} */
getType()292     public Type getType() {
293         return type.getType();
294     }
295 
296     /** {@inheritDoc} */
getFrameType()297     public TypeBearer getFrameType() {
298         return type.getFrameType();
299     }
300 
301     /** {@inheritDoc} */
getBasicType()302     public final int getBasicType() {
303         return type.getBasicType();
304     }
305 
306     /** {@inheritDoc} */
getBasicFrameType()307     public final int getBasicFrameType() {
308         return type.getBasicFrameType();
309     }
310 
311     /** {@inheritDoc} */
isConstant()312     public final boolean isConstant() {
313         return false;
314     }
315 
316     /**
317      * Gets the register number.
318      *
319      * @return {@code >= 0;} the register number
320      */
getReg()321     public int getReg() {
322         return reg;
323     }
324 
325     /**
326      * Gets the type (or actual value) which is loaded from or stored
327      * to the register associated with this instance.
328      *
329      * @return {@code non-null;} the type
330      */
getTypeBearer()331     public TypeBearer getTypeBearer() {
332         return type;
333     }
334 
335     /**
336      * Gets the variable info associated with this instance, if any.
337      *
338      * @return {@code null-ok;} the variable info, or {@code null} if this
339      * instance has none
340      */
getLocalItem()341     public LocalItem getLocalItem() {
342         return local;
343     }
344 
345     /**
346      * Gets the next available register number after the one in this
347      * instance. This is equal to the register number plus the width
348      * (category) of the type used. Among other things, this may also
349      * be used to determine the minimum required register count
350      * implied by this instance.
351      *
352      * @return {@code >= 0;} the required registers size
353      */
getNextReg()354     public int getNextReg() {
355         return reg + getCategory();
356     }
357 
358     /**
359      * Gets the category of this instance's type. This is just a convenient
360      * shorthand for {@code getType().getCategory()}.
361      *
362      * @see #isCategory1
363      * @see #isCategory2
364      * @return {@code 1..2;} the category of this instance's type
365      */
getCategory()366     public int getCategory() {
367         return type.getType().getCategory();
368     }
369 
370     /**
371      * Gets whether this instance's type is category 1. This is just a
372      * convenient shorthand for {@code getType().isCategory1()}.
373      *
374      * @see #getCategory
375      * @see #isCategory2
376      * @return whether or not this instance's type is of category 1
377      */
isCategory1()378     public boolean isCategory1() {
379         return type.getType().isCategory1();
380     }
381 
382     /**
383      * Gets whether this instance's type is category 2. This is just a
384      * convenient shorthand for {@code getType().isCategory2()}.
385      *
386      * @see #getCategory
387      * @see #isCategory1
388      * @return whether or not this instance's type is of category 2
389      */
isCategory2()390     public boolean isCategory2() {
391         return type.getType().isCategory2();
392     }
393 
394     /**
395      * Gets the string form for just the register number of this instance.
396      *
397      * @return {@code non-null;} the register string form
398      */
regString()399     public String regString() {
400         return regString(reg);
401     }
402 
403     /**
404      * Returns an instance that is the intersection between this instance
405      * and the given one, if any. The intersection is defined as follows:
406      *
407      * <ul>
408      *   <li>If {@code other} is {@code null}, then the result
409      *     is {@code null}.
410      *   <li>If the register numbers don't match, then the intersection
411      *     is {@code null}. Otherwise, the register number of the
412      *     intersection is the same as the one in the two instances.</li>
413      *   <li>If the types returned by {@code getType()} are not
414      *     {@code equals()}, then the intersection is null.</li>
415      *   <li>If the type bearers returned by {@code getTypeBearer()}
416      *     are {@code equals()}, then the intersection's type bearer
417      *     is the one from this instance. Otherwise, the intersection's
418      *     type bearer is the {@code getType()} of this instance.</li>
419      *   <li>If the locals are {@code equals()}, then the local info
420      *     of the intersection is the local info of this instance. Otherwise,
421      *     the local info of the intersection is {@code null}.</li>
422      * </ul>
423      *
424      * @param other {@code null-ok;} instance to intersect with (or {@code null})
425      * @param localPrimary whether local variables are primary to the
426      * intersection; if {@code true}, then the only non-null
427      * results occur when registers being intersected have equal local
428      * infos (or both have {@code null} local infos)
429      * @return {@code null-ok;} the intersection
430      */
intersect(RegisterSpec other, boolean localPrimary)431     public RegisterSpec intersect(RegisterSpec other, boolean localPrimary) {
432         if (this == other) {
433             // Easy out.
434             return this;
435         }
436 
437         if ((other == null) || (reg != other.getReg())) {
438             return null;
439         }
440 
441         LocalItem resultLocal =
442             ((local == null) || !local.equals(other.getLocalItem()))
443             ? null : local;
444         boolean sameName = (resultLocal == local);
445 
446         if (localPrimary && !sameName) {
447             return null;
448         }
449 
450         Type thisType = getType();
451         Type otherType = other.getType();
452 
453         // Note: Types are always interned.
454         if (thisType != otherType) {
455             return null;
456         }
457 
458         TypeBearer resultTypeBearer =
459             type.equals(other.getTypeBearer()) ? type : thisType;
460 
461         if ((resultTypeBearer == type) && sameName) {
462             // It turns out that the intersection is "this" after all.
463             return this;
464         }
465 
466         return (resultLocal == null) ? make(reg, resultTypeBearer) :
467             make(reg, resultTypeBearer, resultLocal);
468     }
469 
470     /**
471      * Returns an instance that is identical to this one, except that the
472      * register number is replaced by the given one.
473      *
474      * @param newReg {@code >= 0;} the new register number
475      * @return {@code non-null;} an appropriately-constructed instance
476      */
withReg(int newReg)477     public RegisterSpec withReg(int newReg) {
478         if (reg == newReg) {
479             return this;
480         }
481 
482         return makeLocalOptional(newReg, type, local);
483     }
484 
485     /**
486      * Returns an instance that is identical to this one, except that
487      * the type is replaced by the given one.
488      *
489      * @param newType {@code non-null;} the new type
490      * @return {@code non-null;} an appropriately-constructed instance
491      */
withType(TypeBearer newType)492     public RegisterSpec withType(TypeBearer newType) {
493         return makeLocalOptional(reg, newType, local);
494     }
495 
496     /**
497      * Returns an instance that is identical to this one, except that the
498      * register number is offset by the given amount.
499      *
500      * @param delta the amount to offset the register number by
501      * @return {@code non-null;} an appropriately-constructed instance
502      */
withOffset(int delta)503     public RegisterSpec withOffset(int delta) {
504         if (delta == 0) {
505             return this;
506         }
507 
508         return withReg(reg + delta);
509     }
510 
511     /**
512      * Returns an instance that is identical to this one, except that
513      * the type bearer is replaced by the actual underlying type
514      * (thereby stripping off non-type information) with any
515      * initialization information stripped away as well.
516      *
517      * @return {@code non-null;} an appropriately-constructed instance
518      */
withSimpleType()519     public RegisterSpec withSimpleType() {
520         TypeBearer orig = type;
521         Type newType;
522 
523         if (orig instanceof Type) {
524             newType = (Type) orig;
525         } else {
526             newType = orig.getType();
527         }
528 
529         if (newType.isUninitialized()) {
530             newType = newType.getInitializedType();
531         }
532 
533         if (newType == orig) {
534             return this;
535         }
536 
537         return makeLocalOptional(reg, newType, local);
538     }
539 
540     /**
541      * Returns an instance that is identical to this one except that the
542      * local variable is as specified in the parameter.
543      *
544      * @param local {@code null-ok;} the local item or null for none
545      * @return an appropriate instance
546      */
withLocalItem(LocalItem local)547     public RegisterSpec withLocalItem(LocalItem local) {
548         if ((this.local== local)
549                     || ((this.local != null) && this.local.equals(local))) {
550 
551             return this;
552         }
553 
554         return makeLocalOptional(reg, type, local);
555     }
556 
557 
558     /**
559      * Helper for {@link #toString} and {@link #toHuman}.
560      *
561      * @param human whether to be human-oriented
562      * @return {@code non-null;} the string form
563      */
toString0(boolean human)564     private String toString0(boolean human) {
565         StringBuffer sb = new StringBuffer(40);
566 
567         sb.append(regString());
568         sb.append(":");
569 
570         if (local != null) {
571             sb.append(local.toString());
572         }
573 
574         Type justType = type.getType();
575         sb.append(justType);
576 
577         if (justType != type) {
578             sb.append("=");
579             if (human && (type instanceof Constant)) {
580                 sb.append(((Constant) type).toHuman());
581             } else {
582                 sb.append(type);
583             }
584         }
585 
586         return sb.toString();
587     }
588 
589     /**
590      * Holder of register spec data for the purposes of comparison (so that
591      * {@code RegisterSpec} itself can still keep {@code final}
592      * instance variables.
593      */
594     private static class ForComparison {
595         /** {@code >= 0;} register number */
596         private int reg;
597 
598         /** {@code non-null;} type loaded or stored */
599         private TypeBearer type;
600 
601         /**
602          * {@code null-ok;} local variable associated with this
603          * register, if any
604          */
605         private LocalItem local;
606 
607         /**
608          * Set all the instance variables.
609          *
610          * @param reg {@code >= 0;} the register number
611          * @param type {@code non-null;} the type (or possibly actual
612          * value) which is loaded from or stored to the indicated
613          * register
614          * @param local {@code null-ok;} the associated local variable, if any
615          * @return {@code non-null;} an appropriately-constructed instance
616          */
set(int reg, TypeBearer type, LocalItem local)617         public void set(int reg, TypeBearer type, LocalItem local) {
618             this.reg = reg;
619             this.type = type;
620             this.local = local;
621         }
622 
623         /**
624          * Construct a {@code RegisterSpec} of this instance's
625          * contents.
626          *
627          * @return {@code non-null;} an appropriately-constructed instance
628          */
toRegisterSpec()629         public RegisterSpec toRegisterSpec() {
630             return new RegisterSpec(reg, type, local);
631         }
632 
633         /** {@inheritDoc} */
634         @Override
equals(Object other)635         public boolean equals(Object other) {
636             if (!(other instanceof RegisterSpec)) {
637                 return false;
638             }
639 
640             RegisterSpec spec = (RegisterSpec) other;
641             return spec.equals(reg, type, local);
642         }
643 
644         /** {@inheritDoc} */
645         @Override
hashCode()646         public int hashCode() {
647             return hashCodeOf(reg, type, local);
648         }
649     }
650 }
651