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