--- /dev/null
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeBearer;
+import com.android.dexgen.util.ToHuman;
+
+import java.util.HashMap;
+
+/**
+ * Combination of a register number and a type, used as the sources and
+ * destinations of register-based operations.
+ */
+public final class RegisterSpec
+ implements TypeBearer, ToHuman, Comparable<RegisterSpec> {
+ /** {@code non-null;} string to prefix register numbers with */
+ public static final String PREFIX = "v";
+
+ /** {@code non-null;} intern table for instances */
+ private static final HashMap<Object, RegisterSpec> theInterns =
+ new HashMap<Object, RegisterSpec>(1000);
+
+ /** {@code non-null;} common comparison instance used while interning */
+ private static final ForComparison theInterningItem = new ForComparison();
+
+ /** {@code >= 0;} register number */
+ private final int reg;
+
+ /** {@code non-null;} type loaded or stored */
+ private final TypeBearer type;
+
+ /** {@code null-ok;} local variable info associated with this register, if any */
+ private final LocalItem local;
+
+ /**
+ * Intern the given triple as an instance of this class.
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual value) which
+ * is loaded from or stored to the indicated register
+ * @param local {@code null-ok;} the associated local variable, if any
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ private static RegisterSpec intern(int reg, TypeBearer type,
+ LocalItem local) {
+ theInterningItem.set(reg, type, local);
+ RegisterSpec found = theInterns.get(theInterningItem);
+
+ if (found != null) {
+ return found;
+ }
+
+ found = theInterningItem.toRegisterSpec();
+ theInterns.put(found, found);
+ return found;
+ }
+
+ /**
+ * Returns an instance for the given register number and type, with
+ * no variable info. This method is allowed to return shared
+ * instances (but doesn't necessarily do so).
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual value) which
+ * is loaded from or stored to the indicated register
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static RegisterSpec make(int reg, TypeBearer type) {
+ return intern(reg, type, null);
+ }
+
+ /**
+ * Returns an instance for the given register number, type, and
+ * variable info. This method is allowed to return shared
+ * instances (but doesn't necessarily do so).
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual value) which
+ * is loaded from or stored to the indicated register
+ * @param local {@code non-null;} the associated local variable
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static RegisterSpec make(int reg, TypeBearer type,
+ LocalItem local) {
+ if (local == null) {
+ throw new NullPointerException("local == null");
+ }
+
+ return intern(reg, type, local);
+ }
+
+ /**
+ * Returns an instance for the given register number, type, and
+ * variable info. This method is allowed to return shared
+ * instances (but doesn't necessarily do so).
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual value) which
+ * is loaded from or stored to the indicated register
+ * @param local {@code null-ok;} the associated variable info or null for
+ * none
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static RegisterSpec makeLocalOptional(
+ int reg, TypeBearer type, LocalItem local) {
+
+ return intern(reg, type, local);
+ }
+
+ /**
+ * Gets the string form for the given register number.
+ *
+ * @param reg {@code >= 0;} the register number
+ * @return {@code non-null;} the string form
+ */
+ public static String regString(int reg) {
+ return PREFIX + reg;
+ }
+
+ /**
+ * Constructs an instance. This constructor is private. Use
+ * {@link #make}.
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual value) which
+ * is loaded from or stored to the indicated register
+ * @param local {@code null-ok;} the associated local variable, if any
+ */
+ private RegisterSpec(int reg, TypeBearer type, LocalItem local) {
+ if (reg < 0) {
+ throw new IllegalArgumentException("reg < 0");
+ }
+
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ this.reg = reg;
+ this.type = type;
+ this.local = local;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RegisterSpec)) {
+ if (other instanceof ForComparison) {
+ ForComparison fc = (ForComparison) other;
+ return equals(fc.reg, fc.type, fc.local);
+ }
+ return false;
+ }
+
+ RegisterSpec spec = (RegisterSpec) other;
+ return equals(spec.reg, spec.type, spec.local);
+ }
+
+ /**
+ * Like {@code equals}, but only consider the simple types of the
+ * registers. That is, this compares {@code getType()} on the types
+ * to ignore whatever arbitrary extra stuff might be carried around
+ * by an outer {@link TypeBearer}.
+ *
+ * @param other {@code null-ok;} spec to compare to
+ * @return {@code true} iff {@code this} and {@code other} are equal
+ * in the stated way
+ */
+ public boolean equalsUsingSimpleType(RegisterSpec other) {
+ if (!matchesVariable(other)) {
+ return false;
+ }
+
+ return (reg == other.reg);
+ }
+
+ /**
+ * Like {@link #equalsUsingSimpleType} but ignoring the register number.
+ * This is useful to determine if two instances refer to the "same"
+ * local variable.
+ *
+ * @param other {@code null-ok;} spec to compare to
+ * @return {@code true} iff {@code this} and {@code other} are equal
+ * in the stated way
+ */
+ public boolean matchesVariable(RegisterSpec other) {
+ if (other == null) {
+ return false;
+ }
+
+ return type.getType().equals(other.type.getType())
+ && ((local == other.local)
+ || ((local != null) && local.equals(other.local)));
+ }
+
+ /**
+ * Helper for {@link #equals} and {@link #ForComparison.equals},
+ * which actually does the test.
+ *
+ * @param reg value of the instance variable, for another instance
+ * @param type value of the instance variable, for another instance
+ * @param local value of the instance variable, for another instance
+ * @return whether this instance is equal to one with the given
+ * values
+ */
+ private boolean equals(int reg, TypeBearer type, LocalItem local) {
+ return (this.reg == reg)
+ && this.type.equals(type)
+ && ((this.local == local)
+ || ((this.local != null) && this.local.equals(local)));
+ }
+
+ /**
+ * Compares by (in priority order) register number, unwrapped type
+ * (that is types not {@link TypeBearer}s, and local info.
+ *
+ * @param other {@code non-null;} spec to compare to
+ * @return {@code -1..1;} standard result of comparison
+ */
+ public int compareTo(RegisterSpec other) {
+ if (this.reg < other.reg) {
+ return -1;
+ } else if (this.reg > other.reg) {
+ return 1;
+ }
+
+ int compare = type.getType().compareTo(other.type.getType());
+
+ if (compare != 0) {
+ return compare;
+ }
+
+ if (this.local == null) {
+ return (other.local == null) ? 0 : -1;
+ } else if (other.local == null) {
+ return 1;
+ }
+
+ return this.local.compareTo(other.local);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return hashCodeOf(reg, type, local);
+ }
+
+ /**
+ * Helper for {@link #hashCode} and {@link #ForComparison.hashCode},
+ * which actually does the calculation.
+ *
+ * @param reg value of the instance variable
+ * @param type value of the instance variable
+ * @param local value of the instance variable
+ * @return the hash code
+ */
+ private static int hashCodeOf(int reg, TypeBearer type, LocalItem local) {
+ int hash = (local != null) ? local.hashCode() : 0;
+
+ hash = (hash * 31 + type.hashCode()) * 31 + reg;
+ return hash;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return toString0(false);
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return toString0(true);
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return type.getType();
+ }
+
+ /** {@inheritDoc} */
+ public TypeBearer getFrameType() {
+ return type.getFrameType();
+ }
+
+ /** {@inheritDoc} */
+ public final int getBasicType() {
+ return type.getBasicType();
+ }
+
+ /** {@inheritDoc} */
+ public final int getBasicFrameType() {
+ return type.getBasicFrameType();
+ }
+
+ /** {@inheritDoc} */
+ public final boolean isConstant() {
+ return false;
+ }
+
+ /**
+ * Gets the register number.
+ *
+ * @return {@code >= 0;} the register number
+ */
+ public int getReg() {
+ return reg;
+ }
+
+ /**
+ * Gets the type (or actual value) which is loaded from or stored
+ * to the register associated with this instance.
+ *
+ * @return {@code non-null;} the type
+ */
+ public TypeBearer getTypeBearer() {
+ return type;
+ }
+
+ /**
+ * Gets the variable info associated with this instance, if any.
+ *
+ * @return {@code null-ok;} the variable info, or {@code null} if this
+ * instance has none
+ */
+ public LocalItem getLocalItem() {
+ return local;
+ }
+
+ /**
+ * Gets the next available register number after the one in this
+ * instance. This is equal to the register number plus the width
+ * (category) of the type used. Among other things, this may also
+ * be used to determine the minimum required register count
+ * implied by this instance.
+ *
+ * @return {@code >= 0;} the required registers size
+ */
+ public int getNextReg() {
+ return reg + getCategory();
+ }
+
+ /**
+ * Gets the category of this instance's type. This is just a convenient
+ * shorthand for {@code getType().getCategory()}.
+ *
+ * @see #isCategory1
+ * @see #isCategory2
+ * @return {@code 1..2;} the category of this instance's type
+ */
+ public int getCategory() {
+ return type.getType().getCategory();
+ }
+
+ /**
+ * Gets whether this instance's type is category 1. This is just a
+ * convenient shorthand for {@code getType().isCategory1()}.
+ *
+ * @see #getCategory
+ * @see #isCategory2
+ * @return whether or not this instance's type is of category 1
+ */
+ public boolean isCategory1() {
+ return type.getType().isCategory1();
+ }
+
+ /**
+ * Gets whether this instance's type is category 2. This is just a
+ * convenient shorthand for {@code getType().isCategory2()}.
+ *
+ * @see #getCategory
+ * @see #isCategory1
+ * @return whether or not this instance's type is of category 2
+ */
+ public boolean isCategory2() {
+ return type.getType().isCategory2();
+ }
+
+ /**
+ * Gets the string form for just the register number of this instance.
+ *
+ * @return {@code non-null;} the register string form
+ */
+ public String regString() {
+ return regString(reg);
+ }
+
+ /**
+ * Returns an instance that is the intersection between this instance
+ * and the given one, if any. The intersection is defined as follows:
+ *
+ * <ul>
+ * <li>If {@code other} is {@code null}, then the result
+ * is {@code null}.
+ * <li>If the register numbers don't match, then the intersection
+ * is {@code null}. Otherwise, the register number of the
+ * intersection is the same as the one in the two instances.</li>
+ * <li>If the types returned by {@code getType()} are not
+ * {@code equals()}, then the intersection is null.</li>
+ * <li>If the type bearers returned by {@code getTypeBearer()}
+ * are {@code equals()}, then the intersection's type bearer
+ * is the one from this instance. Otherwise, the intersection's
+ * type bearer is the {@code getType()} of this instance.</li>
+ * <li>If the locals are {@code equals()}, then the local info
+ * of the intersection is the local info of this instance. Otherwise,
+ * the local info of the intersection is {@code null}.</li>
+ * </ul>
+ *
+ * @param other {@code null-ok;} instance to intersect with (or {@code null})
+ * @param localPrimary whether local variables are primary to the
+ * intersection; if {@code true}, then the only non-null
+ * results occur when registers being intersected have equal local
+ * infos (or both have {@code null} local infos)
+ * @return {@code null-ok;} the intersection
+ */
+ public RegisterSpec intersect(RegisterSpec other, boolean localPrimary) {
+ if (this == other) {
+ // Easy out.
+ return this;
+ }
+
+ if ((other == null) || (reg != other.getReg())) {
+ return null;
+ }
+
+ LocalItem resultLocal =
+ ((local == null) || !local.equals(other.getLocalItem()))
+ ? null : local;
+ boolean sameName = (resultLocal == local);
+
+ if (localPrimary && !sameName) {
+ return null;
+ }
+
+ Type thisType = getType();
+ Type otherType = other.getType();
+
+ // Note: Types are always interned.
+ if (thisType != otherType) {
+ return null;
+ }
+
+ TypeBearer resultTypeBearer =
+ type.equals(other.getTypeBearer()) ? type : thisType;
+
+ if ((resultTypeBearer == type) && sameName) {
+ // It turns out that the intersection is "this" after all.
+ return this;
+ }
+
+ return (resultLocal == null) ? make(reg, resultTypeBearer) :
+ make(reg, resultTypeBearer, resultLocal);
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that the
+ * register number is replaced by the given one.
+ *
+ * @param newReg {@code >= 0;} the new register number
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpec withReg(int newReg) {
+ if (reg == newReg) {
+ return this;
+ }
+
+ return makeLocalOptional(newReg, type, local);
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * the type is replaced by the given one.
+ *
+ * @param newType {@code non-null;} the new type
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpec withType(TypeBearer newType) {
+ return makeLocalOptional(reg, newType, local);
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that the
+ * register number is offset by the given amount.
+ *
+ * @param delta the amount to offset the register number by
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpec withOffset(int delta) {
+ if (delta == 0) {
+ return this;
+ }
+
+ return withReg(reg + delta);
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * the type bearer is replaced by the actual underlying type
+ * (thereby stripping off non-type information) with any
+ * initialization information stripped away as well.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpec withSimpleType() {
+ TypeBearer orig = type;
+ Type newType;
+
+ if (orig instanceof Type) {
+ newType = (Type) orig;
+ } else {
+ newType = orig.getType();
+ }
+
+ if (newType.isUninitialized()) {
+ newType = newType.getInitializedType();
+ }
+
+ if (newType == orig) {
+ return this;
+ }
+
+ return makeLocalOptional(reg, newType, local);
+ }
+
+ /**
+ * Returns an instance that is identical to this one except that the
+ * local variable is as specified in the parameter.
+ *
+ * @param local {@code null-ok;} the local item or null for none
+ * @return an appropriate instance
+ */
+ public RegisterSpec withLocalItem(LocalItem local) {
+ if ((this.local== local)
+ || ((this.local != null) && this.local.equals(local))) {
+
+ return this;
+ }
+
+ return makeLocalOptional(reg, type, local);
+ }
+
+
+ /**
+ * Helper for {@link #toString} and {@link #toHuman}.
+ *
+ * @param human whether to be human-oriented
+ * @return {@code non-null;} the string form
+ */
+ private String toString0(boolean human) {
+ StringBuffer sb = new StringBuffer(40);
+
+ sb.append(regString());
+ sb.append(":");
+
+ if (local != null) {
+ sb.append(local.toString());
+ }
+
+ Type justType = type.getType();
+ sb.append(justType);
+
+ if (justType != type) {
+ sb.append("=");
+ if (human && (type instanceof Constant)) {
+ sb.append(((Constant) type).toHuman());
+ } else {
+ sb.append(type);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Holder of register spec data for the purposes of comparison (so that
+ * {@code RegisterSpec} itself can still keep {@code final}
+ * instance variables.
+ */
+ private static class ForComparison {
+ /** {@code >= 0;} register number */
+ private int reg;
+
+ /** {@code non-null;} type loaded or stored */
+ private TypeBearer type;
+
+ /**
+ * {@code null-ok;} local variable associated with this
+ * register, if any
+ */
+ private LocalItem local;
+
+ /**
+ * Set all the instance variables.
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual
+ * value) which is loaded from or stored to the indicated
+ * register
+ * @param local {@code null-ok;} the associated local variable, if any
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public void set(int reg, TypeBearer type, LocalItem local) {
+ this.reg = reg;
+ this.type = type;
+ this.local = local;
+ }
+
+ /**
+ * Construct a {@code RegisterSpec} of this instance's
+ * contents.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpec toRegisterSpec() {
+ return new RegisterSpec(reg, type, local);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RegisterSpec)) {
+ return false;
+ }
+
+ RegisterSpec spec = (RegisterSpec) other;
+ return spec.equals(reg, type, local);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return hashCodeOf(reg, type, local);
+ }
+ }
+}