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