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 }