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.calls.callUtil.CallUtilKt;
044 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
045 import org.jetbrains.kotlin.resolve.inline.InlineUtil;
046 import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
047 import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
048 import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
049 import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
050 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor;
051 import org.jetbrains.kotlin.types.expressions.LabelResolver;
052 import org.jetbrains.org.objectweb.asm.Label;
053 import org.jetbrains.org.objectweb.asm.MethodVisitor;
054 import org.jetbrains.org.objectweb.asm.Opcodes;
055 import org.jetbrains.org.objectweb.asm.Type;
056 import org.jetbrains.org.objectweb.asm.commons.Method;
057 import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
058 import org.jetbrains.org.objectweb.asm.tree.InsnList;
059 import org.jetbrains.org.objectweb.asm.tree.LabelNode;
060 import org.jetbrains.org.objectweb.asm.tree.MethodNode;
061
062 import java.io.IOException;
063 import java.util.*;
064
065 import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
066 import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
067 import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*;
068 import static org.jetbrains.kotlin.descriptors.annotations.AnnotationUtilKt.hasInlineOnlyAnnotation;
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 state.getTypeMapper().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 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 }