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 }