001 /* 002 * Copyright 2010-2014 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.jet.codegen.inline; 018 019 import com.google.common.collect.Lists; 020 import com.intellij.openapi.vfs.VirtualFile; 021 import com.intellij.util.ArrayUtil; 022 import org.jetbrains.annotations.NotNull; 023 import org.jetbrains.jet.codegen.ClosureCodegen; 024 import org.jetbrains.jet.codegen.StackValue; 025 import org.jetbrains.jet.codegen.state.JetTypeMapper; 026 import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass; 027 import org.jetbrains.jet.lang.resolve.java.JvmClassName; 028 import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; 029 import org.jetbrains.jet.lang.resolve.kotlin.KotlinBinaryClassCache; 030 import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass; 031 import org.jetbrains.org.objectweb.asm.Label; 032 import org.jetbrains.org.objectweb.asm.MethodVisitor; 033 import org.jetbrains.org.objectweb.asm.Opcodes; 034 import org.jetbrains.org.objectweb.asm.Type; 035 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; 036 import org.jetbrains.org.objectweb.asm.commons.Method; 037 import org.jetbrains.org.objectweb.asm.commons.RemappingMethodAdapter; 038 import org.jetbrains.org.objectweb.asm.tree.*; 039 import org.jetbrains.org.objectweb.asm.tree.analysis.*; 040 041 import java.util.*; 042 043 import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.*; 044 045 public class MethodInliner { 046 047 private final MethodNode node; 048 049 private final Parameters parameters; 050 051 private final InliningContext inliningContext; 052 053 private final FieldRemapper nodeRemapper; 054 055 private final boolean isSameModule; 056 057 private final String errorPrefix; 058 059 private final JetTypeMapper typeMapper; 060 061 private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>(); 062 063 //keeps order 064 private final List<ConstructorInvocation> constructorInvocations = new ArrayList<ConstructorInvocation>(); 065 //current state 066 private final Map<String, String> currentTypeMapping = new HashMap<String, String>(); 067 068 private final InlineResult result; 069 070 private int lambdasFinallyBlocks; 071 072 /* 073 * 074 * @param node 075 * @param parameters 076 * @param inliningContext 077 * @param lambdaType - in case on lambda 'invoke' inlining 078 */ 079 public MethodInliner( 080 @NotNull MethodNode node, 081 @NotNull Parameters parameters, 082 @NotNull InliningContext parent, 083 @NotNull FieldRemapper nodeRemapper, 084 boolean isSameModule, 085 @NotNull String errorPrefix 086 ) { 087 this.node = node; 088 this.parameters = parameters; 089 this.inliningContext = parent; 090 this.nodeRemapper = nodeRemapper; 091 this.isSameModule = isSameModule; 092 this.errorPrefix = errorPrefix; 093 this.typeMapper = parent.state.getTypeMapper(); 094 this.result = InlineResult.create(); 095 } 096 097 public InlineResult doInline( 098 @NotNull MethodVisitor adapter, 099 @NotNull LocalVarRemapper remapper, 100 boolean remapReturn, 101 @NotNull LabelOwner labelOwner 102 ) { 103 //analyze body 104 MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node); 105 106 //substitute returns with "goto end" instruction to keep non local returns in lambdas 107 Label end = new Label(); 108 transformedNode = doInline(transformedNode); 109 removeClosureAssertions(transformedNode); 110 InsnList instructions = transformedNode.instructions; 111 instructions.resetLabels(); 112 113 MethodNode resultNode = new MethodNode(InlineCodegenUtil.API, transformedNode.access, transformedNode.name, transformedNode.desc, 114 transformedNode.signature, ArrayUtil.toStringArray(transformedNode.exceptions)); 115 RemapVisitor visitor = new RemapVisitor(resultNode, remapper, nodeRemapper); 116 try { 117 transformedNode.accept(visitor); 118 } 119 catch (Exception e) { 120 throw wrapException(e, transformedNode, "couldn't inline method call"); 121 } 122 123 resultNode.visitLabel(end); 124 125 if (inliningContext.isRoot()) { 126 InternalFinallyBlockInliner.processInlineFunFinallyBlocks(resultNode, lambdasFinallyBlocks); 127 } 128 129 processReturns(resultNode, labelOwner, remapReturn, end); 130 //flush transformed node to output 131 resultNode.accept(new InliningInstructionAdapter(adapter)); 132 133 return result; 134 } 135 136 private MethodNode doInline(MethodNode node) { 137 138 final Deque<InvokeCall> currentInvokes = new LinkedList<InvokeCall>(invokeCalls); 139 140 final MethodNode resultNode = new MethodNode(node.access, node.name, node.desc, node.signature, null); 141 142 final Iterator<ConstructorInvocation> iterator = constructorInvocations.iterator(); 143 144 RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter(resultNode.access, resultNode.desc, resultNode, 145 new TypeRemapper(currentTypeMapping)); 146 147 InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.totalSize()) { 148 149 private ConstructorInvocation invocation; 150 @Override 151 public void anew(@NotNull Type type) { 152 if (isAnonymousConstructorCall(type.getInternalName(), "<init>")) { 153 invocation = iterator.next(); 154 155 if (invocation.shouldRegenerate()) { 156 //TODO: need poping of type but what to do with local funs??? 157 Type newLambdaType = Type.getObjectType(inliningContext.nameGenerator.genLambdaClassName()); 158 currentTypeMapping.put(invocation.getOwnerInternalName(), newLambdaType.getInternalName()); 159 AnonymousObjectTransformer transformer = 160 new AnonymousObjectTransformer(invocation.getOwnerInternalName(), 161 inliningContext 162 .subInlineWithClassRegeneration( 163 inliningContext.nameGenerator, 164 currentTypeMapping, 165 invocation), 166 isSameModule, newLambdaType 167 ); 168 169 InlineResult transformResult = transformer.doTransform(invocation, nodeRemapper); 170 result.addAllClassesToRemove(transformResult); 171 172 if (inliningContext.isInliningLambda) { 173 //this class is transformed and original not used so we should remove original one after inlining 174 result.addClassToRemove(invocation.getOwnerInternalName()); 175 } 176 } 177 } 178 179 //in case of regenerated invocation type would be remapped to new one via remappingMethodAdapter 180 super.anew(type); 181 } 182 183 @Override 184 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 185 if (/*INLINE_RUNTIME.equals(owner) &&*/ isInvokeOnLambda(owner, name)) { //TODO add method 186 assert !currentInvokes.isEmpty(); 187 InvokeCall invokeCall = currentInvokes.remove(); 188 LambdaInfo info = invokeCall.lambdaInfo; 189 190 if (info == null) { 191 //noninlinable lambda 192 super.visitMethodInsn(opcode, owner, name, desc, itf); 193 return; 194 } 195 196 int valueParamShift = getNextLocalIndex();//NB: don't inline cause it changes 197 putStackValuesIntoLocals(info.getInvokeParamsWithoutCaptured(), valueParamShift, this, desc); 198 199 addInlineMarker(this, true); 200 Parameters lambdaParameters = info.addAllParameters(nodeRemapper); 201 202 InlinedLambdaRemapper newCapturedRemapper = 203 new InlinedLambdaRemapper(info.getLambdaClassType().getInternalName(), nodeRemapper, lambdaParameters); 204 205 setLambdaInlining(true); 206 MethodInliner inliner = new MethodInliner(info.getNode(), lambdaParameters, 207 inliningContext.subInlineLambda(info), 208 newCapturedRemapper, true /*cause all calls in same module as lambda*/, 209 "Lambda inlining " + info.getLambdaClassType().getInternalName()); 210 211 LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift); 212 InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info);//TODO add skipped this and receiver 213 result.addAllClassesToRemove(lambdaResult); 214 215 //return value boxing/unboxing 216 Method bridge = 217 typeMapper.mapSignature(ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor())).getAsmMethod(); 218 Method delegate = typeMapper.mapSignature(info.getFunctionDescriptor()).getAsmMethod(); 219 StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this); 220 setLambdaInlining(false); 221 addInlineMarker(this, false); 222 } 223 else if (isAnonymousConstructorCall(owner, name)) { //TODO add method 224 assert invocation != null : "<init> call not corresponds to new call" + owner + " " + name; 225 if (invocation.shouldRegenerate()) { 226 //put additional captured parameters on stack 227 for (CapturedParamDesc capturedParamDesc : invocation.getAllRecapturedParameters()) { 228 visitFieldInsn(Opcodes.GETSTATIC, capturedParamDesc.getContainingLambdaName(), 229 "$$$" + capturedParamDesc.getFieldName(), capturedParamDesc.getType().getDescriptor()); 230 } 231 super.visitMethodInsn(opcode, invocation.getNewLambdaType().getInternalName(), name, invocation.getNewConstructorDescriptor(), itf); 232 invocation = null; 233 } else { 234 super.visitMethodInsn(opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf); 235 } 236 } 237 else { 238 super.visitMethodInsn(opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf); 239 } 240 } 241 242 @Override 243 public void visitMaxs(int stack, int locals) { 244 lambdasFinallyBlocks = resultNode.tryCatchBlocks.size(); 245 super.visitMaxs(stack, locals); 246 } 247 248 }; 249 250 node.accept(lambdaInliner); 251 252 return resultNode; 253 } 254 255 @NotNull 256 public static CapturedParamInfo findCapturedField(FieldInsnNode node, FieldRemapper fieldRemapper) { 257 assert node.name.startsWith("$$$") : "Captured field template should start with $$$ prefix"; 258 FieldInsnNode fin = new FieldInsnNode(node.getOpcode(), node.owner, node.name.substring(3), node.desc); 259 CapturedParamInfo field = fieldRemapper.findField(fin); 260 if (field == null) { 261 throw new IllegalStateException("Couldn't find captured field " + node.owner + "." + node.name + " in " + fieldRemapper.getLambdaInternalName()); 262 } 263 return field; 264 } 265 266 @NotNull 267 public MethodNode prepareNode(@NotNull MethodNode node) { 268 final int capturedParamsSize = parameters.getCaptured().size(); 269 final int realParametersSize = parameters.getReal().size(); 270 Type[] types = Type.getArgumentTypes(node.desc); 271 Type returnType = Type.getReturnType(node.desc); 272 273 ArrayList<Type> capturedTypes = parameters.getCapturedTypes(); 274 Type[] allTypes = ArrayUtil.mergeArrays(types, capturedTypes.toArray(new Type[capturedTypes.size()])); 275 276 node.instructions.resetLabels(); 277 MethodNode transformedNode = new MethodNode(InlineCodegenUtil.API, node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null) { 278 279 private final boolean isInliningLambda = nodeRemapper.isInsideInliningLambda(); 280 281 private int getNewIndex(int var) { 282 return var + (var < realParametersSize ? 0 : capturedParamsSize); 283 } 284 285 @Override 286 public void visitVarInsn(int opcode, int var) { 287 super.visitVarInsn(opcode, getNewIndex(var)); 288 } 289 290 @Override 291 public void visitIincInsn(int var, int increment) { 292 super.visitIincInsn(getNewIndex(var), increment); 293 } 294 295 @Override 296 public void visitMaxs(int maxStack, int maxLocals) { 297 super.visitMaxs(maxStack, maxLocals + capturedParamsSize); 298 } 299 300 @Override 301 public void visitLineNumber(int line, @NotNull Label start) { 302 if(isInliningLambda) { 303 super.visitLineNumber(line, start); 304 } 305 } 306 307 @Override 308 public void visitLocalVariable( 309 @NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index 310 ) { 311 if (isInliningLambda) { 312 super.visitLocalVariable(name, desc, signature, start, end, getNewIndex(index)); 313 } 314 } 315 }; 316 317 node.accept(transformedNode); 318 319 transformCaptured(transformedNode); 320 321 return transformedNode; 322 } 323 324 @NotNull 325 protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) { 326 node = prepareNode(node); 327 328 Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()) { 329 @NotNull 330 @Override 331 protected Frame<SourceValue> newFrame( 332 int nLocals, int nStack 333 ) { 334 return new Frame<SourceValue>(nLocals, nStack) { 335 @Override 336 public void execute( 337 @NotNull AbstractInsnNode insn, Interpreter<SourceValue> interpreter 338 ) throws AnalyzerException { 339 if (insn.getOpcode() == Opcodes.RETURN) { 340 //there is exception on void non local return in frame 341 return; 342 } 343 super.execute(insn, interpreter); 344 } 345 }; 346 } 347 }; 348 349 Frame<SourceValue>[] sources; 350 try { 351 sources = analyzer.analyze("fake", node); 352 } 353 catch (AnalyzerException e) { 354 throw wrapException(e, node, "couldn't inline method call"); 355 } 356 357 AbstractInsnNode cur = node.instructions.getFirst(); 358 int index = 0; 359 360 while (cur != null) { 361 Frame<SourceValue> frame = sources[index]; 362 363 if (frame != null) { 364 if (cur.getType() == AbstractInsnNode.METHOD_INSN) { 365 MethodInsnNode methodInsnNode = (MethodInsnNode) cur; 366 String owner = methodInsnNode.owner; 367 String desc = methodInsnNode.desc; 368 String name = methodInsnNode.name; 369 //TODO check closure 370 int paramLength = Type.getArgumentTypes(desc).length + 1;//non static 371 if (isInvokeOnLambda(owner, name) /*&& methodInsnNode.owner.equals(INLINE_RUNTIME)*/) { 372 SourceValue sourceValue = frame.getStack(frame.getStackSize() - paramLength); 373 374 LambdaInfo lambdaInfo = null; 375 int varIndex = -1; 376 377 if (sourceValue.insns.size() == 1) { 378 AbstractInsnNode insnNode = sourceValue.insns.iterator().next(); 379 380 lambdaInfo = getLambdaIfExists(insnNode); 381 if (lambdaInfo != null) { 382 //remove inlinable access 383 node.instructions.remove(insnNode); 384 } 385 } 386 387 invokeCalls.add(new InvokeCall(varIndex, lambdaInfo)); 388 } 389 else if (isAnonymousConstructorCall(owner, name)) { 390 Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>(); 391 int paramStart = frame.getStackSize() - paramLength; 392 393 for (int i = 0; i < paramLength; i++) { 394 SourceValue sourceValue = frame.getStack(paramStart + i); 395 if (sourceValue.insns.size() == 1) { 396 AbstractInsnNode insnNode = sourceValue.insns.iterator().next(); 397 LambdaInfo lambdaInfo = getLambdaIfExists(insnNode); 398 if (lambdaInfo != null) { 399 lambdaMapping.put(i, lambdaInfo); 400 node.instructions.remove(insnNode); 401 } 402 } 403 } 404 405 constructorInvocations.add(new ConstructorInvocation(owner, desc, lambdaMapping, isSameModule, inliningContext.classRegeneration)); 406 } 407 } 408 } 409 410 AbstractInsnNode prevNode = cur; 411 cur = cur.getNext(); 412 index++; 413 414 //given frame is <tt>null</tt> if and only if the corresponding instruction cannot be reached (dead code). 415 if (frame == null) { 416 //clean dead code otherwise there is problems in unreachable finally block, don't touch label it cause try/catch/finally problems 417 if (prevNode.getType() == AbstractInsnNode.LABEL) { 418 //NB: Cause we generate exception table for default handler using gaps (see ExpressionCodegen.visitTryExpression) 419 //it may occurs that interval for default handler starts before catch start label, so this label seems as dead, 420 //but as result all this labels will be merged into one (see KT-5863) 421 } else { 422 node.instructions.remove(prevNode); 423 } 424 } 425 } 426 427 //clean dead try/catch blocks 428 List<TryCatchBlockNode> blocks = node.tryCatchBlocks; 429 for (Iterator<TryCatchBlockNode> iterator = blocks.iterator(); iterator.hasNext(); ) { 430 TryCatchBlockNode block = iterator.next(); 431 if (isEmptyTryInterval(block)) { 432 iterator.remove(); 433 } 434 } 435 436 return node; 437 } 438 439 private static boolean isEmptyTryInterval(@NotNull TryCatchBlockNode tryCatchBlockNode) { 440 LabelNode start = tryCatchBlockNode.start; 441 AbstractInsnNode end = tryCatchBlockNode.end; 442 while (end != start && end instanceof LabelNode) { 443 end = end.getPrevious(); 444 } 445 return start == end; 446 } 447 448 public LambdaInfo getLambdaIfExists(AbstractInsnNode insnNode) { 449 if (insnNode.getOpcode() == Opcodes.ALOAD) { 450 int varIndex = ((VarInsnNode) insnNode).var; 451 if (varIndex < parameters.totalSize()) { 452 return parameters.get(varIndex).getLambda(); 453 } 454 } 455 else if (insnNode instanceof FieldInsnNode) { 456 FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode; 457 if (fieldInsnNode.name.startsWith("$$$")) { 458 return findCapturedField(fieldInsnNode, nodeRemapper).getLambda(); 459 } 460 } 461 462 return null; 463 } 464 465 private static void removeClosureAssertions(MethodNode node) { 466 AbstractInsnNode cur = node.instructions.getFirst(); 467 while (cur != null && cur.getNext() != null) { 468 AbstractInsnNode next = cur.getNext(); 469 if (next.getType() == AbstractInsnNode.METHOD_INSN) { 470 MethodInsnNode methodInsnNode = (MethodInsnNode) next; 471 if (methodInsnNode.name.equals("checkParameterIsNotNull") && methodInsnNode.owner.equals("kotlin/jvm/internal/Intrinsics")) { 472 AbstractInsnNode prev = cur.getPrevious(); 473 474 assert cur.getOpcode() == Opcodes.LDC : "checkParameterIsNotNull should go after LDC but " + cur; 475 assert prev.getOpcode() == Opcodes.ALOAD : "checkParameterIsNotNull should be invoked on local var but " + prev; 476 477 node.instructions.remove(prev); 478 node.instructions.remove(cur); 479 cur = next.getNext(); 480 node.instructions.remove(next); 481 next = cur; 482 } 483 } 484 cur = next; 485 } 486 } 487 488 private void transformCaptured(@NotNull MethodNode node) { 489 if (nodeRemapper.isRoot()) { 490 return; 491 } 492 493 //Fold all captured variable chain - ALOAD 0 ALOAD this$0 GETFIELD $captured - to GETFIELD $$$$captured 494 //On future decoding this field could be inline or unfolded in another field access chain (it can differ in some missed this$0) 495 AbstractInsnNode cur = node.instructions.getFirst(); 496 while (cur != null) { 497 if (cur instanceof VarInsnNode && cur.getOpcode() == Opcodes.ALOAD) { 498 if (((VarInsnNode) cur).var == 0) { 499 List<AbstractInsnNode> accessChain = getCapturedFieldAccessChain((VarInsnNode) cur); 500 AbstractInsnNode insnNode = nodeRemapper.foldFieldAccessChainIfNeeded(accessChain, node); 501 if (insnNode != null) { 502 cur = insnNode; 503 } 504 } 505 } 506 cur = cur.getNext(); 507 } 508 } 509 510 @NotNull 511 public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) { 512 List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>(); 513 fieldAccessChain.add(aload0); 514 AbstractInsnNode next = aload0.getNext(); 515 while (next != null && next instanceof FieldInsnNode || next instanceof LabelNode) { 516 if (next instanceof LabelNode) { 517 next = next.getNext(); 518 continue; //it will be delete on transformation 519 } 520 fieldAccessChain.add(next); 521 if ("this$0".equals(((FieldInsnNode) next).name)) { 522 next = next.getNext(); 523 } 524 else { 525 break; 526 } 527 } 528 529 return fieldAccessChain; 530 } 531 532 public static void putStackValuesIntoLocals(List<Type> directOrder, int shift, InstructionAdapter iv, String descriptor) { 533 Type[] actualParams = Type.getArgumentTypes(descriptor); 534 assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!"; 535 536 int size = 0; 537 for (Type next : directOrder) { 538 size += next.getSize(); 539 } 540 541 shift += size; 542 int index = directOrder.size(); 543 544 for (Type next : Lists.reverse(directOrder)) { 545 shift -= next.getSize(); 546 Type typeOnStack = actualParams[--index]; 547 if (!typeOnStack.equals(next)) { 548 StackValue.onStack(typeOnStack).put(next, iv); 549 } 550 iv.store(shift, next); 551 } 552 } 553 554 //TODO: check it's external module 555 //TODO?: assert method exists in facade? 556 public String changeOwnerForExternalPackage(String type, int opcode) { 557 if (isSameModule || (opcode & Opcodes.INVOKESTATIC) == 0) { 558 return type; 559 } 560 561 JvmClassName name = JvmClassName.byInternalName(type); 562 String packageClassInternalName = PackageClassUtils.getPackageClassInternalName(name.getPackageFqName()); 563 if (type.startsWith(packageClassInternalName + '$')) { 564 VirtualFile virtualFile = InlineCodegenUtil.findVirtualFile(inliningContext.state.getProject(), type); 565 if (virtualFile != null) { 566 KotlinJvmBinaryClass klass = KotlinBinaryClassCache.getKotlinBinaryClass(virtualFile); 567 if (klass != null && klass.getClassHeader().getSyntheticClassKind() == KotlinSyntheticClass.Kind.PACKAGE_PART) { 568 return packageClassInternalName; 569 } 570 } 571 } 572 573 return type; 574 } 575 576 @NotNull 577 public RuntimeException wrapException(@NotNull Exception originalException, @NotNull MethodNode node, @NotNull String errorSuffix) { 578 if (originalException instanceof InlineException) { 579 return new InlineException(errorPrefix + ": " + errorSuffix, originalException); 580 } 581 else { 582 return new InlineException(errorPrefix + ": " + errorSuffix + "\ncause: " + 583 getNodeText(node), originalException); 584 } 585 } 586 587 @NotNull 588 //process local and global returns (local substituted with goto end-label global kept unchanged) 589 public static List<ExternalFinallyBlockInfo> processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) { 590 if (!remapReturn) { 591 return Collections.emptyList(); 592 } 593 List<ExternalFinallyBlockInfo> result = new ArrayList<ExternalFinallyBlockInfo>(); 594 InsnList instructions = node.instructions; 595 AbstractInsnNode insnNode = instructions.getFirst(); 596 while (insnNode != null) { 597 if (InlineCodegenUtil.isReturnOpcode(insnNode.getOpcode())) { 598 AbstractInsnNode previous = insnNode.getPrevious(); 599 MethodInsnNode flagNode; 600 boolean isLocalReturn = true; 601 String labelName = null; 602 if (previous != null && previous instanceof MethodInsnNode && InlineCodegenUtil.NON_LOCAL_RETURN.equals(((MethodInsnNode) previous).owner)) { 603 flagNode = (MethodInsnNode) previous; 604 labelName = flagNode.name; 605 } 606 607 if (labelName != null) { 608 isLocalReturn = labelOwner.isMyLabel(labelName); 609 //remove global return flag 610 if (isLocalReturn) { 611 instructions.remove(previous); 612 } 613 } 614 615 if (isLocalReturn && endLabel != null) { 616 LabelNode labelNode = (LabelNode) endLabel.info; 617 JumpInsnNode jumpInsnNode = new JumpInsnNode(Opcodes.GOTO, labelNode); 618 instructions.insert(insnNode, jumpInsnNode); 619 instructions.remove(insnNode); 620 insnNode = jumpInsnNode; 621 } 622 623 //genetate finally block before nonLocalReturn flag/return/goto 624 result.add(new ExternalFinallyBlockInfo(isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode()) 625 )); 626 } 627 insnNode = insnNode.getNext(); 628 } 629 return result; 630 } 631 632 public static class ExternalFinallyBlockInfo { 633 634 final AbstractInsnNode beforeIns; 635 636 final Type returnType; 637 638 public ExternalFinallyBlockInfo(AbstractInsnNode beforeIns, Type returnType) { 639 this.beforeIns = beforeIns; 640 this.returnType = returnType; 641 } 642 643 } 644 645 }