1 21 package proguard.optimize.peephole; 22 23 import proguard.classfile.util.*; 24 import proguard.classfile.attribute.visitor.*; 25 import proguard.classfile.attribute.*; 26 import proguard.classfile.instruction.visitor.InstructionVisitor; 27 import proguard.classfile.instruction.*; 28 import proguard.classfile.editor.*; 29 import proguard.classfile.*; 30 import proguard.classfile.visitor.*; 31 import proguard.classfile.constant.visitor.ConstantVisitor; 32 import proguard.classfile.constant.*; 33 import proguard.optimize.info.*; 34 35 import java.util.Stack ; 36 37 43 public class MethodInliner 44 extends SimplifiedVisitor 45 implements AttributeVisitor, 46 InstructionVisitor, 47 ExceptionInfoVisitor, 48 ConstantVisitor, 49 MemberVisitor 50 { 51 private static final int MAXIMUM_INLINING_CODE_LENGTH = 8; 52 private static final int MAXIMUM_CODE_EXPANSION = 2; 53 private static final int MAXIMUM_EXTRA_CODE_LENGTH = 128; 54 55 private static final boolean DEBUG = false; 57 60 61 62 private boolean allowAccessModification; 63 private boolean inlineSingleInvocations; 64 private InstructionVisitor extraInlinedInvocationVisitor; 65 66 private CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(); 67 private AccessMethodMarker accessMethodMarker = new AccessMethodMarker(); 68 private CatchExceptionMarker catchExceptionMarker = new CatchExceptionMarker(); 69 private ConstantAdder constantAdder = new ConstantAdder(); 70 private StackSizeComputer stackSizeComputer = new StackSizeComputer(); 71 72 private Clazz targetClass; 73 private Method targetMethod; 74 private boolean inlining; 75 private Stack inliningMethods = new Stack (); 76 private boolean emptyInvokingStack; 77 private int uninitializedObjectCount; 78 private int variableOffset; 79 private boolean inlined; 80 private boolean inlinedAny; 81 82 83 89 public MethodInliner(boolean allowAccessModification, 90 boolean inlineSingleInvocations) 91 { 92 this(allowAccessModification, inlineSingleInvocations, null); 93 } 94 95 96 107 public MethodInliner(boolean allowAccessModification, 108 boolean inlineSingleInvocations, 109 InstructionVisitor extraInlinedInvocationVisitor) 110 { 111 this.allowAccessModification = allowAccessModification; 112 this.inlineSingleInvocations = inlineSingleInvocations; 113 this.extraInlinedInvocationVisitor = extraInlinedInvocationVisitor; 114 } 115 116 117 119 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 120 121 122 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 123 { 124 if (!inlining) 125 { 126 133 targetClass = clazz; 134 targetMethod = method; 135 inliningMethods.clear(); 136 uninitializedObjectCount = method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? 1 : 0; 137 inlinedAny = false; 138 codeAttributeComposer.reset(); 139 constantAdder.setTargetClass((ProgramClass)clazz); 140 stackSizeComputer.visitCodeAttribute(clazz, method, codeAttribute); 141 142 copyCode(clazz, method, codeAttribute); 144 145 targetClass = null; 146 targetMethod = null; 147 constantAdder.setTargetClass(null); 148 149 if (inlinedAny) 151 { 152 codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute); 153 154 codeAttribute.instructionsAccept(clazz, method, accessMethodMarker); 156 157 catchExceptionMarker.visitCodeAttribute(clazz, method, codeAttribute); 159 } 160 } 161 162 else if (inlineSingleInvocations ? 165 MethodInvocationMarker.getInvocationCount(method) == 1 : 166 codeAttribute.u4codeLength <= MAXIMUM_INLINING_CODE_LENGTH) 167 { 168 if (DEBUG) 169 { 170 System.out.println("MethodInliner: inlining ["+ 171 clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] in ["+ 172 targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]"); 173 } 174 175 storeParameters(clazz, method); 177 178 copyCode(clazz, method, codeAttribute); 180 181 inlined = true; 182 inlinedAny = true; 183 } 184 } 185 186 187 191 private void storeParameters(Clazz clazz, Method method) 192 { 193 194 String descriptor = method.getDescriptor(clazz); 195 196 boolean isStatic = 197 (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0; 198 199 int parameterCount = ClassUtil.internalMethodParameterCount(descriptor); 201 int parameterSize = ClassUtil.internalMethodParameterSize(descriptor); 202 int parameterOffset = isStatic ? 0 : 1; 203 204 String [] parameterTypes = new String [parameterSize]; 206 207 InternalTypeEnumeration internalTypeEnumeration = 208 new InternalTypeEnumeration(descriptor); 209 210 for (int parameterIndex = 0; parameterIndex < parameterSize; parameterIndex++) 211 { 212 String parameterType = internalTypeEnumeration.nextType(); 213 parameterTypes[parameterIndex] = parameterType; 214 if (ClassUtil.internalTypeSize(parameterType) == 2) 215 { 216 parameterIndex++; 217 } 218 } 219 220 codeAttributeComposer.beginCodeFragment((parameterOffset + parameterCount) * 4); 221 222 for (int parameterIndex = parameterSize-1; parameterIndex >= 0; parameterIndex--) 225 { 226 String parameterType = parameterTypes[parameterIndex]; 227 if (parameterType != null) 228 { 229 byte opcode; 230 switch (parameterType.charAt(0)) 231 { 232 case ClassConstants.INTERNAL_TYPE_BOOLEAN: 233 case ClassConstants.INTERNAL_TYPE_BYTE: 234 case ClassConstants.INTERNAL_TYPE_CHAR: 235 case ClassConstants.INTERNAL_TYPE_SHORT: 236 case ClassConstants.INTERNAL_TYPE_INT: 237 opcode = InstructionConstants.OP_ISTORE; 238 break; 239 240 case ClassConstants.INTERNAL_TYPE_LONG: 241 opcode = InstructionConstants.OP_LSTORE; 242 break; 243 244 case ClassConstants.INTERNAL_TYPE_FLOAT: 245 opcode = InstructionConstants.OP_FSTORE; 246 break; 247 248 case ClassConstants.INTERNAL_TYPE_DOUBLE: 249 opcode = InstructionConstants.OP_DSTORE; 250 break; 251 252 default: 253 opcode = InstructionConstants.OP_ASTORE; 254 break; 255 } 256 257 codeAttributeComposer.appendInstruction(parameterSize-parameterIndex-1, 258 new VariableInstruction(opcode, variableOffset + parameterOffset + parameterIndex).shrink()); 259 } 260 } 261 262 if (!isStatic) 264 { 265 codeAttributeComposer.appendInstruction(parameterSize, 266 new VariableInstruction(InstructionConstants.OP_ASTORE, variableOffset).shrink()); 267 } 268 269 codeAttributeComposer.endCodeFragment(); 270 } 271 272 273 276 private void copyCode(Clazz clazz, Method method, CodeAttribute codeAttribute) 277 { 278 codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength * MAXIMUM_CODE_EXPANSION + 281 MAXIMUM_EXTRA_CODE_LENGTH); 282 283 codeAttribute.instructionsAccept(clazz, method, this); 285 286 codeAttribute.exceptionsAccept(clazz, method, this); 288 289 codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); 291 292 codeAttributeComposer.endCodeFragment(); 293 } 294 295 296 298 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) 299 { 300 codeAttributeComposer.appendInstruction(offset, instruction.shrink()); 301 } 302 303 304 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 305 { 306 if (inlining) 308 { 309 switch (simpleInstruction.opcode) 311 { 312 case InstructionConstants.OP_IRETURN: 313 case InstructionConstants.OP_LRETURN: 314 case InstructionConstants.OP_FRETURN: 315 case InstructionConstants.OP_DRETURN: 316 case InstructionConstants.OP_ARETURN: 317 case InstructionConstants.OP_RETURN: 318 if (offset < codeAttribute.u4codeLength-1) 320 { 321 Instruction branchInstruction = 323 new BranchInstruction(InstructionConstants.OP_GOTO_W, 324 codeAttribute.u4codeLength - offset); 325 326 codeAttributeComposer.appendInstruction(offset, 327 branchInstruction.shrink()); 328 } 329 else 330 { 331 codeAttributeComposer.appendLabel(offset); 334 } 335 336 return; 337 } 338 } 339 340 codeAttributeComposer.appendInstruction(offset, simpleInstruction.shrink()); 341 } 342 343 344 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 345 { 346 if (inlining) 348 { 349 variableInstruction.variableIndex += variableOffset; 351 } 352 353 codeAttributeComposer.appendInstruction(offset, variableInstruction.shrink()); 354 } 355 356 357 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 358 { 359 switch (constantInstruction.opcode) 361 { 362 case InstructionConstants.OP_NEW: 363 uninitializedObjectCount++; 364 break; 365 366 case InstructionConstants.OP_INVOKEVIRTUAL: 367 case InstructionConstants.OP_INVOKESPECIAL: 368 case InstructionConstants.OP_INVOKESTATIC: 369 case InstructionConstants.OP_INVOKEINTERFACE: 370 inlined = false; 372 373 codeAttributeComposer.appendLabel(offset); 375 376 emptyInvokingStack = 377 !inlining && 378 stackSizeComputer.isReachable(offset) && 379 stackSizeComputer.getStackSize(offset) == 0; 380 381 variableOffset += codeAttribute.u2maxLocals; 382 383 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 384 385 variableOffset -= codeAttribute.u2maxLocals; 386 387 if (inlined) 389 { 390 if (extraInlinedInvocationVisitor != null) 391 { 392 extraInlinedInvocationVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction); 393 } 394 395 return; 397 } 398 399 break; 400 } 401 402 if (inlining) 404 { 405 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantAdder); 408 409 constantInstruction.constantIndex = constantAdder.getConstantIndex(); 411 } 412 413 codeAttributeComposer.appendInstruction(offset, constantInstruction.shrink()); 414 } 415 416 417 419 public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) 420 { 421 int catchType = exceptionInfo.u2catchType; 422 423 if (inlining && catchType != 0) 424 { 425 clazz.constantPoolEntryAccept(catchType, constantAdder); 428 429 catchType = constantAdder.getConstantIndex(); 431 } 432 433 codeAttributeComposer.appendException(new ExceptionInfo(exceptionInfo.u2startPC, 434 exceptionInfo.u2endPC, 435 exceptionInfo.u2handlerPC, 436 catchType)); 437 } 438 439 440 442 public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) {} 443 444 445 public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) 446 { 447 methodrefConstant.referencedMemberAccept(this); 448 } 449 450 451 453 public void visitAnyMember(Clazz Clazz, Member member) {} 454 455 456 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 457 { 458 int accessFlags = programMethod.getAccessFlags(); 459 460 if ( (accessFlags & (ClassConstants.INTERNAL_ACC_PRIVATE | 462 ClassConstants.INTERNAL_ACC_STATIC | 463 ClassConstants.INTERNAL_ACC_FINAL)) != 0 && 464 465 (accessFlags & (ClassConstants.INTERNAL_ACC_SYNCHRONIZED | 467 ClassConstants.INTERNAL_ACC_NATIVE | 468 ClassConstants.INTERNAL_ACC_INTERFACE | 469 ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0 && 470 471 !programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) && 477 478 (!programMethod.equals(targetMethod) || 480 !programClass.equals(targetClass)) && 481 482 !inliningMethods.contains(programMethod) && 484 485 (!SuperInvocationMarker.invokesSuperMethods(programMethod) || 488 programClass.equals(targetClass)) && 489 490 (!BackwardBranchMarker.branchesBackward(programMethod) || 493 uninitializedObjectCount == 0) && 494 495 (allowAccessModification || 497 ((!AccessMethodMarker.accessesPrivateCode(programMethod) || 498 programClass.equals(targetClass)) && 499 500 (!AccessMethodMarker.accessesPackageCode(programMethod) || 501 ClassUtil.internalPackageName(programClass.getName()).equals( 502 ClassUtil.internalPackageName(targetClass.getName()))))) && 503 504 (!AccessMethodMarker.accessesProtectedCode(programMethod) || 508 programClass.equals(targetClass)) && 509 510 (!CatchExceptionMarker.catchesExceptions(programMethod) || 513 emptyInvokingStack) && 514 515 (programClass.equals(targetClass) || 518 programClass.findMethod(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, 519 ClassConstants.INTERNAL_METHOD_TYPE_CLINIT) == null)) 520 { 521 542 boolean oldInlining = inlining; 543 inlining = true; 544 inliningMethods.push(programMethod); 545 546 programMethod.attributesAccept(programClass, this); 548 549 inlining = oldInlining; 550 inliningMethods.pop(); 551 } 552 else if (programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) 553 { 554 uninitializedObjectCount--; 555 } 556 } 557 } 558 | Popular Tags |