001    /*
002     * Copyright 2010-2015 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.kotlin.codegen.inline;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.util.ArrayUtil;
021    import com.intellij.util.containers.SmartHashSet;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.codegen.ClosureCodegen;
025    import org.jetbrains.kotlin.codegen.StackValue;
026    import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
027    import org.jetbrains.kotlin.codegen.optimization.MandatoryMethodTransformer;
028    import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
029    import org.jetbrains.org.objectweb.asm.Label;
030    import org.jetbrains.org.objectweb.asm.MethodVisitor;
031    import org.jetbrains.org.objectweb.asm.Opcodes;
032    import org.jetbrains.org.objectweb.asm.Type;
033    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
034    import org.jetbrains.org.objectweb.asm.commons.Method;
035    import org.jetbrains.org.objectweb.asm.commons.RemappingMethodAdapter;
036    import org.jetbrains.org.objectweb.asm.tree.*;
037    import org.jetbrains.org.objectweb.asm.tree.analysis.*;
038    
039    import java.util.*;
040    
041    import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*;
042    
043    public class MethodInliner {
044    
045        private final MethodNode node;
046    
047        private final Parameters parameters;
048    
049        private final InliningContext inliningContext;
050    
051        private final FieldRemapper nodeRemapper;
052    
053        private final boolean isSameModule;
054    
055        private final String errorPrefix;
056    
057        private final SourceMapper sourceMapper;
058    
059        private final InlineCallSiteInfo inlineCallSiteInfo;
060    
061        private final JetTypeMapper typeMapper;
062    
063        private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>();
064    
065        //keeps order
066        private final List<AnonymousObjectGeneration> anonymousObjectGenerations = new ArrayList<AnonymousObjectGeneration>();
067        //current state
068        private final Map<String, String> currentTypeMapping = new HashMap<String, String>();
069    
070        private final InlineResult result;
071    
072        private int lambdasFinallyBlocks;
073    
074        /*
075         *
076         * @param node
077         * @param parameters
078         * @param inliningContext
079         * @param lambdaType - in case on lambda 'invoke' inlining
080         */
081        public MethodInliner(
082                @NotNull MethodNode node,
083                @NotNull Parameters parameters,
084                @NotNull InliningContext inliningContext,
085                @NotNull FieldRemapper nodeRemapper,
086                boolean isSameModule,
087                @NotNull String errorPrefix,
088                @NotNull SourceMapper sourceMapper,
089                @NotNull InlineCallSiteInfo inlineCallSiteInfo
090        ) {
091            this.node = node;
092            this.parameters = parameters;
093            this.inliningContext = inliningContext;
094            this.nodeRemapper = nodeRemapper;
095            this.isSameModule = isSameModule;
096            this.errorPrefix = errorPrefix;
097            this.sourceMapper = sourceMapper;
098            this.inlineCallSiteInfo = inlineCallSiteInfo;
099            this.typeMapper = inliningContext.state.getTypeMapper();
100            this.result = InlineResult.create();
101        }
102    
103        public InlineResult doInline(
104                @NotNull MethodVisitor adapter,
105                @NotNull LocalVarRemapper remapper,
106                boolean remapReturn,
107                @NotNull LabelOwner labelOwner
108        ) {
109            return doInline(adapter, remapper, remapReturn, labelOwner, 0);
110        }
111    
112        private InlineResult doInline(
113                @NotNull MethodVisitor adapter,
114                @NotNull LocalVarRemapper remapper,
115                boolean remapReturn,
116                @NotNull LabelOwner labelOwner,
117                int finallyDeepShift
118        ) {
119            //analyze body
120            MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node, finallyDeepShift);
121    
122            //substitute returns with "goto end" instruction to keep non local returns in lambdas
123            Label end = new Label();
124            transformedNode = doInline(transformedNode);
125            removeClosureAssertions(transformedNode);
126            InsnList instructions = transformedNode.instructions;
127            instructions.resetLabels();
128    
129            MethodNode resultNode = new MethodNode(InlineCodegenUtil.API, transformedNode.access, transformedNode.name, transformedNode.desc,
130                                             transformedNode.signature, ArrayUtil.toStringArray(transformedNode.exceptions));
131            RemapVisitor visitor = new RemapVisitor(resultNode, remapper, nodeRemapper);
132            try {
133                transformedNode.accept(visitor);
134            }
135            catch (Exception e) {
136                throw wrapException(e, transformedNode, "couldn't inline method call");
137            }
138    
139            resultNode.visitLabel(end);
140    
141            if (inliningContext.isRoot()) {
142                InternalFinallyBlockInliner.processInlineFunFinallyBlocks(resultNode, lambdasFinallyBlocks, ((StackValue.Local)remapper.remap(parameters.getArgsSizeOnStack() + 1).value).index);
143            }
144    
145            processReturns(resultNode, labelOwner, remapReturn, end);
146            //flush transformed node to output
147            resultNode.accept(new InliningInstructionAdapter(adapter));
148    
149            sourceMapper.endMapping();
150            return result;
151        }
152    
153        private MethodNode doInline(final MethodNode node) {
154    
155            final Deque<InvokeCall> currentInvokes = new LinkedList<InvokeCall>(invokeCalls);
156    
157            final MethodNode resultNode = new MethodNode(node.access, node.name, node.desc, node.signature, null);
158    
159            final Iterator<AnonymousObjectGeneration> iterator = anonymousObjectGenerations.iterator();
160    
161            final TypeRemapper remapper = TypeRemapper.createFrom(currentTypeMapping);
162            RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter(
163                    resultNode.access,
164                    resultNode.desc,
165                    resultNode,
166                    new AsmTypeRemapper(remapper, inliningContext.getRoot().typeParameterMappings == null, result)
167            );
168    
169            final int markerShift = InlineCodegenUtil.calcMarkerShift(parameters, node);
170            InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.getArgsSizeOnStack(), sourceMapper) {
171    
172                private AnonymousObjectGeneration anonymousObjectGen;
173                private void handleAnonymousObjectGeneration() {
174                    anonymousObjectGen = iterator.next();
175    
176                    if (anonymousObjectGen.shouldRegenerate()) {
177                        //TODO: need poping of type but what to do with local funs???
178                        String oldClassName = anonymousObjectGen.getOwnerInternalName();
179                        String newClassName = inliningContext.nameGenerator.genLambdaClassName();
180                        remapper.addMapping(oldClassName, newClassName);
181                        AnonymousObjectTransformer transformer =
182                                new AnonymousObjectTransformer(oldClassName,
183                                                               inliningContext
184                                                                       .subInlineWithClassRegeneration(
185                                                                               inliningContext.nameGenerator,
186                                                                               currentTypeMapping,
187                                                                               anonymousObjectGen,
188                                                                               inlineCallSiteInfo),
189                                                               isSameModule, Type.getObjectType(newClassName)
190                                );
191    
192                        InlineResult transformResult = transformer.doTransform(anonymousObjectGen, nodeRemapper);
193                        result.addAllClassesToRemove(transformResult);
194                        result.addChangedType(oldClassName, newClassName);
195    
196                        if (inliningContext.isInliningLambda && !anonymousObjectGen.isStaticOrigin()) {
197                            // this class is transformed and original not used so we should remove original one after inlining
198                            // Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC within lambda
199                            // because it can be local function from outer scope
200                            result.addClassToRemove(oldClassName);
201                        }
202    
203                        if (transformResult.getReifiedTypeParametersUsages().wereUsedReifiedParameters()) {
204                            ReifiedTypeInliner.putNeedClassReificationMarker(mv);
205                            result.getReifiedTypeParametersUsages().mergeAll(transformResult.getReifiedTypeParametersUsages());
206                        }
207                    }
208                }
209    
210                @Override
211                public void anew(@NotNull Type type) {
212                    if (isAnonymousConstructorCall(type.getInternalName(), "<init>")) {
213                        handleAnonymousObjectGeneration();
214                    }
215    
216                    //in case of regenerated anonymousObjectGen type would be remapped to new one via remappingMethodAdapter
217                    super.anew(type);
218                }
219    
220                @Override
221                public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
222                    if (/*INLINE_RUNTIME.equals(owner) &&*/ isInvokeOnLambda(owner, name)) { //TODO add method
223                        assert !currentInvokes.isEmpty();
224                        InvokeCall invokeCall = currentInvokes.remove();
225                        LambdaInfo info = invokeCall.lambdaInfo;
226    
227                        if (info == null) {
228                            //noninlinable lambda
229                            super.visitMethodInsn(opcode, owner, name, desc, itf);
230                            return;
231                        }
232    
233                        int valueParamShift = Math.max(getNextLocalIndex(), markerShift);//NB: don't inline cause it changes
234                        putStackValuesIntoLocals(info.getInvokeParamsWithoutCaptured(), valueParamShift, this, desc);
235    
236                        addInlineMarker(this, true);
237                        Parameters lambdaParameters = info.addAllParameters(nodeRemapper);
238    
239                        InlinedLambdaRemapper newCapturedRemapper =
240                                new InlinedLambdaRemapper(info.getLambdaClassType().getInternalName(), nodeRemapper, lambdaParameters);
241    
242                        setLambdaInlining(true);
243                        SMAP lambdaSMAP = info.getNode().getClassSMAP();
244                        SourceMapper mapper =
245                                inliningContext.classRegeneration && !inliningContext.isInliningLambda ?
246                                new NestedSourceMapper(sourceMapper, lambdaSMAP.getIntervals(), lambdaSMAP.getSourceInfo())
247                                : new InlineLambdaSourceMapper(sourceMapper.getParent(), info.getNode());
248                        MethodInliner inliner = new MethodInliner(info.getNode().getNode(), lambdaParameters,
249                                                                  inliningContext.subInlineLambda(info),
250                                                                  newCapturedRemapper, true /*cause all calls in same module as lambda*/,
251                                                                  "Lambda inlining " + info.getLambdaClassType().getInternalName(),
252                                                                  mapper, inlineCallSiteInfo);
253    
254                        LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift);
255                        InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info, invokeCall.finallyDepthShift);//TODO add skipped this and receiver
256                        result.addAllClassesToRemove(lambdaResult);
257    
258                        //return value boxing/unboxing
259                        Method bridge =
260                                typeMapper.mapSignature(ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor())).getAsmMethod();
261                        Method delegate = typeMapper.mapSignature(info.getFunctionDescriptor()).getAsmMethod();
262                        StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this);
263                        setLambdaInlining(false);
264                        addInlineMarker(this, false);
265                        mapper.endMapping();
266                    }
267                    else if (isAnonymousConstructorCall(owner, name)) { //TODO add method
268                        assert anonymousObjectGen != null : "<init> call not corresponds to new call" + owner + " " + name;
269                        if (anonymousObjectGen.shouldRegenerate()) {
270                            //put additional captured parameters on stack
271                            for (CapturedParamDesc capturedParamDesc : anonymousObjectGen.getAllRecapturedParameters()) {
272                                visitFieldInsn(Opcodes.GETSTATIC, capturedParamDesc.getContainingLambdaName(),
273                                               "$$$" + capturedParamDesc.getFieldName(), capturedParamDesc.getType().getDescriptor());
274                            }
275                            String newInternalName = anonymousObjectGen.getNewLambdaType().getInternalName();
276                            super.visitMethodInsn(opcode, newInternalName, name, anonymousObjectGen.getNewConstructorDescriptor(), itf);
277    
278                            //TODO: add new inner class also for other contexts
279                            if (inliningContext.getParent() instanceof RegeneratedClassContext) {
280                                inliningContext.getParent().typeRemapper.addAdditionalMappings(anonymousObjectGen.getOwnerInternalName(), newInternalName);
281                            }
282    
283                            anonymousObjectGen = null;
284                        } else {
285                            super.visitMethodInsn(opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf);
286                        }
287                    }
288                    else if (ReifiedTypeInliner.isNeedClassReificationMarker(new MethodInsnNode(opcode, owner, name, desc, false))) {
289                        // we will put it if needed in anew processing
290                    }
291                    else {
292                        super.visitMethodInsn(opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf);
293                    }
294                }
295    
296                @Override
297                public void visitFieldInsn(int opcode, @NotNull String owner, @NotNull String name, @NotNull String desc) {
298                    if (opcode == Opcodes.GETSTATIC && isAnonymousSingletonLoad(owner, name)) {
299                        handleAnonymousObjectGeneration();
300                    }
301                    super.visitFieldInsn(opcode, owner, name, desc);
302                }
303    
304                @Override
305                public void visitMaxs(int stack, int locals) {
306                    lambdasFinallyBlocks = resultNode.tryCatchBlocks.size();
307                    super.visitMaxs(stack, locals);
308                }
309    
310            };
311    
312            node.accept(lambdaInliner);
313    
314            return resultNode;
315        }
316    
317        @NotNull
318        public static CapturedParamInfo findCapturedField(FieldInsnNode node, FieldRemapper fieldRemapper) {
319            assert node.name.startsWith("$$$") : "Captured field template should start with $$$ prefix";
320            FieldInsnNode fin = new FieldInsnNode(node.getOpcode(), node.owner, node.name.substring(3), node.desc);
321            CapturedParamInfo field = fieldRemapper.findField(fin);
322            if (field == null) {
323                throw new IllegalStateException("Couldn't find captured field " + node.owner + "." + node.name + " in " + fieldRemapper.getLambdaInternalName());
324            }
325            return field;
326        }
327    
328        @NotNull
329        public MethodNode prepareNode(@NotNull MethodNode node, int finallyDeepShift) {
330            final int capturedParamsSize = parameters.getCapturedArgsSizeOnStack();
331            final int realParametersSize = parameters.getRealArgsSizeOnStack();
332            Type[] types = Type.getArgumentTypes(node.desc);
333            Type returnType = Type.getReturnType(node.desc);
334    
335            List<Type> capturedTypes = parameters.getCapturedTypes();
336            Type[] allTypes = ArrayUtil.mergeArrays(types, capturedTypes.toArray(new Type[capturedTypes.size()]));
337    
338            node.instructions.resetLabels();
339            MethodNode transformedNode = new MethodNode(InlineCodegenUtil.API, node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null) {
340    
341                private final boolean isInliningLambda = nodeRemapper.isInsideInliningLambda();
342    
343                private int getNewIndex(int var) {
344                    return var + (var < realParametersSize ? 0 : capturedParamsSize);
345                }
346    
347                @Override
348                public void visitVarInsn(int opcode, int var) {
349                    super.visitVarInsn(opcode, getNewIndex(var));
350                }
351    
352                @Override
353                public void visitIincInsn(int var, int increment) {
354                    super.visitIincInsn(getNewIndex(var), increment);
355                }
356    
357                @Override
358                public void visitMaxs(int maxStack, int maxLocals) {
359                    super.visitMaxs(maxStack, maxLocals + capturedParamsSize);
360                }
361    
362                @Override
363                public void visitLineNumber(int line, @NotNull Label start) {
364                    if(isInliningLambda || InlineCodegenUtil.GENERATE_SMAP) {
365                        super.visitLineNumber(line, start);
366                    }
367                }
368    
369                @Override
370                public void visitLocalVariable(
371                        @NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index
372                ) {
373                    if (isInliningLambda || InlineCodegenUtil.GENERATE_SMAP) {
374                        String varSuffix = inliningContext.isRoot() &&
375                                           !((RootInliningContext) inliningContext).isDefaultCompilation &&
376                                           !InlineCodegenUtil.isFakeLocalVariableForInline(name) ?
377                                           INLINE_FUN_VAR_SUFFIX : "";
378                        String varName = !varSuffix.isEmpty() && name.equals("this") ? name + "_" : name;
379                        super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index));
380                    }
381                }
382            };
383    
384            node.accept(transformedNode);
385    
386            transformCaptured(transformedNode);
387            transformFinallyDeepIndex(transformedNode, finallyDeepShift);
388    
389            return transformedNode;
390        }
391    
392        @NotNull
393        protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node, int finallyDeepShift) {
394            node = prepareNode(node, finallyDeepShift);
395    
396            try {
397                new MandatoryMethodTransformer().transform("fake", node);
398            }
399            catch (Throwable e) {
400                throw wrapException(e, node, "couldn't inline method call");
401            }
402    
403            Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()) {
404                @NotNull
405                @Override
406                protected Frame<SourceValue> newFrame(
407                        int nLocals, int nStack
408                ) {
409                    return new Frame<SourceValue>(nLocals, nStack) {
410                        @Override
411                        public void execute(
412                                @NotNull AbstractInsnNode insn, Interpreter<SourceValue> interpreter
413                        ) throws AnalyzerException {
414                            if (insn.getOpcode() == Opcodes.RETURN) {
415                                //there is exception on void non local return in frame
416                                return;
417                            }
418                            super.execute(insn, interpreter);
419                        }
420                    };
421                }
422            };
423    
424            Frame<SourceValue>[] sources;
425            try {
426                sources = analyzer.analyze("fake", node);
427            }
428            catch (AnalyzerException e) {
429                throw wrapException(e, node, "couldn't inline method call");
430            }
431            Set<AbstractInsnNode> toDelete = new SmartHashSet<AbstractInsnNode>();
432            InstructionsAndFrames instructionsAndFrames = new InstructionsAndFrames(sources, node.instructions);
433            AbstractInsnNode cur = node.instructions.getFirst();
434            int index = 0;
435    
436            boolean awaitClassReification = false;
437            int currentFinallyDeep = 0;
438    
439            while (cur != null) {
440                Frame<SourceValue> frame = sources[index];
441    
442                if (frame != null) {
443                    if (ReifiedTypeInliner.isNeedClassReificationMarker(cur)) {
444                        awaitClassReification = true;
445                    }
446                    else if (cur.getType() == AbstractInsnNode.METHOD_INSN) {
447                        if (InlineCodegenUtil.isFinallyStart(cur)) {
448                            //TODO deep index calc could be more precise
449                            currentFinallyDeep = InlineCodegenUtil.getConstant(cur.getPrevious());
450                        }
451    
452                        MethodInsnNode methodInsnNode = (MethodInsnNode) cur;
453                        String owner = methodInsnNode.owner;
454                        String desc = methodInsnNode.desc;
455                        String name = methodInsnNode.name;
456                        //TODO check closure
457                        Type[] argTypes = Type.getArgumentTypes(desc);
458                        int paramCount = argTypes.length + 1;//non static
459                        int firstParameterIndex = frame.getStackSize() - paramCount;
460                        if (isInvokeOnLambda(owner, name) /*&& methodInsnNode.owner.equals(INLINE_RUNTIME)*/) {
461                            SourceValue sourceValue = frame.getStack(firstParameterIndex);
462    
463                            LambdaInfo lambdaInfo = null;
464                            int varIndex = -1;
465    
466                            if (sourceValue.insns.size() == 1) {
467                                AbstractInsnNode insnNode = sourceValue.insns.iterator().next();
468                                AbstractInsnNode processingInstruction = insnNode;
469    
470                                if (insnNode.getOpcode() == Opcodes.SWAP) {
471                                    processingInstruction = InlineCodegenUtil.getPrevMeaningful(insnNode);
472                                }
473                                lambdaInfo = getLambdaIfExistsAndMarkInstructions(processingInstruction, frame, instructionsAndFrames, toDelete);
474                                if (lambdaInfo != null) {
475                                    //remove inlinable access
476                                    assert processingInstruction != null;
477                                    InlineCodegenUtil.removeInterval(node, processingInstruction, insnNode);
478                                }
479                            }
480    
481                            invokeCalls.add(new InvokeCall(varIndex, lambdaInfo, currentFinallyDeep));
482                        }
483                        else if (isAnonymousConstructorCall(owner, name)) {
484                            Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>();
485    
486                            int offset = 0;
487                            for (int i = 0; i < paramCount; i++) {
488                                SourceValue sourceValue = frame.getStack(firstParameterIndex + i);
489                                if (sourceValue.insns.size() == 1) {
490                                    AbstractInsnNode insnNode = sourceValue.insns.iterator().next();
491                                    LambdaInfo lambdaInfo = getLambdaIfExistsAndMarkInstructions(insnNode, frame, instructionsAndFrames, toDelete);
492                                    if (lambdaInfo != null) {
493                                        lambdaMapping.put(offset, lambdaInfo);
494                                        node.instructions.remove(insnNode);
495                                    }
496                                }
497                                offset += i == 0 ? 1 : argTypes[i - 1].getSize();
498                            }
499    
500                            anonymousObjectGenerations.add(
501                                    buildConstructorInvocation(
502                                            owner, desc, lambdaMapping, awaitClassReification
503                                    )
504                            );
505                            awaitClassReification = false;
506                        }
507                    }
508                    else if (cur.getOpcode() == Opcodes.GETSTATIC) {
509                        FieldInsnNode fieldInsnNode = (FieldInsnNode) cur;
510                        String owner = fieldInsnNode.owner;
511                        if (isAnonymousSingletonLoad(owner, fieldInsnNode.name)) {
512                            anonymousObjectGenerations.add(
513                                    new AnonymousObjectGeneration(
514                                            owner, isSameModule, awaitClassReification, isAlreadyRegenerated(owner), true
515                                    )
516                            );
517                            awaitClassReification = false;
518                        }
519                    }
520                }
521                AbstractInsnNode prevNode = cur;
522                cur = cur.getNext();
523                index++;
524    
525                //given frame is <tt>null</tt> if and only if the corresponding instruction cannot be reached (dead code).
526                if (frame == null) {
527                    //clean dead code otherwise there is problems in unreachable finally block, don't touch label it cause try/catch/finally problems
528                    if (prevNode.getType() == AbstractInsnNode.LABEL) {
529                        //NB: Cause we generate exception table for default handler using gaps (see ExpressionCodegen.visitTryExpression)
530                        //it may occurs that interval for default handler starts before catch start label, so this label seems as dead,
531                        //but as result all this labels will be merged into one (see KT-5863)
532                    } else {
533                        node.instructions.remove(prevNode);
534                    }
535                }
536            }
537    
538            for (AbstractInsnNode insnNode : toDelete) {
539                node.instructions.remove(insnNode);
540            }
541    
542            //clean dead try/catch blocks
543            List<TryCatchBlockNode> blocks = node.tryCatchBlocks;
544            for (Iterator<TryCatchBlockNode> iterator = blocks.iterator(); iterator.hasNext(); ) {
545                TryCatchBlockNode block = iterator.next();
546                if (isEmptyTryInterval(block)) {
547                    iterator.remove();
548                }
549            }
550    
551            return node;
552        }
553    
554        private static boolean isEmptyTryInterval(@NotNull TryCatchBlockNode tryCatchBlockNode) {
555            LabelNode start = tryCatchBlockNode.start;
556            AbstractInsnNode end = tryCatchBlockNode.end;
557            while (end != start && end instanceof LabelNode) {
558                end = end.getPrevious();
559            }
560            return start == end;
561        }
562    
563        @NotNull
564        private AnonymousObjectGeneration buildConstructorInvocation(
565                @NotNull String owner,
566                @NotNull String desc,
567                @NotNull Map<Integer, LambdaInfo> lambdaMapping,
568                boolean needReification
569        ) {
570            return new AnonymousObjectGeneration(
571                    owner, needReification, isSameModule, lambdaMapping,
572                    inliningContext.classRegeneration,
573                    isAlreadyRegenerated(owner),
574                    desc,
575                    false
576            );
577        }
578    
579        private boolean isAlreadyRegenerated(@NotNull String owner) {
580            return inliningContext.typeRemapper.hasNoAdditionalMapping(owner);
581        }
582    
583        @Nullable
584        private LambdaInfo getLambdaIfExistsAndMarkInstructions(
585                @Nullable AbstractInsnNode insnNode,
586                @NotNull Frame<SourceValue> localFrame,
587                @NotNull InstructionsAndFrames insAndFrames,
588                @NotNull Set<AbstractInsnNode> toDelete
589        ) {
590            LambdaInfo lambdaInfo = getLambdaIfExists(insnNode);
591    
592            if (lambdaInfo == null && insnNode instanceof VarInsnNode && insnNode.getOpcode() == Opcodes.ALOAD) {
593                int varIndex = ((VarInsnNode) insnNode).var;
594                SourceValue local = localFrame.getLocal(varIndex);
595                if (local.insns.size() == 1) {
596                    AbstractInsnNode storeIns = local.insns.iterator().next();
597                    if (storeIns instanceof VarInsnNode && storeIns.getOpcode() == Opcodes.ASTORE) {
598                        Frame<SourceValue> frame = insAndFrames.get(storeIns);
599                        if (frame != null) {
600                            SourceValue topOfStack = frame.getStack(frame.getStackSize() - 1);
601                            if(topOfStack.insns.size() == 1) {
602                                AbstractInsnNode lambdaAload = topOfStack.insns.iterator().next();
603                                lambdaInfo = getLambdaIfExistsAndMarkInstructions(lambdaAload, frame, insAndFrames, toDelete);
604                                if (lambdaInfo != null) {
605                                    toDelete.add(storeIns);
606                                    toDelete.add(lambdaAload);
607                                }
608                            }
609                        }
610                    }
611                }
612            }
613    
614            return lambdaInfo;
615        }
616    
617        @Nullable
618        private LambdaInfo getLambdaIfExists(@Nullable AbstractInsnNode insnNode) {
619            if (insnNode == null) {
620                return null;
621            }
622    
623            if (insnNode.getOpcode() == Opcodes.ALOAD) {
624                int varIndex = ((VarInsnNode) insnNode).var;
625                return getLambdaIfExists(varIndex);
626            }
627            else if (insnNode instanceof FieldInsnNode) {
628                FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode;
629                if (fieldInsnNode.name.startsWith("$$$")) {
630                    return findCapturedField(fieldInsnNode, nodeRemapper).getLambda();
631                }
632            }
633    
634            return null;
635        }
636    
637        private LambdaInfo getLambdaIfExists(int varIndex) {
638            if (varIndex < parameters.getArgsSizeOnStack()) {
639                return parameters.getParameterByDeclarationSlot(varIndex).getLambda();
640            }
641            return null;
642        }
643    
644        private static void removeClosureAssertions(MethodNode node) {
645            AbstractInsnNode cur = node.instructions.getFirst();
646            while (cur != null && cur.getNext() != null) {
647                AbstractInsnNode next = cur.getNext();
648                if (next.getType() == AbstractInsnNode.METHOD_INSN) {
649                    MethodInsnNode methodInsnNode = (MethodInsnNode) next;
650                    if (methodInsnNode.name.equals("checkParameterIsNotNull") && methodInsnNode.owner.equals(IntrinsicMethods.INTRINSICS_CLASS_NAME)) {
651                        AbstractInsnNode prev = cur.getPrevious();
652    
653                        assert cur.getOpcode() == Opcodes.LDC : "checkParameterIsNotNull should go after LDC but " + cur;
654                        assert prev.getOpcode() == Opcodes.ALOAD : "checkParameterIsNotNull should be invoked on local var but " + prev;
655    
656                        node.instructions.remove(prev);
657                        node.instructions.remove(cur);
658                        cur = next.getNext();
659                        node.instructions.remove(next);
660                        next = cur;
661                    }
662                }
663                cur = next;
664            }
665        }
666    
667        private void transformCaptured(@NotNull MethodNode node) {
668            if (nodeRemapper.isRoot()) {
669                return;
670            }
671    
672            //Fold all captured variable chain - ALOAD 0 ALOAD this$0 GETFIELD $captured - to GETFIELD $$$$captured
673            //On future decoding this field could be inline or unfolded in another field access chain (it can differ in some missed this$0)
674            AbstractInsnNode cur = node.instructions.getFirst();
675            while (cur != null) {
676                if (cur instanceof VarInsnNode && cur.getOpcode() == Opcodes.ALOAD) {
677                    int varIndex = ((VarInsnNode) cur).var;
678                    if (varIndex == 0 || nodeRemapper.processNonAload0FieldAccessChains(getLambdaIfExists(varIndex) != null)) {
679                        List<AbstractInsnNode> accessChain = getCapturedFieldAccessChain((VarInsnNode) cur);
680                        AbstractInsnNode insnNode = nodeRemapper.foldFieldAccessChainIfNeeded(accessChain, node);
681                        if (insnNode != null) {
682                            cur = insnNode;
683                        }
684                    }
685                }
686                cur = cur.getNext();
687            }
688        }
689    
690        private static void transformFinallyDeepIndex(@NotNull MethodNode node, int finallyDeepShift) {
691            if (finallyDeepShift == 0) {
692                return;
693            }
694    
695            AbstractInsnNode cur = node.instructions.getFirst();
696            while (cur != null) {
697                if (cur instanceof MethodInsnNode && InlineCodegenUtil.isFinallyMarker(cur)) {
698                    AbstractInsnNode constant = cur.getPrevious();
699                    int curDeep = InlineCodegenUtil.getConstant(constant);
700                    node.instructions.insert(constant, new LdcInsnNode(curDeep + finallyDeepShift));
701                    node.instructions.remove(constant);
702                }
703                cur = cur.getNext();
704            }
705        }
706    
707        @NotNull
708        public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) {
709            List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>();
710            fieldAccessChain.add(aload0);
711            AbstractInsnNode next = aload0.getNext();
712            while (next != null && next instanceof FieldInsnNode || next instanceof LabelNode) {
713                if (next instanceof LabelNode) {
714                    next = next.getNext();
715                    continue; //it will be delete on transformation
716                }
717                fieldAccessChain.add(next);
718                if ("this$0".equals(((FieldInsnNode) next).name)) {
719                    next = next.getNext();
720                }
721                else {
722                    break;
723                }
724            }
725    
726            return fieldAccessChain;
727        }
728    
729        public static void putStackValuesIntoLocals(List<Type> directOrder, int shift, InstructionAdapter iv, String descriptor) {
730            Type[] actualParams = Type.getArgumentTypes(descriptor);
731            assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!";
732    
733            int size = 0;
734            for (Type next : directOrder) {
735                size += next.getSize();
736            }
737    
738            shift += size;
739            int index = directOrder.size();
740    
741            for (Type next : Lists.reverse(directOrder)) {
742                shift -= next.getSize();
743                Type typeOnStack = actualParams[--index];
744                if (!typeOnStack.equals(next)) {
745                    StackValue.onStack(typeOnStack).put(next, iv);
746                }
747                iv.store(shift, next);
748            }
749        }
750    
751        //TODO: check it's external module
752        //TODO?: assert method exists in facade?
753        public String changeOwnerForExternalPackage(String type, int opcode) {
754            //if (isSameModule || (opcode & Opcodes.INVOKESTATIC) == 0) {
755            //    return type;
756            //}
757    
758            //JvmClassName name = JvmClassName.byInternalName(type);
759            //String packageClassInternalName = PackageClassUtils.getPackageClassInternalName(name.getPackageFqName());
760            //if (type.startsWith(packageClassInternalName + '$')) {
761            //    VirtualFile virtualFile = InlineCodegenUtil.findVirtualFile(inliningContext.state.getProject(), type);
762            //    if (virtualFile != null) {
763            //        KotlinJvmBinaryClass klass = KotlinBinaryClassCache.getKotlinBinaryClass(virtualFile);
764            //        if (klass != null && klass.getClassHeader().getSyntheticClassKind() == KotlinSyntheticClass.Kind.PACKAGE_PART) {
765            //            return packageClassInternalName;
766            //        }
767            //    }
768            //}
769    
770            return type;
771        }
772    
773        @NotNull
774        public RuntimeException wrapException(@NotNull Throwable originalException, @NotNull MethodNode node, @NotNull String errorSuffix) {
775            if (originalException instanceof InlineException) {
776                return new InlineException(errorPrefix + ": " + errorSuffix, originalException);
777            }
778            else {
779                return new InlineException(errorPrefix + ": " + errorSuffix + "\ncause: " +
780                                           getNodeText(node), originalException);
781            }
782        }
783    
784        @NotNull
785        //process local and global returns (local substituted with goto end-label global kept unchanged)
786        public static List<PointForExternalFinallyBlocks> processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) {
787            if (!remapReturn) {
788                return Collections.emptyList();
789            }
790            List<PointForExternalFinallyBlocks> result = new ArrayList<PointForExternalFinallyBlocks>();
791            InsnList instructions = node.instructions;
792            AbstractInsnNode insnNode = instructions.getFirst();
793            while (insnNode != null) {
794                if (InlineCodegenUtil.isReturnOpcode(insnNode.getOpcode())) {
795                    AbstractInsnNode previous = insnNode.getPrevious();
796                    MethodInsnNode flagNode;
797                    boolean isLocalReturn = true;
798                    String labelName = null;
799                    if (previous != null && previous instanceof MethodInsnNode && InlineCodegenUtil.NON_LOCAL_RETURN.equals(((MethodInsnNode) previous).owner)) {
800                        flagNode = (MethodInsnNode) previous;
801                        labelName = flagNode.name;
802                    }
803    
804                    if (labelName != null) {
805                        isLocalReturn = labelOwner.isMyLabel(labelName);
806                        //remove global return flag
807                        if (isLocalReturn) {
808                            instructions.remove(previous);
809                        }
810                    }
811    
812                    if (isLocalReturn && endLabel != null) {
813                        LabelNode labelNode = (LabelNode) endLabel.info;
814                        JumpInsnNode jumpInsnNode = new JumpInsnNode(Opcodes.GOTO, labelNode);
815                        instructions.insert(insnNode, jumpInsnNode);
816                        instructions.remove(insnNode);
817                        insnNode = jumpInsnNode;
818                    }
819    
820                    //genetate finally block before nonLocalReturn flag/return/goto
821                    LabelNode label = new LabelNode();
822                    instructions.insert(insnNode, label);
823                    result.add(new PointForExternalFinallyBlocks(getInstructionToInsertFinallyBefore(insnNode, isLocalReturn),
824                                                                 getReturnType(insnNode.getOpcode()),
825                                                                 label));
826                }
827                insnNode = insnNode.getNext();
828            }
829            return result;
830        }
831    
832        @NotNull
833        private static AbstractInsnNode getInstructionToInsertFinallyBefore(@NotNull AbstractInsnNode nonLocalReturnOrJump, boolean isLocal)  {
834            return isLocal ? nonLocalReturnOrJump : nonLocalReturnOrJump.getPrevious();
835        }
836    
837        //Place to insert finally blocks from try blocks that wraps inline fun call
838        public static class PointForExternalFinallyBlocks {
839    
840            final AbstractInsnNode beforeIns;
841    
842            final Type returnType;
843    
844            final LabelNode finallyIntervalEnd;
845    
846            public PointForExternalFinallyBlocks(
847                    @NotNull AbstractInsnNode beforeIns,
848                    @NotNull Type returnType,
849                    @NotNull LabelNode finallyIntervalEnd
850            ) {
851                this.beforeIns = beforeIns;
852                this.returnType = returnType;
853                this.finallyIntervalEnd = finallyIntervalEnd;
854            }
855    
856        }
857    
858    }