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.rop.code; 18 19import com.android.dx.rop.type.StdTypeList; 20import com.android.dx.rop.type.Type; 21import com.android.dx.rop.type.TypeList; 22import com.android.dx.util.Hex; 23 24/** 25 * Class that describes all the immutable parts of register-based operations. 26 */ 27public 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 */ 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 */ 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 */ 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 */ 340 public TypeList getExceptions() { 341 return exceptions; 342 } 343 344 /** 345 * Gets the branchingness of this instance. 346 * 347 * @return the branchingness 348 */ 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 */ 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 */ 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 */ 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 */ 404 public final boolean canThrow() { 405 return (exceptions.size() != 0); 406 } 407} 408