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.rop.code; 18 19 import com.android.dx.rop.type.StdTypeList; 20 import com.android.dx.rop.type.Type; 21 import com.android.dx.rop.type.TypeList; 22 import com.android.dx.util.Hex; 23 24 /** 25 * Class that describes all the immutable parts of register-based operations. 26 */ 27 public final class Rop { 28 /** minimum {@code BRANCH_*} value */ 29 public static final int BRANCH_MIN = 1; 30 31 /** indicates a non-branching op */ 32 public static final int BRANCH_NONE = 1; 33 34 /** indicates a function/method return */ 35 public static final int BRANCH_RETURN = 2; 36 37 /** indicates an unconditional goto */ 38 public static final int BRANCH_GOTO = 3; 39 40 /** indicates a two-way branch */ 41 public static final int BRANCH_IF = 4; 42 43 /** indicates a switch-style branch */ 44 public static final int BRANCH_SWITCH = 5; 45 46 /** indicates a throw-style branch (both always-throws and may-throw) */ 47 public static final int BRANCH_THROW = 6; 48 49 /** maximum {@code BRANCH_*} value */ 50 public static final int BRANCH_MAX = 6; 51 52 /** the opcode; one of the constants in {@link RegOps} */ 53 private final int opcode; 54 55 /** 56 * {@code non-null;} result type of this operation; {@link Type#VOID} for 57 * no-result operations 58 */ 59 private final Type result; 60 61 /** {@code non-null;} types of all the sources of this operation */ 62 private final TypeList sources; 63 64 /** {@code non-null;} list of possible types thrown by this operation */ 65 private final TypeList exceptions; 66 67 /** 68 * the branchingness of this op; one of the {@code BRANCH_*} 69 * constants in this class 70 */ 71 private final int branchingness; 72 73 /** whether this is a function/method call op or similar */ 74 private final boolean isCallLike; 75 76 /** {@code null-ok;} nickname, if specified (used for debugging) */ 77 private final String nickname; 78 79 /** 80 * Constructs an instance. This method is private. Use one of the 81 * public constructors. 82 * 83 * @param opcode the opcode; one of the constants in {@link RegOps} 84 * @param result {@code non-null;} result type of this operation; {@link 85 * Type#VOID} for no-result operations 86 * @param sources {@code non-null;} types of all the sources of this operation 87 * @param exceptions {@code non-null;} list of possible types thrown by this 88 * operation 89 * @param branchingness the branchingness of this op; one of the 90 * {@code BRANCH_*} constants 91 * @param isCallLike whether the op is a function/method call or similar 92 * @param nickname {@code null-ok;} optional nickname (used for debugging) 93 */ Rop(int opcode, Type result, TypeList sources, TypeList exceptions, int branchingness, boolean isCallLike, String nickname)94 public Rop(int opcode, Type result, TypeList sources, 95 TypeList exceptions, int branchingness, boolean isCallLike, 96 String nickname) { 97 if (result == null) { 98 throw new NullPointerException("result == null"); 99 } 100 101 if (sources == null) { 102 throw new NullPointerException("sources == null"); 103 } 104 105 if (exceptions == null) { 106 throw new NullPointerException("exceptions == null"); 107 } 108 109 if ((branchingness < BRANCH_MIN) || (branchingness > BRANCH_MAX)) { 110 throw new IllegalArgumentException("bogus branchingness"); 111 } 112 113 if ((exceptions.size() != 0) && (branchingness != BRANCH_THROW)) { 114 throw new IllegalArgumentException("exceptions / branchingness " + 115 "mismatch"); 116 } 117 118 this.opcode = opcode; 119 this.result = result; 120 this.sources = sources; 121 this.exceptions = exceptions; 122 this.branchingness = branchingness; 123 this.isCallLike = isCallLike; 124 this.nickname = nickname; 125 } 126 127 /** 128 * Constructs an instance. The constructed instance is never a 129 * call-like op (see {@link #isCallLike}). 130 * 131 * @param opcode the opcode; one of the constants in {@link RegOps} 132 * @param result {@code non-null;} result type of this operation; {@link 133 * Type#VOID} for no-result operations 134 * @param sources {@code non-null;} types of all the sources of this operation 135 * @param exceptions {@code non-null;} list of possible types thrown by this 136 * operation 137 * @param branchingness the branchingness of this op; one of the 138 * {@code BRANCH_*} constants 139 * @param nickname {@code null-ok;} optional nickname (used for debugging) 140 */ Rop(int opcode, Type result, TypeList sources, TypeList exceptions, int branchingness, String nickname)141 public Rop(int opcode, Type result, TypeList sources, 142 TypeList exceptions, int branchingness, String nickname) { 143 this(opcode, result, sources, exceptions, branchingness, false, 144 nickname); 145 } 146 147 /** 148 * Constructs a no-exception instance. The constructed instance is never a 149 * call-like op (see {@link #isCallLike}). 150 * 151 * @param opcode the opcode; one of the constants in {@link RegOps} 152 * @param result {@code non-null;} result type of this operation; {@link 153 * Type#VOID} for no-result operations 154 * @param sources {@code non-null;} types of all the sources of this operation 155 * @param branchingness the branchingness of this op; one of the 156 * {@code BRANCH_*} constants 157 * @param nickname {@code null-ok;} optional nickname (used for debugging) 158 */ Rop(int opcode, Type result, TypeList sources, int branchingness, String nickname)159 public Rop(int opcode, Type result, TypeList sources, int branchingness, 160 String nickname) { 161 this(opcode, result, sources, StdTypeList.EMPTY, branchingness, false, 162 nickname); 163 } 164 165 /** 166 * Constructs a non-branching no-exception instance. The 167 * {@code branchingness} is always {@code BRANCH_NONE}, 168 * and it is never a call-like op (see {@link #isCallLike}). 169 * 170 * @param opcode the opcode; one of the constants in {@link RegOps} 171 * @param result {@code non-null;} result type of this operation; {@link 172 * Type#VOID} for no-result operations 173 * @param sources {@code non-null;} types of all the sources of this operation 174 * @param nickname {@code null-ok;} optional nickname (used for debugging) 175 */ Rop(int opcode, Type result, TypeList sources, String nickname)176 public Rop(int opcode, Type result, TypeList sources, String nickname) { 177 this(opcode, result, sources, StdTypeList.EMPTY, Rop.BRANCH_NONE, 178 false, nickname); 179 } 180 181 /** 182 * Constructs a non-empty exceptions instance. Its 183 * {@code branchingness} is always {@code BRANCH_THROW}, 184 * but it is never a call-like op (see {@link #isCallLike}). 185 * 186 * @param opcode the opcode; one of the constants in {@link RegOps} 187 * @param result {@code non-null;} result type of this operation; {@link 188 * Type#VOID} for no-result operations 189 * @param sources {@code non-null;} types of all the sources of this operation 190 * @param exceptions {@code non-null;} list of possible types thrown by this 191 * operation 192 * @param nickname {@code null-ok;} optional nickname (used for debugging) 193 */ Rop(int opcode, Type result, TypeList sources, TypeList exceptions, String nickname)194 public Rop(int opcode, Type result, TypeList sources, TypeList exceptions, 195 String nickname) { 196 this(opcode, result, sources, exceptions, Rop.BRANCH_THROW, false, 197 nickname); 198 } 199 200 /** 201 * Constructs a non-nicknamed instance with non-empty exceptions, which 202 * is always a call-like op (see {@link #isCallLike}). Its 203 * {@code branchingness} is always {@code BRANCH_THROW}. 204 * 205 * @param opcode the opcode; one of the constants in {@link RegOps} 206 * @param sources {@code non-null;} types of all the sources of this operation 207 * @param exceptions {@code non-null;} list of possible types thrown by this 208 * operation 209 */ Rop(int opcode, TypeList sources, TypeList exceptions)210 public Rop(int opcode, TypeList sources, TypeList exceptions) { 211 this(opcode, Type.VOID, sources, exceptions, Rop.BRANCH_THROW, true, 212 null); 213 } 214 215 /** {@inheritDoc} */ 216 @Override equals(Object other)217 public boolean equals(Object other) { 218 if (this == other) { 219 // Easy out. 220 return true; 221 } 222 223 if (!(other instanceof Rop)) { 224 return false; 225 } 226 227 Rop rop = (Rop) other; 228 229 return (opcode == rop.opcode) && 230 (branchingness == rop.branchingness) && 231 (result == rop.result) && 232 sources.equals(rop.sources) && 233 exceptions.equals(rop.exceptions); 234 } 235 236 /** {@inheritDoc} */ 237 @Override hashCode()238 public int hashCode() { 239 int h = (opcode * 31) + branchingness; 240 h = (h * 31) + result.hashCode(); 241 h = (h * 31) + sources.hashCode(); 242 h = (h * 31) + exceptions.hashCode(); 243 244 return h; 245 } 246 247 /** {@inheritDoc} */ 248 @Override toString()249 public String toString() { 250 StringBuffer sb = new StringBuffer(40); 251 252 sb.append("Rop{"); 253 254 sb.append(RegOps.opName(opcode)); 255 256 if (result != Type.VOID) { 257 sb.append(" "); 258 sb.append(result); 259 } else { 260 sb.append(" ."); 261 } 262 263 sb.append(" <-"); 264 265 int sz = sources.size(); 266 if (sz == 0) { 267 sb.append(" ."); 268 } else { 269 for (int i = 0; i < sz; i++) { 270 sb.append(' '); 271 sb.append(sources.getType(i)); 272 } 273 } 274 275 if (isCallLike) { 276 sb.append(" call"); 277 } 278 279 sz = exceptions.size(); 280 if (sz != 0) { 281 sb.append(" throws"); 282 for (int i = 0; i < sz; i++) { 283 sb.append(' '); 284 Type one = exceptions.getType(i); 285 if (one == Type.THROWABLE) { 286 sb.append("<any>"); 287 } else { 288 sb.append(exceptions.getType(i)); 289 } 290 } 291 } else { 292 switch (branchingness) { 293 case BRANCH_NONE: sb.append(" flows"); break; 294 case BRANCH_RETURN: sb.append(" returns"); break; 295 case BRANCH_GOTO: sb.append(" gotos"); break; 296 case BRANCH_IF: sb.append(" ifs"); break; 297 case BRANCH_SWITCH: sb.append(" switches"); break; 298 default: sb.append(" " + Hex.u1(branchingness)); break; 299 } 300 } 301 302 sb.append('}'); 303 304 return sb.toString(); 305 } 306 307 /** 308 * Gets the opcode. 309 * 310 * @return the opcode 311 */ getOpcode()312 public int getOpcode() { 313 return opcode; 314 } 315 316 /** 317 * Gets the result type. A return value of {@link Type#VOID} 318 * means this operation returns nothing. 319 * 320 * @return {@code null-ok;} the result spec 321 */ getResult()322 public Type getResult() { 323 return result; 324 } 325 326 /** 327 * Gets the source types. 328 * 329 * @return {@code non-null;} the source types 330 */ getSources()331 public TypeList getSources() { 332 return sources; 333 } 334 335 /** 336 * Gets the list of exception types that might be thrown. 337 * 338 * @return {@code non-null;} the list of exception types 339 */ getExceptions()340 public TypeList getExceptions() { 341 return exceptions; 342 } 343 344 /** 345 * Gets the branchingness of this instance. 346 * 347 * @return the branchingness 348 */ getBranchingness()349 public int getBranchingness() { 350 return branchingness; 351 } 352 353 /** 354 * Gets whether this opcode is a function/method call or similar. 355 * 356 * @return {@code true} iff this opcode is call-like 357 */ isCallLike()358 public boolean isCallLike() { 359 return isCallLike; 360 } 361 362 363 /** 364 * Gets whether this opcode is commutative (the order of its sources are 365 * unimportant) or not. All commutative Rops have exactly two sources and 366 * have no branchiness. 367 * 368 * @return true if rop is commutative 369 */ isCommutative()370 public boolean isCommutative() { 371 switch (opcode) { 372 case RegOps.AND: 373 case RegOps.OR: 374 case RegOps.XOR: 375 case RegOps.ADD: 376 case RegOps.MUL: 377 return true; 378 default: 379 return false; 380 } 381 } 382 383 /** 384 * Gets the nickname. If this instance has no nickname, this returns 385 * the result of calling {@link #toString}. 386 * 387 * @return {@code non-null;} the nickname 388 */ getNickname()389 public String getNickname() { 390 if (nickname != null) { 391 return nickname; 392 } 393 394 return toString(); 395 } 396 397 /** 398 * Gets whether this operation can possibly throw an exception. This 399 * is just a convenient wrapper for 400 * {@code getExceptions().size() != 0}. 401 * 402 * @return {@code true} iff this operation can possibly throw 403 */ canThrow()404 public final boolean canThrow() { 405 return (exceptions.size() != 0); 406 } 407 } 408