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