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.intellij.openapi.vfs.VirtualFile;
020    import com.intellij.psi.PsiElement;
021    import com.intellij.psi.PsiFile;
022    import com.intellij.util.ArrayUtil;
023    import kotlin.jvm.functions.Function0;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.kotlin.backend.common.CodegenUtil;
027    import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment;
028    import org.jetbrains.kotlin.codegen.*;
029    import org.jetbrains.kotlin.codegen.context.*;
030    import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt;
031    import org.jetbrains.kotlin.codegen.state.GenerationState;
032    import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
033    import org.jetbrains.kotlin.descriptors.*;
034    import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache;
035    import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents;
036    import org.jetbrains.kotlin.modules.TargetId;
037    import org.jetbrains.kotlin.name.ClassId;
038    import org.jetbrains.kotlin.name.Name;
039    import org.jetbrains.kotlin.psi.*;
040    import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
041    import org.jetbrains.kotlin.resolve.DescriptorUtils;
042    import org.jetbrains.kotlin.resolve.FunctionImportedFromObject;
043    import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt;
044    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
045    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
046    import org.jetbrains.kotlin.resolve.inline.InlineUtil;
047    import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
048    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
049    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
050    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
051    import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor;
052    import org.jetbrains.kotlin.types.expressions.LabelResolver;
053    import org.jetbrains.org.objectweb.asm.Label;
054    import org.jetbrains.org.objectweb.asm.MethodVisitor;
055    import org.jetbrains.org.objectweb.asm.Opcodes;
056    import org.jetbrains.org.objectweb.asm.Type;
057    import org.jetbrains.org.objectweb.asm.commons.Method;
058    import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
059    import org.jetbrains.org.objectweb.asm.tree.InsnList;
060    import org.jetbrains.org.objectweb.asm.tree.LabelNode;
061    import org.jetbrains.org.objectweb.asm.tree.MethodNode;
062    
063    import java.io.IOException;
064    import java.util.*;
065    
066    import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
067    import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
068    import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*;
069    import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral;
070    
071    public class InlineCodegen extends CallGenerator {
072        private final GenerationState state;
073        private final KotlinTypeMapper typeMapper;
074    
075        private final FunctionDescriptor functionDescriptor;
076        private final JvmMethodSignature jvmSignature;
077        private final KtElement callElement;
078        private final MethodContext context;
079        private final ExpressionCodegen codegen;
080    
081        private final boolean asFunctionInline;
082        private final int initialFrameSize;
083        private final boolean isSameModule;
084    
085        private final ParametersBuilder invocationParamBuilder = ParametersBuilder.newBuilder();
086        private final Map<Integer, LambdaInfo> expressionMap = new HashMap<Integer, LambdaInfo>();
087    
088        private final ReifiedTypeInliner reifiedTypeInliner;
089    
090        @Nullable
091        private final TypeParameterMappings typeParameterMappings;
092    
093        private LambdaInfo activeLambda;
094    
095        private final SourceMapper sourceMapper;
096    
097        public InlineCodegen(
098                @NotNull ExpressionCodegen codegen,
099                @NotNull GenerationState state,
100                @NotNull FunctionDescriptor function,
101                @NotNull KtElement callElement,
102                @Nullable TypeParameterMappings typeParameterMappings
103        ) {
104            assert InlineUtil.isInline(function) || InlineUtil.isArrayConstructorWithLambda(function) :
105                    "InlineCodegen can inline only inline functions and array constructors: " + function;
106            this.state = state;
107            this.typeMapper = state.getTypeMapper();
108            this.codegen = codegen;
109            this.callElement = callElement;
110            this.functionDescriptor =
111                    InlineUtil.isArrayConstructorWithLambda(function)
112                    ? FictitiousArrayConstructor.create((ConstructorDescriptor) function)
113                    : function.getOriginal();
114            this.typeParameterMappings = typeParameterMappings;
115    
116            reifiedTypeInliner = new ReifiedTypeInliner(typeParameterMappings);
117    
118            initialFrameSize = codegen.getFrameMap().getCurrentSize();
119    
120            PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
121            context = (MethodContext) getContext(functionDescriptor, state, element != null ? (KtFile) element.getContainingFile() : null);
122            jvmSignature = typeMapper.mapSignatureWithGeneric(functionDescriptor, context.getContextKind());
123    
124            // TODO: implement AS_FUNCTION inline strategy
125            this.asFunctionInline = false;
126    
127            isSameModule = JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), state.getOutDirectory());
128    
129            sourceMapper = codegen.getParentCodegen().getOrCreateSourceMapper();
130    
131            if (!(functionDescriptor instanceof FictitiousArrayConstructor)) {
132                reportIncrementalInfo(functionDescriptor, codegen.getContext().getFunctionDescriptor().getOriginal(), jvmSignature, state);
133            }
134        }
135    
136        @Override
137        public void genCallInner(
138                @NotNull Callable callableMethod,
139                @Nullable ResolvedCall<?> resolvedCall,
140                boolean callDefault,
141                @NotNull ExpressionCodegen codegen
142        ) {
143            if (!state.getInlineCycleReporter().enterIntoInlining(resolvedCall)) {
144                generateStub(resolvedCall, codegen);
145                return;
146            }
147    
148            SMAPAndMethodNode nodeAndSmap = null;
149            try {
150                nodeAndSmap = createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault);
151                endCall(inlineCall(nodeAndSmap));
152            }
153            catch (CompilationException e) {
154                throw e;
155            }
156            catch (InlineException e) {
157                throw throwCompilationException(nodeAndSmap, e, false);
158            }
159            catch (Exception e) {
160                throw throwCompilationException(nodeAndSmap, e, true);
161            }
162            finally {
163                state.getInlineCycleReporter().exitFromInliningOf(resolvedCall);
164            }
165        }
166    
167        @NotNull
168        private CompilationException throwCompilationException(
169                @Nullable SMAPAndMethodNode nodeAndSmap, @NotNull Exception e, boolean generateNodeText
170        ) {
171            CallableMemberDescriptor contextDescriptor = codegen.getContext().getContextDescriptor();
172            PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(contextDescriptor);
173            MethodNode node = nodeAndSmap != null ? nodeAndSmap.getNode() : null;
174            throw new CompilationException(
175                    "Couldn't inline method call '" + functionDescriptor.getName() + "' into\n" +
176                    contextDescriptor + "\n" +
177                    (element != null ? element.getText() : "<no source>") +
178                    (generateNodeText ? ("\nCause: " + InlineCodegenUtil.getNodeText(node)) : ""),
179                    e, callElement
180            );
181        }
182    
183        private void generateStub(@Nullable ResolvedCall<?> resolvedCall, @NotNull ExpressionCodegen codegen) {
184            leaveTemps();
185            assert resolvedCall != null;
186            String message = "Call is part of inline cycle: " + resolvedCall.getCall().getCallElement().getText();
187            AsmUtil.genThrow(codegen.v, "java/lang/UnsupportedOperationException", message);
188        }
189    
190        private void endCall(@NotNull InlineResult result) {
191            leaveTemps();
192    
193            codegen.propagateChildReifiedTypeParametersUsages(result.getReifiedTypeParametersUsages());
194    
195            state.getFactory().removeClasses(result.getClassesToRemove());
196    
197            codegen.markLineNumberAfterInlineIfNeeded();
198        }
199    
200        @NotNull
201        static SMAPAndMethodNode createMethodNode(
202                @NotNull final FunctionDescriptor functionDescriptor,
203                @NotNull JvmMethodSignature jvmSignature,
204                @NotNull ExpressionCodegen codegen,
205                @NotNull CodegenContext context,
206                boolean callDefault
207        ) {
208            final GenerationState state = codegen.getState();
209            final Method asmMethod =
210                    callDefault
211                    ? state.getTypeMapper().mapDefaultMethod(functionDescriptor, context.getContextKind())
212                    : jvmSignature.getAsmMethod();
213    
214            MethodId methodId = new MethodId(DescriptorUtils.getFqNameSafe(functionDescriptor.getContainingDeclaration()), asmMethod);
215            final FunctionDescriptor directMember = getCallableFromObject(functionDescriptor);
216            if (!isBuiltInArrayIntrinsic(directMember) && !(directMember instanceof DeserializedSimpleFunctionDescriptor)) {
217                return doCreateMethodNodeFromSource(directMember, jvmSignature, codegen, context, callDefault, state, asmMethod);
218            }
219    
220            SMAPAndMethodNode resultInCache = InlineCacheKt.getOrPut(
221                    state.getInlineCache().getMethodNodeById(), methodId, new Function0<SMAPAndMethodNode>() {
222                        @Override
223                        public SMAPAndMethodNode invoke() {
224                            SMAPAndMethodNode result = doCreateMethodNodeFromCompiled(directMember, state, asmMethod);
225                            if (result == null) {
226                                throw new IllegalStateException("Couldn't obtain compiled function body for " + functionDescriptor);
227                            }
228                            return result;
229                        }
230                    }
231            );
232    
233            return resultInCache.copyWithNewNode(cloneMethodNode(resultInCache.getNode()));
234        }
235    
236        @NotNull
237        private static FunctionDescriptor getCallableFromObject(@NotNull FunctionDescriptor functionDescriptor) {
238            if (functionDescriptor instanceof FunctionImportedFromObject) {
239                return  ((FunctionImportedFromObject) functionDescriptor).getCallableFromObject();
240            }
241            return functionDescriptor;
242        }
243    
244        @NotNull
245        private static MethodNode cloneMethodNode(@NotNull MethodNode methodNode) {
246            methodNode.instructions.resetLabels();
247            MethodNode result = new MethodNode(
248                    API, methodNode.access, methodNode.name, methodNode.desc, methodNode.signature,
249                    ArrayUtil.toStringArray(methodNode.exceptions)
250            );
251            methodNode.accept(result);
252            return result;
253        }
254    
255        @Nullable
256        private static SMAPAndMethodNode doCreateMethodNodeFromCompiled(
257                @NotNull FunctionDescriptor functionDescriptor,
258                @NotNull final GenerationState state,
259                @NotNull Method asmMethod
260        ) {
261            if (isBuiltInArrayIntrinsic(functionDescriptor)) {
262                ClassId classId = IntrinsicArrayConstructorsKt.getClassId();
263                byte[] bytes = InlineCacheKt.getOrPut(state.getInlineCache().getClassBytes(), classId, new Function0<byte[]>() {
264                    @Override
265                    public byte[] invoke() {
266                        return IntrinsicArrayConstructorsKt.getBytecode();
267                    }
268                });
269    
270                return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), classId);
271            }
272    
273            assert functionDescriptor instanceof DeserializedSimpleFunctionDescriptor : "Not a deserialized function: " + functionDescriptor;
274    
275            KotlinTypeMapper.ContainingClassesInfo containingClasses =
276                    KotlinTypeMapper.getContainingClassesForDeserializedCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor);
277    
278            final ClassId containerId = containingClasses.getImplClassId();
279    
280            byte[] bytes = InlineCacheKt.getOrPut(state.getInlineCache().getClassBytes(), containerId, new Function0<byte[]>() {
281                @Override
282                public byte[] invoke() {
283                    VirtualFile file = InlineCodegenUtil.findVirtualFile(state, containerId);
284                    if (file == null) {
285                        throw new IllegalStateException("Couldn't find declaration file for " + containerId);
286                    }
287                    try {
288                        return file.contentsToByteArray();
289                    }
290                    catch (IOException e) {
291                        throw new RuntimeException(e);
292                    }
293                }
294            });
295    
296            return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), containerId);
297        }
298    
299        @NotNull
300        private static SMAPAndMethodNode doCreateMethodNodeFromSource(
301                @NotNull FunctionDescriptor functionDescriptor,
302                @NotNull JvmMethodSignature jvmSignature,
303                @NotNull ExpressionCodegen codegen,
304                @NotNull CodegenContext context,
305                boolean callDefault,
306                @NotNull GenerationState state,
307                @NotNull Method asmMethod
308        ) {
309            PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
310    
311            if (!(element instanceof KtNamedFunction)) {
312                throw new IllegalStateException("Couldn't find declaration for function " + functionDescriptor);
313            }
314            KtNamedFunction inliningFunction = (KtNamedFunction) element;
315    
316            MethodNode node = new MethodNode(
317                    InlineCodegenUtil.API,
318                    getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
319                    asmMethod.getName(),
320                    asmMethod.getDescriptor(),
321                    null, null
322            );
323    
324            //for maxLocals calculation
325            MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
326            CodegenContext parentContext = context.getParentContext();
327            assert parentContext != null : "Context has no parent: " + context;
328            MethodContext methodContext = parentContext.intoFunction(functionDescriptor);
329    
330            SMAP smap;
331            if (callDefault) {
332                Type implementationOwner = state.getTypeMapper().mapImplementationOwner(functionDescriptor);
333                FakeMemberCodegen parentCodegen = new FakeMemberCodegen(
334                        codegen.getParentCodegen(), inliningFunction, (FieldOwnerContext) methodContext.getParentContext(),
335                        implementationOwner.getInternalName()
336                );
337                FunctionCodegen.generateDefaultImplBody(
338                        methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
339                        inliningFunction, parentCodegen, asmMethod
340                );
341                smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.getOrCreateSourceMapper().getResultMappings());
342            }
343            else {
344                smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, inliningFunction, jvmSignature, codegen,
345                                          null);
346            }
347            maxCalcAdapter.visitMaxs(-1, -1);
348            maxCalcAdapter.visitEnd();
349    
350            return new SMAPAndMethodNode(node, smap);
351        }
352    
353        private static boolean isBuiltInArrayIntrinsic(@NotNull FunctionDescriptor functionDescriptor) {
354            if (functionDescriptor instanceof FictitiousArrayConstructor) return true;
355            String name = functionDescriptor.getName().asString();
356            return (name.equals("arrayOf") || name.equals("emptyArray")) &&
357                   functionDescriptor.getContainingDeclaration() instanceof BuiltInsPackageFragment;
358        }
359    
360        @NotNull
361        private InlineResult inlineCall(@NotNull SMAPAndMethodNode nodeAndSmap) {
362            DefaultSourceMapper defaultSourceMapper = codegen.getParentCodegen().getOrCreateSourceMapper();
363            defaultSourceMapper.setCallSiteMarker(new CallSiteMarker(codegen.getLastLineNumber()));
364            MethodNode node = nodeAndSmap.getNode();
365            ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node);
366            generateClosuresBodies();
367    
368            //through generation captured parameters will be added to invocationParamBuilder
369            putClosureParametersOnStack();
370    
371            addInlineMarker(codegen.v, true);
372    
373            Parameters parameters = invocationParamBuilder.buildParameters();
374    
375            InliningContext info = new RootInliningContext(
376                    expressionMap, state, codegen.getInlineNameGenerator().subGenerator(jvmSignature.getAsmMethod().getName()),
377                    callElement, getInlineCallSiteInfo(), reifiedTypeInliner, typeParameterMappings
378            );
379    
380            MethodInliner inliner = new MethodInliner(
381                    node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule,
382                    "Method inlining " + callElement.getText(),
383                    createNestedSourceMapper(nodeAndSmap, sourceMapper), info.getCallSiteInfo(),
384                    AnnotationUtilKt.hasInlineOnlyAnnotation(functionDescriptor) ? new InlineOnlySmapSkipper(codegen) : null
385            ); //with captured
386    
387            LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize);
388    
389            MethodNode adapter = InlineCodegenUtil.createEmptyMethodNode();
390            //hack to keep linenumber info, otherwise jdi will skip begin of linenumber chain
391            adapter.visitInsn(Opcodes.NOP);
392    
393            InlineResult result = inliner.doInline(adapter, remapper, true, LabelOwner.SKIP_ALL);
394            result.getReifiedTypeParametersUsages().mergeAll(reificationResult);
395    
396            CallableMemberDescriptor descriptor = codegen.getContext().getContextDescriptor();
397            final Set<String> labels = getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor);
398            LabelOwner labelOwner = new LabelOwner() {
399                @Override
400                public boolean isMyLabel(@NotNull String name) {
401                    return labels.contains(name);
402                }
403            };
404    
405            List<MethodInliner.PointForExternalFinallyBlocks> infos = MethodInliner.processReturns(adapter, labelOwner, true, null);
406            generateAndInsertFinallyBlocks(
407                    adapter, infos, ((StackValue.Local) remapper.remap(parameters.getArgsSizeOnStack() + 1).value).index
408            );
409            removeStaticInitializationTrigger(adapter);
410            removeFinallyMarkers(adapter);
411    
412            adapter.accept(new MethodBodyVisitor(codegen.v));
413    
414            addInlineMarker(codegen.v, false);
415    
416            defaultSourceMapper.setCallSiteMarker(null);
417    
418            return result;
419        }
420    
421        private static void removeStaticInitializationTrigger(@NotNull MethodNode methodNode) {
422            InsnList insnList = methodNode.instructions;
423            AbstractInsnNode insn = insnList.getFirst();
424            while (insn != null) {
425                if (MultifileClassPartCodegen.isStaticInitTrigger(insn)) {
426                    AbstractInsnNode clinitTriggerCall = insn;
427                    insn = insn.getNext();
428                    insnList.remove(clinitTriggerCall);
429                }
430                else {
431                    insn = insn.getNext();
432                }
433            }
434        }
435    
436        @NotNull
437        private InlineCallSiteInfo getInlineCallSiteInfo() {
438            MethodContext context = codegen.getContext();
439            MemberCodegen<?> parentCodegen = codegen.getParentCodegen();
440            while (context instanceof InlineLambdaContext) {
441                CodegenContext closureContext = context.getParentContext();
442                assert closureContext instanceof ClosureContext : "Parent context of inline lambda should be closure context";
443                assert closureContext.getParentContext() instanceof MethodContext : "Closure context should appear in method context";
444                context = (MethodContext) closureContext.getParentContext();
445                assert parentCodegen instanceof FakeMemberCodegen : "Parent codegen of inlined lambda should be FakeMemberCodegen";
446                parentCodegen = ((FakeMemberCodegen) parentCodegen).delegate;
447            }
448    
449            JvmMethodSignature signature = typeMapper.mapSignatureSkipGeneric(context.getFunctionDescriptor(), context.getContextKind());
450            return new InlineCallSiteInfo(
451                    parentCodegen.getClassName(), signature.getAsmMethod().getName(), signature.getAsmMethod().getDescriptor()
452            );
453        }
454    
455        private void generateClosuresBodies() {
456            for (LambdaInfo info : expressionMap.values()) {
457                info.setNode(generateLambdaBody(info));
458            }
459        }
460    
461        @NotNull
462        private SMAPAndMethodNode generateLambdaBody(@NotNull LambdaInfo info) {
463            KtExpression declaration = info.getFunctionWithBodyOrCallableReference();
464            FunctionDescriptor descriptor = info.getFunctionDescriptor();
465    
466            ClassContext closureContext = info.isPropertyReference()
467                                            ? codegen.getContext().intoAnonymousClass(info.getClassDescriptor(), codegen, OwnerKind.IMPLEMENTATION)
468                                            : codegen.getContext().intoClosure(descriptor, codegen, typeMapper);
469            MethodContext context = closureContext.intoInlinedLambda(descriptor, info.isCrossInline, info.isPropertyReference());
470    
471            JvmMethodSignature jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(descriptor);
472            Method asmMethod = jvmMethodSignature.getAsmMethod();
473            MethodNode methodNode = new MethodNode(
474                    InlineCodegenUtil.API, getMethodAsmFlags(descriptor, context.getContextKind()),
475                    asmMethod.getName(), asmMethod.getDescriptor(), null, null
476            );
477    
478            MethodVisitor adapter = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode);
479    
480            SMAP smap = generateMethodBody(adapter, descriptor, context, declaration, jvmMethodSignature, codegen, info);
481            adapter.visitMaxs(-1, -1);
482            return new SMAPAndMethodNode(methodNode, smap);
483        }
484    
485        @NotNull
486        private static SMAP generateMethodBody(
487                @NotNull MethodVisitor adapter,
488                @NotNull FunctionDescriptor descriptor,
489                @NotNull MethodContext context,
490                @NotNull KtExpression expression,
491                @NotNull JvmMethodSignature jvmMethodSignature,
492                @NotNull ExpressionCodegen codegen,
493                @Nullable LambdaInfo lambdaInfo
494        ) {
495            boolean isLambda = lambdaInfo != null;
496            GenerationState state = codegen.getState();
497    
498            // Wrapping for preventing marking actual parent codegen as containing reified markers
499            FakeMemberCodegen parentCodegen = new FakeMemberCodegen(
500                    codegen.getParentCodegen(), expression, (FieldOwnerContext) context.getParentContext(),
501                    isLambda ? codegen.getParentCodegen().getClassName()
502                             : state.getTypeMapper().mapImplementationOwner(descriptor).getInternalName()
503            );
504    
505            FunctionGenerationStrategy strategy;
506            if (expression instanceof KtCallableReferenceExpression) {
507                KtCallableReferenceExpression callableReferenceExpression = (KtCallableReferenceExpression) expression;
508    
509                if (isLambda && lambdaInfo.isPropertyReference()) {
510                    Type asmType = state.getTypeMapper().mapClass(lambdaInfo.getClassDescriptor());
511                    PropertyReferenceInfo info = lambdaInfo.getPropertyReferenceInfo();
512                    strategy = new PropertyReferenceCodegen.PropertyReferenceGenerationStrategy(
513                            true, info.getGetFunction(), info.getTarget(), asmType, lambdaInfo.expression, state
514                    );
515                }
516                else {
517                    strategy = new FunctionReferenceGenerationStrategy(
518                            state,
519                            descriptor,
520                            CallUtilKt
521                                    .getResolvedCallWithAssert(callableReferenceExpression.getCallableReference(), codegen.getBindingContext())
522                    );
523                }
524            }
525            else {
526                strategy = new FunctionGenerationStrategy.FunctionDefault(state, descriptor, (KtDeclarationWithBody) expression);
527            }
528    
529            FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen);
530    
531            if (isLambda) {
532                codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.getReifiedTypeParametersUsages());
533            }
534    
535            return createSMAPWithDefaultMapping(expression, parentCodegen.getOrCreateSourceMapper().getResultMappings());
536        }
537    
538        private static SMAP createSMAPWithDefaultMapping(
539                @NotNull KtExpression declaration,
540                @NotNull List<FileMapping> mappings
541        ) {
542            PsiFile containingFile = declaration.getContainingFile();
543            Integer lineNumbers = CodegenUtil.getLineNumberForElement(containingFile, true);
544            assert lineNumbers != null : "Couldn't extract line count in " + containingFile;
545    
546            return new SMAP(mappings);
547        }
548    
549        private static class FakeMemberCodegen extends MemberCodegen {
550            private final MemberCodegen delegate;
551            private final String className;
552    
553            @SuppressWarnings("unchecked")
554            public FakeMemberCodegen(
555                    @NotNull MemberCodegen wrapped,
556                    @NotNull KtElement declaration,
557                    @NotNull FieldOwnerContext codegenContext,
558                    @NotNull String className
559            ) {
560                super(wrapped, declaration, codegenContext);
561                this.delegate = wrapped;
562                this.className = className;
563            }
564    
565            @Override
566            protected void generateDeclaration() {
567                throw new IllegalStateException();
568            }
569    
570            @Override
571            protected void generateBody() {
572                throw new IllegalStateException();
573            }
574    
575            @Override
576            protected void generateKotlinMetadataAnnotation() {
577                throw new IllegalStateException();
578            }
579    
580            @NotNull
581            @Override
582            public NameGenerator getInlineNameGenerator() {
583                return delegate.getInlineNameGenerator();
584            }
585    
586            @NotNull
587            @Override
588            //TODO: obtain name from context
589            public String getClassName() {
590                return className;
591            }
592        }
593    
594        @Override
595        public void afterParameterPut(@NotNull Type type, @Nullable StackValue stackValue, int parameterIndex) {
596            putArgumentOrCapturedToLocalVal(type, stackValue, -1, parameterIndex);
597        }
598    
599        private void putArgumentOrCapturedToLocalVal(
600                @NotNull Type type,
601                @Nullable StackValue stackValue,
602                int capturedParamIndex,
603                int parameterIndex
604        ) {
605            if (!asFunctionInline && Type.VOID_TYPE != type) {
606                //TODO remap only inlinable closure => otherwise we could get a lot of problem
607                boolean couldBeRemapped = !shouldPutValue(type, stackValue);
608                StackValue remappedIndex = couldBeRemapped ? stackValue : null;
609    
610                ParameterInfo info;
611                if (capturedParamIndex >= 0) {
612                    CapturedParamDesc capturedParamInfoInLambda = activeLambda.getCapturedVars().get(capturedParamIndex);
613                    info = invocationParamBuilder.addCapturedParam(capturedParamInfoInLambda, capturedParamInfoInLambda.getFieldName(), false);
614                    info.setRemapValue(remappedIndex);
615                }
616                else {
617                    info = invocationParamBuilder.addNextValueParameter(type, false, remappedIndex, parameterIndex);
618                }
619    
620                recordParameterValueInLocalVal(info);
621            }
622        }
623    
624        /*descriptor is null for captured vars*/
625        private static boolean shouldPutValue(@NotNull Type type, @Nullable StackValue stackValue) {
626            if (stackValue == null) {
627                //default or vararg
628                return true;
629            }
630    
631            //remap only inline functions (and maybe non primitives)
632            //TODO - clean asserion and remapping logic
633            if (isPrimitive(type) != isPrimitive(stackValue.type)) {
634                //don't remap boxing/unboxing primitives - lost identity and perfomance
635                return true;
636            }
637    
638            if (stackValue instanceof StackValue.Local) {
639                return false;
640            }
641    
642            StackValue field = stackValue;
643            if (stackValue instanceof StackValue.FieldForSharedVar) {
644                field = ((StackValue.FieldForSharedVar) stackValue).receiver;
645            }
646    
647            //check that value corresponds to captured inlining parameter
648            if (field instanceof StackValue.Field) {
649                DeclarationDescriptor varDescriptor = ((StackValue.Field) field).descriptor;
650                //check that variable is inline function parameter
651                return !(varDescriptor instanceof ParameterDescriptor &&
652                         InlineUtil.isInlineLambdaParameter((ParameterDescriptor) varDescriptor) &&
653                         InlineUtil.isInline(varDescriptor.getContainingDeclaration()));
654            }
655    
656            return true;
657        }
658    
659        private void recordParameterValueInLocalVal(@NotNull ParameterInfo... infos) {
660            int[] index = new int[infos.length];
661            for (int i = 0; i < infos.length; i++) {
662                ParameterInfo info = infos[i];
663                if (!info.isSkippedOrRemapped()) {
664                    index[i] = codegen.getFrameMap().enterTemp(info.getType());
665                }
666                else {
667                    index[i] = -1;
668                }
669            }
670    
671            for (int i = infos.length - 1; i >= 0; i--) {
672                ParameterInfo info = infos[i];
673                if (!info.isSkippedOrRemapped()) {
674                    Type type = info.type;
675                    StackValue.local(index[i], type).store(StackValue.onStack(type), codegen.v);
676                }
677            }
678        }
679    
680        @Override
681        public void putHiddenParams() {
682            if ((getMethodAsmFlags(functionDescriptor, context.getContextKind()) & Opcodes.ACC_STATIC) == 0) {
683                invocationParamBuilder.addNextParameter(AsmTypes.OBJECT_TYPE, false);
684            }
685    
686            for (JvmMethodParameterSignature param : jvmSignature.getValueParameters()) {
687                if (param.getKind() == JvmMethodParameterKind.VALUE) {
688                    break;
689                }
690                invocationParamBuilder.addNextParameter(param.getAsmType(), false);
691            }
692    
693            invocationParamBuilder.markValueParametersStart();
694            List<ParameterInfo> hiddenParameters = invocationParamBuilder.buildParameters().getReal();
695            recordParameterValueInLocalVal(hiddenParameters.toArray(new ParameterInfo[hiddenParameters.size()]));
696        }
697    
698        private void leaveTemps() {
699            List<ParameterInfo> infos = invocationParamBuilder.listAllParams();
700            for (ListIterator<? extends ParameterInfo> iterator = infos.listIterator(infos.size()); iterator.hasPrevious(); ) {
701                ParameterInfo param = iterator.previous();
702                if (!param.isSkippedOrRemapped()) {
703                    codegen.getFrameMap().leaveTemp(param.type);
704                }
705            }
706        }
707    
708        /*lambda or callable reference*/
709        private static boolean isInliningParameter(@NotNull KtExpression expression, @NotNull ValueParameterDescriptor valueParameterDescriptor) {
710            //TODO deparenthisise typed
711            KtExpression deparenthesized = KtPsiUtil.deparenthesize(expression);
712    
713            return InlineUtil.isInlineLambdaParameter(valueParameterDescriptor) &&
714                   isInlinableParameterExpression(deparenthesized);
715        }
716    
717        private static boolean isInlinableParameterExpression(@Nullable KtExpression deparenthesized) {
718            return deparenthesized instanceof KtLambdaExpression ||
719                   deparenthesized instanceof KtNamedFunction ||
720                   deparenthesized instanceof KtCallableReferenceExpression;
721        }
722    
723        private void rememberClosure(@NotNull KtExpression expression, @NotNull Type type, @NotNull ValueParameterDescriptor parameter) {
724            KtExpression lambda = KtPsiUtil.deparenthesize(expression);
725            assert isInlinableParameterExpression(lambda) : "Couldn't find inline expression in " + expression.getText();
726    
727            LambdaInfo info =
728                    new LambdaInfo(lambda, typeMapper, parameter.isCrossinline());
729    
730            ParameterInfo closureInfo = invocationParamBuilder.addNextValueParameter(type, true, null, parameter.getIndex());
731            closureInfo.setLambda(info);
732            expressionMap.put(closureInfo.getIndex(), info);
733        }
734    
735        @NotNull
736        public static Set<String> getDeclarationLabels(@Nullable PsiElement lambdaOrFun, @NotNull DeclarationDescriptor descriptor) {
737            Set<String> result = new HashSet<String>();
738    
739            if (lambdaOrFun != null) {
740                Name label = LabelResolver.INSTANCE.getLabelNameIfAny(lambdaOrFun);
741                if (label != null) {
742                    result.add(label.asString());
743                }
744            }
745    
746            if (!isFunctionLiteral(descriptor)) {
747                if (!descriptor.getName().isSpecial()) {
748                    result.add(descriptor.getName().asString());
749                }
750                result.add(InlineCodegenUtil.FIRST_FUN_LABEL);
751            }
752            return result;
753        }
754    
755        private void putClosureParametersOnStack() {
756            for (LambdaInfo next : expressionMap.values()) {
757                activeLambda = next;
758                codegen.pushClosureOnStack(next.getClassDescriptor(), true, this);
759            }
760            activeLambda = null;
761        }
762    
763        @NotNull
764        public static CodegenContext getContext(
765                @NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state, @Nullable KtFile sourceFile
766        ) {
767            if (descriptor instanceof PackageFragmentDescriptor) {
768                return new PackageContext((PackageFragmentDescriptor) descriptor, state.getRootContext(), null, sourceFile);
769            }
770    
771            DeclarationDescriptor container = descriptor.getContainingDeclaration();
772            assert container != null : "No container for descriptor: " + descriptor;
773            CodegenContext parent = getContext(container, state, sourceFile);
774    
775            if (descriptor instanceof ScriptDescriptor) {
776                List<ScriptDescriptor> earlierScripts = state.getReplSpecific().getEarlierScriptsForReplInterpreter();
777                return parent.intoScript(
778                        (ScriptDescriptor) descriptor,
779                        earlierScripts == null ? Collections.emptyList() : earlierScripts,
780                        (ClassDescriptor) descriptor, state.getTypeMapper()
781                );
782            }
783            else if (descriptor instanceof ClassDescriptor) {
784                OwnerKind kind = DescriptorUtils.isInterface(descriptor) ? OwnerKind.DEFAULT_IMPLS : OwnerKind.IMPLEMENTATION;
785                return parent.intoClass((ClassDescriptor) descriptor, kind, state);
786            }
787            else if (descriptor instanceof FunctionDescriptor) {
788                return parent.intoFunction((FunctionDescriptor) descriptor);
789            }
790    
791            throw new IllegalStateException("Couldn't build context for " + descriptor);
792        }
793    
794        @Override
795        public void genValueAndPut(
796                @NotNull ValueParameterDescriptor valueParameterDescriptor,
797                @NotNull KtExpression argumentExpression,
798                @NotNull Type parameterType,
799                int parameterIndex
800        ) {
801            if (isInliningParameter(argumentExpression, valueParameterDescriptor)) {
802                rememberClosure(argumentExpression, parameterType, valueParameterDescriptor);
803            }
804            else {
805                StackValue value = codegen.gen(argumentExpression);
806                putValueIfNeeded(parameterType, value, valueParameterDescriptor.getIndex());
807            }
808        }
809    
810        @Override
811        public void putValueIfNeeded(@NotNull Type parameterType, @NotNull StackValue value) {
812            putValueIfNeeded(parameterType, value, -1);
813        }
814    
815        private void putValueIfNeeded(@NotNull Type parameterType, @NotNull StackValue value, int index) {
816            if (shouldPutValue(parameterType, value)) {
817                value.put(parameterType, codegen.v);
818            }
819            afterParameterPut(parameterType, value, index);
820        }
821    
822        @Override
823        public void putCapturedValueOnStack(@NotNull StackValue stackValue, @NotNull Type valueType, int paramIndex) {
824            if (shouldPutValue(stackValue.type, stackValue)) {
825                stackValue.put(stackValue.type, codegen.v);
826            }
827            putArgumentOrCapturedToLocalVal(stackValue.type, stackValue, paramIndex, paramIndex);
828        }
829    
830        private void generateAndInsertFinallyBlocks(
831                @NotNull MethodNode intoNode,
832                @NotNull List<MethodInliner.PointForExternalFinallyBlocks> insertPoints,
833                int offsetForFinallyLocalVar
834        ) {
835            if (!codegen.hasFinallyBlocks()) return;
836    
837            Map<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks> extensionPoints =
838                    new HashMap<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks>();
839            for (MethodInliner.PointForExternalFinallyBlocks insertPoint : insertPoints) {
840                extensionPoints.put(insertPoint.beforeIns, insertPoint);
841            }
842    
843            DefaultProcessor processor = new DefaultProcessor(intoNode, offsetForFinallyLocalVar);
844    
845            int curFinallyDepth = 0;
846            AbstractInsnNode curInstr = intoNode.instructions.getFirst();
847            while (curInstr != null) {
848                processor.processInstruction(curInstr, true);
849                if (InlineCodegenUtil.isFinallyStart(curInstr)) {
850                    //TODO depth index calc could be more precise
851                    curFinallyDepth = getConstant(curInstr.getPrevious());
852                }
853    
854                MethodInliner.PointForExternalFinallyBlocks extension = extensionPoints.get(curInstr);
855                if (extension != null) {
856                    Label start = new Label();
857    
858                    MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode();
859                    finallyNode.visitLabel(start);
860    
861                    ExpressionCodegen finallyCodegen =
862                            new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(),
863                                                  codegen.getContext(), codegen.getState(), codegen.getParentCodegen());
864                    finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements(), curFinallyDepth);
865    
866                    FrameMap frameMap = finallyCodegen.getFrameMap();
867                    FrameMap.Mark mark = frameMap.mark();
868                    int marker = -1;
869                    Set<LocalVarNodeWrapper> intervals = processor.getLocalVarsMetaInfo().getCurrentIntervals();
870                    for (LocalVarNodeWrapper interval : intervals) {
871                        marker = Math.max(interval.getNode().index + 1, marker);
872                    }
873                    while (frameMap.getCurrentSize() < Math.max(processor.getNextFreeLocalIndex(), offsetForFinallyLocalVar + marker)) {
874                        frameMap.enterTemp(Type.INT_TYPE);
875                    }
876    
877                    finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType, extension.finallyIntervalEnd.getLabel());
878    
879                    //Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
880                    InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, curInstr);
881    
882                    SimpleInterval splitBy = new SimpleInterval((LabelNode) start.info, extension.finallyIntervalEnd);
883                    processor.getTryBlocksMetaInfo().splitCurrentIntervals(splitBy, true);
884    
885                    //processor.getLocalVarsMetaInfo().splitAndRemoveIntervalsFromCurrents(splitBy);
886    
887                    mark.dropTo();
888                }
889    
890                curInstr = curInstr.getNext();
891            }
892    
893            processor.substituteTryBlockNodes(intoNode);
894    
895            //processor.substituteLocalVarTable(intoNode);
896        }
897    
898        private void removeFinallyMarkers(@NotNull MethodNode intoNode) {
899            if (InlineCodegenUtil.isFinallyMarkerRequired(codegen.getContext())) return;
900    
901            InsnList instructions = intoNode.instructions;
902            AbstractInsnNode curInstr = instructions.getFirst();
903            while (curInstr != null) {
904                if (InlineCodegenUtil.isFinallyMarker(curInstr)) {
905                    AbstractInsnNode marker = curInstr;
906                    //just to assert
907                    getConstant(marker.getPrevious());
908                    curInstr = curInstr.getNext();
909                    instructions.remove(marker.getPrevious());
910                    instructions.remove(marker);
911                    continue;
912                }
913                curInstr = curInstr.getNext();
914            }
915        }
916    
917        @NotNull
918        public static SourceMapper createNestedSourceMapper(@NotNull SMAPAndMethodNode nodeAndSmap, @NotNull SourceMapper parent) {
919            return new NestedSourceMapper(parent, nodeAndSmap.getSortedRanges(), nodeAndSmap.getClassSMAP().getSourceInfo());
920        }
921    
922        static void reportIncrementalInfo(
923                @NotNull FunctionDescriptor sourceDescriptor,
924                @NotNull FunctionDescriptor targetDescriptor,
925                @NotNull JvmMethodSignature jvmSignature,
926                @NotNull GenerationState state
927        ) {
928            IncrementalCompilationComponents incrementalCompilationComponents = state.getIncrementalCompilationComponents();
929            TargetId targetId = state.getTargetId();
930    
931            if (incrementalCompilationComponents == null || targetId == null) return;
932    
933            IncrementalCache incrementalCache = incrementalCompilationComponents.getIncrementalCache(targetId);
934            String classFilePath = InlineCodegenUtilsKt.getClassFilePath(sourceDescriptor, state.getTypeMapper(), incrementalCache);
935            String sourceFilePath = InlineCodegenUtilsKt.getSourceFilePath(targetDescriptor);
936            Method method = jvmSignature.getAsmMethod();
937            incrementalCache.registerInline(classFilePath, method.getName() + method.getDescriptor(), sourceFilePath);
938        }
939    
940        @Override
941        public void reorderArgumentsIfNeeded(
942                @NotNull List<ArgumentAndDeclIndex> actualArgsWithDeclIndex, @NotNull List<? extends Type> valueParameterTypes
943        ) {
944    
945        }
946    }