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 Set<LabelNode> possibleDeadLabels = new HashSet<LabelNode>();
360
361 while (cur != null) {
362 Frame<SourceValue> frame = sources[index];
363
364 if (frame != null) {
365 if (cur.getType() == AbstractInsnNode.METHOD_INSN) {
366 MethodInsnNode methodInsnNode = (MethodInsnNode) cur;
367 String owner = methodInsnNode.owner;
368 String desc = methodInsnNode.desc;
369 String name = methodInsnNode.name;
370 //TODO check closure
371 int paramLength = Type.getArgumentTypes(desc).length + 1;//non static
372 if (isInvokeOnLambda(owner, name) /*&& methodInsnNode.owner.equals(INLINE_RUNTIME)*/) {
373 SourceValue sourceValue = frame.getStack(frame.getStackSize() - paramLength);
374
375 LambdaInfo lambdaInfo = null;
376 int varIndex = -1;
377
378 if (sourceValue.insns.size() == 1) {
379 AbstractInsnNode insnNode = sourceValue.insns.iterator().next();
380
381 lambdaInfo = getLambdaIfExists(insnNode);
382 if (lambdaInfo != null) {
383 //remove inlinable access
384 node.instructions.remove(insnNode);
385 }
386 }
387
388 invokeCalls.add(new InvokeCall(varIndex, lambdaInfo));
389 }
390 else if (isAnonymousConstructorCall(owner, name)) {
391 Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>();
392 int paramStart = frame.getStackSize() - paramLength;
393
394 for (int i = 0; i < paramLength; i++) {
395 SourceValue sourceValue = frame.getStack(paramStart + i);
396 if (sourceValue.insns.size() == 1) {
397 AbstractInsnNode insnNode = sourceValue.insns.iterator().next();
398 LambdaInfo lambdaInfo = getLambdaIfExists(insnNode);
399 if (lambdaInfo != null) {
400 lambdaMapping.put(i, lambdaInfo);
401 node.instructions.remove(insnNode);
402 }
403 }
404 }
405
406 constructorInvocations.add(new ConstructorInvocation(owner, desc, lambdaMapping, isSameModule, inliningContext.classRegeneration));
407 }
408 }
409 }
410
411 AbstractInsnNode prevNode = cur;
412 cur = cur.getNext();
413 index++;
414
415 //given frame is <tt>null</tt> if and only if the corresponding instruction cannot be reached (dead code).
416 if (frame == null) {
417 //clean dead code otherwise there is problems in unreachable finally block, don't touch label it cause try/catch/finally problems
418 if (prevNode.getType() == AbstractInsnNode.LABEL) {
419 possibleDeadLabels.add((LabelNode) prevNode);
420 } else {
421 node.instructions.remove(prevNode);
422 }
423 } else {
424 //Cause we generate exception table for default handler using gaps (see ExpressionCodegen.visitTryExpression)
425 //it may occurs that interval for default handler starts before catch start label, so this label seems as dead,
426 //but as result all this labels will be merged into one (see KT-5863)
427 possibleDeadLabels.remove(prevNode.getPrevious());
428 }
429 }
430
431 //clean dead try/catch blocks
432 List<TryCatchBlockNode> blocks = node.tryCatchBlocks;
433 for (Iterator<TryCatchBlockNode> iterator = blocks.iterator(); iterator.hasNext(); ) {
434 TryCatchBlockNode block = iterator.next();
435 if (possibleDeadLabels.contains(block.start) && possibleDeadLabels.contains(block.end)) {
436 iterator.remove();
437 }
438 }
439
440 return node;
441 }
442
443 public LambdaInfo getLambdaIfExists(AbstractInsnNode insnNode) {
444 if (insnNode.getOpcode() == Opcodes.ALOAD) {
445 int varIndex = ((VarInsnNode) insnNode).var;
446 if (varIndex < parameters.totalSize()) {
447 return parameters.get(varIndex).getLambda();
448 }
449 }
450 else if (insnNode instanceof FieldInsnNode) {
451 FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode;
452 if (fieldInsnNode.name.startsWith("$$$")) {
453 return findCapturedField(fieldInsnNode, nodeRemapper).getLambda();
454 }
455 }
456
457 return null;
458 }
459
460 private static void removeClosureAssertions(MethodNode node) {
461 AbstractInsnNode cur = node.instructions.getFirst();
462 while (cur != null && cur.getNext() != null) {
463 AbstractInsnNode next = cur.getNext();
464 if (next.getType() == AbstractInsnNode.METHOD_INSN) {
465 MethodInsnNode methodInsnNode = (MethodInsnNode) next;
466 if (methodInsnNode.name.equals("checkParameterIsNotNull") && methodInsnNode.owner.equals("kotlin/jvm/internal/Intrinsics")) {
467 AbstractInsnNode prev = cur.getPrevious();
468
469 assert cur.getOpcode() == Opcodes.LDC : "checkParameterIsNotNull should go after LDC but " + cur;
470 assert prev.getOpcode() == Opcodes.ALOAD : "checkParameterIsNotNull should be invoked on local var but " + prev;
471
472 node.instructions.remove(prev);
473 node.instructions.remove(cur);
474 cur = next.getNext();
475 node.instructions.remove(next);
476 next = cur;
477 }
478 }
479 cur = next;
480 }
481 }
482
483 private void transformCaptured(@NotNull MethodNode node) {
484 if (nodeRemapper.isRoot()) {
485 return;
486 }
487
488 //Fold all captured variable chain - ALOAD 0 ALOAD this$0 GETFIELD $captured - to GETFIELD $$$$captured
489 //On future decoding this field could be inline or unfolded in another field access chain (it can differ in some missed this$0)
490 AbstractInsnNode cur = node.instructions.getFirst();
491 while (cur != null) {
492 if (cur instanceof VarInsnNode && cur.getOpcode() == Opcodes.ALOAD) {
493 if (((VarInsnNode) cur).var == 0) {
494 List<AbstractInsnNode> accessChain = getCapturedFieldAccessChain((VarInsnNode) cur);
495 AbstractInsnNode insnNode = nodeRemapper.foldFieldAccessChainIfNeeded(accessChain, node);
496 if (insnNode != null) {
497 cur = insnNode;
498 }
499 }
500 }
501 cur = cur.getNext();
502 }
503 }
504
505 @NotNull
506 public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) {
507 List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>();
508 fieldAccessChain.add(aload0);
509 AbstractInsnNode next = aload0.getNext();
510 while (next != null && next instanceof FieldInsnNode || next instanceof LabelNode) {
511 if (next instanceof LabelNode) {
512 next = next.getNext();
513 continue; //it will be delete on transformation
514 }
515 fieldAccessChain.add(next);
516 if ("this$0".equals(((FieldInsnNode) next).name)) {
517 next = next.getNext();
518 }
519 else {
520 break;
521 }
522 }
523
524 return fieldAccessChain;
525 }
526
527 public static void putStackValuesIntoLocals(List<Type> directOrder, int shift, InstructionAdapter iv, String descriptor) {
528 Type[] actualParams = Type.getArgumentTypes(descriptor);
529 assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!";
530
531 int size = 0;
532 for (Type next : directOrder) {
533 size += next.getSize();
534 }
535
536 shift += size;
537 int index = directOrder.size();
538
539 for (Type next : Lists.reverse(directOrder)) {
540 shift -= next.getSize();
541 Type typeOnStack = actualParams[--index];
542 if (!typeOnStack.equals(next)) {
543 StackValue.onStack(typeOnStack).put(next, iv);
544 }
545 iv.store(shift, next);
546 }
547 }
548
549 //TODO: check it's external module
550 //TODO?: assert method exists in facade?
551 public String changeOwnerForExternalPackage(String type, int opcode) {
552 if (isSameModule || (opcode & Opcodes.INVOKESTATIC) == 0) {
553 return type;
554 }
555
556 JvmClassName name = JvmClassName.byInternalName(type);
557 String packageClassInternalName = PackageClassUtils.getPackageClassInternalName(name.getPackageFqName());
558 if (type.startsWith(packageClassInternalName + '$')) {
559 VirtualFile virtualFile = InlineCodegenUtil.findVirtualFile(inliningContext.state.getProject(), type);
560 if (virtualFile != null) {
561 KotlinJvmBinaryClass klass = KotlinBinaryClassCache.getKotlinBinaryClass(virtualFile);
562 if (klass != null && klass.getClassHeader().getSyntheticClassKind() == KotlinSyntheticClass.Kind.PACKAGE_PART) {
563 return packageClassInternalName;
564 }
565 }
566 }
567
568 return type;
569 }
570
571 @NotNull
572 public RuntimeException wrapException(@NotNull Exception originalException, @NotNull MethodNode node, @NotNull String errorSuffix) {
573 if (originalException instanceof InlineException) {
574 return new InlineException(errorPrefix + ": " + errorSuffix, originalException);
575 }
576 else {
577 return new InlineException(errorPrefix + ": " + errorSuffix + "\ncause: " +
578 getNodeText(node), originalException);
579 }
580 }
581
582 @NotNull
583 //process local and global returns (local substituted with goto end-label global kept unchanged)
584 public static List<ExternalFinallyBlockInfo> processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) {
585 if (!remapReturn) {
586 return Collections.emptyList();
587 }
588 List<ExternalFinallyBlockInfo> result = new ArrayList<ExternalFinallyBlockInfo>();
589 InsnList instructions = node.instructions;
590 AbstractInsnNode insnNode = instructions.getFirst();
591 while (insnNode != null) {
592 if (InlineCodegenUtil.isReturnOpcode(insnNode.getOpcode())) {
593 AbstractInsnNode previous = insnNode.getPrevious();
594 MethodInsnNode flagNode;
595 boolean isLocalReturn = true;
596 String labelName = null;
597 if (previous != null && previous instanceof MethodInsnNode && InlineCodegenUtil.NON_LOCAL_RETURN.equals(((MethodInsnNode) previous).owner)) {
598 flagNode = (MethodInsnNode) previous;
599 labelName = flagNode.name;
600 }
601
602 if (labelName != null) {
603 isLocalReturn = labelOwner.isMyLabel(labelName);
604 //remove global return flag
605 if (isLocalReturn) {
606 instructions.remove(previous);
607 }
608 }
609
610 if (isLocalReturn && endLabel != null) {
611 LabelNode labelNode = (LabelNode) endLabel.info;
612 JumpInsnNode jumpInsnNode = new JumpInsnNode(Opcodes.GOTO, labelNode);
613 instructions.insert(insnNode, jumpInsnNode);
614 instructions.remove(insnNode);
615 insnNode = jumpInsnNode;
616 }
617
618 //genetate finally block before nonLocalReturn flag/return/goto
619 result.add(new ExternalFinallyBlockInfo(isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode())
620 ));
621 }
622 insnNode = insnNode.getNext();
623 }
624 return result;
625 }
626
627 public static class ExternalFinallyBlockInfo {
628
629 final AbstractInsnNode beforeIns;
630
631 final Type returnType;
632
633 public ExternalFinallyBlockInfo(AbstractInsnNode beforeIns, Type returnType) {
634 this.beforeIns = beforeIns;
635 this.returnType = returnType;
636 }
637
638 }
639
640 }