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