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.google.common.collect.Lists; 020 import com.intellij.util.ArrayUtil; 021 import org.jetbrains.annotations.NotNull; 022 import org.jetbrains.annotations.Nullable; 023 import org.jetbrains.kotlin.codegen.ClosureCodegen; 024 import org.jetbrains.kotlin.codegen.StackValue; 025 import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods; 026 import org.jetbrains.kotlin.codegen.optimization.MandatoryMethodTransformer; 027 import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; 028 import org.jetbrains.kotlin.utils.SmartSet; 029 import org.jetbrains.org.objectweb.asm.Label; 030 import org.jetbrains.org.objectweb.asm.MethodVisitor; 031 import org.jetbrains.org.objectweb.asm.Opcodes; 032 import org.jetbrains.org.objectweb.asm.Type; 033 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; 034 import org.jetbrains.org.objectweb.asm.commons.Method; 035 import org.jetbrains.org.objectweb.asm.commons.RemappingMethodAdapter; 036 import org.jetbrains.org.objectweb.asm.tree.*; 037 import org.jetbrains.org.objectweb.asm.tree.analysis.*; 038 039 import java.util.*; 040 041 import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*; 042 043 public class MethodInliner { 044 045 private final MethodNode node; 046 047 private final Parameters parameters; 048 049 private final InliningContext inliningContext; 050 051 private final FieldRemapper nodeRemapper; 052 053 private final boolean isSameModule; 054 055 private final String errorPrefix; 056 057 private final SourceMapper sourceMapper; 058 059 private final InlineCallSiteInfo inlineCallSiteInfo; 060 061 private final KotlinTypeMapper typeMapper; 062 063 private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>(); 064 065 //keeps order 066 private final List<TransformationInfo> transformations = new ArrayList<TransformationInfo>(); 067 //current state 068 private final Map<String, String> currentTypeMapping = new HashMap<String, String>(); 069 070 private final InlineResult result; 071 072 private int lambdasFinallyBlocks; 073 074 private final InlineOnlySmapSkipper inlineOnlySmapSkipper; 075 076 /* 077 * 078 * @param node 079 * @param parameters 080 * @param inliningContext 081 * @param lambdaType - in case on lambda 'invoke' inlining 082 */ 083 public MethodInliner( 084 @NotNull MethodNode node, 085 @NotNull Parameters parameters, 086 @NotNull InliningContext inliningContext, 087 @NotNull FieldRemapper nodeRemapper, 088 boolean isSameModule, 089 @NotNull String errorPrefix, 090 @NotNull SourceMapper sourceMapper, 091 @NotNull InlineCallSiteInfo inlineCallSiteInfo, 092 @Nullable InlineOnlySmapSkipper smapSkipper //non null only for root 093 ) { 094 this.node = node; 095 this.parameters = parameters; 096 this.inliningContext = inliningContext; 097 this.nodeRemapper = nodeRemapper; 098 this.isSameModule = isSameModule; 099 this.errorPrefix = errorPrefix; 100 this.sourceMapper = sourceMapper; 101 this.inlineCallSiteInfo = inlineCallSiteInfo; 102 this.typeMapper = inliningContext.state.getTypeMapper(); 103 this.result = InlineResult.create(); 104 this.inlineOnlySmapSkipper = smapSkipper; 105 } 106 107 public InlineResult doInline( 108 @NotNull MethodVisitor adapter, 109 @NotNull LocalVarRemapper remapper, 110 boolean remapReturn, 111 @NotNull LabelOwner labelOwner 112 ) { 113 return doInline(adapter, remapper, remapReturn, labelOwner, 0); 114 } 115 116 private InlineResult doInline( 117 @NotNull MethodVisitor adapter, 118 @NotNull LocalVarRemapper remapper, 119 boolean remapReturn, 120 @NotNull LabelOwner labelOwner, 121 int finallyDeepShift 122 ) { 123 //analyze body 124 MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node, finallyDeepShift); 125 126 //substitute returns with "goto end" instruction to keep non local returns in lambdas 127 Label end = new Label(); 128 transformedNode = doInline(transformedNode); 129 removeClosureAssertions(transformedNode); 130 InsnList instructions = transformedNode.instructions; 131 instructions.resetLabels(); 132 133 MethodNode resultNode = new MethodNode(InlineCodegenUtil.API, transformedNode.access, transformedNode.name, transformedNode.desc, 134 transformedNode.signature, ArrayUtil.toStringArray(transformedNode.exceptions)); 135 RemapVisitor visitor = new RemapVisitor(resultNode, remapper, nodeRemapper); 136 try { 137 transformedNode.accept(visitor); 138 } 139 catch (Exception e) { 140 throw wrapException(e, transformedNode, "couldn't inline method call"); 141 } 142 143 resultNode.visitLabel(end); 144 145 if (inliningContext.isRoot()) { 146 InternalFinallyBlockInliner.processInlineFunFinallyBlocks(resultNode, lambdasFinallyBlocks, ((StackValue.Local)remapper.remap(parameters.getArgsSizeOnStack() + 1).value).index); 147 } 148 149 processReturns(resultNode, labelOwner, remapReturn, end); 150 //flush transformed node to output 151 resultNode.accept(new MethodBodyVisitor(adapter)); 152 153 sourceMapper.endMapping(); 154 return result; 155 } 156 157 private MethodNode doInline(final MethodNode node) { 158 159 final Deque<InvokeCall> currentInvokes = new LinkedList<InvokeCall>(invokeCalls); 160 161 final MethodNode resultNode = new MethodNode(node.access, node.name, node.desc, node.signature, null); 162 163 final Iterator<TransformationInfo> iterator = transformations.iterator(); 164 165 final TypeRemapper remapper = TypeRemapper.createFrom(currentTypeMapping); 166 final RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter( 167 resultNode.access, 168 resultNode.desc, 169 resultNode, 170 new AsmTypeRemapper(remapper, inliningContext.getRoot().typeParameterMappings == null, result) 171 ); 172 173 final int markerShift = InlineCodegenUtil.calcMarkerShift(parameters, node); 174 InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.getArgsSizeOnStack(), sourceMapper) { 175 176 private TransformationInfo transformationInfo; 177 private void handleAnonymousObjectRegeneration() { 178 transformationInfo = iterator.next(); 179 180 if (transformationInfo.shouldRegenerate(isSameModule)) { 181 //TODO: need poping of type but what to do with local funs??? 182 String oldClassName = transformationInfo.getOldClassName(); 183 String newClassName = transformationInfo.getNewClassName(); 184 remapper.addMapping(oldClassName, newClassName); 185 186 InliningContext childInliningContext = inliningContext.subInlineWithClassRegeneration( 187 inliningContext.nameGenerator, 188 currentTypeMapping, 189 inlineCallSiteInfo 190 ); 191 ObjectTransformer transformer = transformationInfo.createTransformer( 192 childInliningContext, 193 isSameModule 194 ); 195 196 InlineResult transformResult = transformer.doTransform(nodeRemapper); 197 result.addAllClassesToRemove(transformResult); 198 result.addChangedType(oldClassName, newClassName); 199 200 if (inliningContext.isInliningLambda && transformationInfo.canRemoveAfterTransformation()) { 201 // this class is transformed and original not used so we should remove original one after inlining 202 result.addClassToRemove(oldClassName); 203 } 204 205 if (transformResult.getReifiedTypeParametersUsages().wereUsedReifiedParameters()) { 206 ReifiedTypeInliner.putNeedClassReificationMarker(mv); 207 result.getReifiedTypeParametersUsages().mergeAll(transformResult.getReifiedTypeParametersUsages()); 208 } 209 } 210 } 211 212 @Override 213 public void anew(@NotNull Type type) { 214 if (isAnonymousConstructorCall(type.getInternalName(), "<init>")) { 215 handleAnonymousObjectRegeneration(); 216 } 217 218 //in case of regenerated transformationInfo type would be remapped to new one via remappingMethodAdapter 219 super.anew(type); 220 } 221 222 @Override 223 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 224 if (/*INLINE_RUNTIME.equals(owner) &&*/ isInvokeOnLambda(owner, name)) { //TODO add method 225 assert !currentInvokes.isEmpty(); 226 InvokeCall invokeCall = currentInvokes.remove(); 227 LambdaInfo info = invokeCall.lambdaInfo; 228 229 if (info == null) { 230 //noninlinable lambda 231 super.visitMethodInsn(opcode, owner, name, desc, itf); 232 return; 233 } 234 235 int valueParamShift = Math.max(getNextLocalIndex(), markerShift);//NB: don't inline cause it changes 236 putStackValuesIntoLocals(info.getInvokeParamsWithoutCaptured(), valueParamShift, this, desc); 237 238 addInlineMarker(this, true); 239 Parameters lambdaParameters = info.addAllParameters(nodeRemapper); 240 241 InlinedLambdaRemapper newCapturedRemapper = 242 new InlinedLambdaRemapper(info.getLambdaClassType().getInternalName(), nodeRemapper, lambdaParameters); 243 244 setLambdaInlining(true); 245 SMAP lambdaSMAP = info.getNode().getClassSMAP(); 246 SourceMapper mapper = 247 inliningContext.classRegeneration && !inliningContext.isInliningLambda ? 248 new NestedSourceMapper(sourceMapper, lambdaSMAP.getIntervals(), lambdaSMAP.getSourceInfo()) 249 : new InlineLambdaSourceMapper(sourceMapper.getParent(), info.getNode()); 250 MethodInliner inliner = new MethodInliner(info.getNode().getNode(), lambdaParameters, 251 inliningContext.subInlineLambda(info), 252 newCapturedRemapper, true /*cause all calls in same module as lambda*/, 253 "Lambda inlining " + info.getLambdaClassType().getInternalName(), 254 mapper, inlineCallSiteInfo, null); 255 256 LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift); 257 InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info, invokeCall.finallyDepthShift);//TODO add skipped this and receiver 258 result.addAllClassesToRemove(lambdaResult); 259 260 //return value boxing/unboxing 261 Method bridge = 262 typeMapper.mapAsmMethod(ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor())); 263 Method delegate = typeMapper.mapAsmMethod(info.getFunctionDescriptor()); 264 StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this); 265 setLambdaInlining(false); 266 addInlineMarker(this, false); 267 mapper.endMapping(); 268 if (inlineOnlySmapSkipper != null) { 269 inlineOnlySmapSkipper.markCallSiteLineNumber(remappingMethodAdapter); 270 } 271 } 272 else if (isAnonymousConstructorCall(owner, name)) { //TODO add method 273 //TODO add proper message 274 assert transformationInfo instanceof AnonymousObjectTransformationInfo : 275 "<init> call doesn't correspond to object transformation info: " + owner + "." + name + ", info " + transformationInfo; 276 if (transformationInfo.shouldRegenerate(isSameModule)) { 277 //put additional captured parameters on stack 278 for (CapturedParamDesc capturedParamDesc : ((AnonymousObjectTransformationInfo) transformationInfo).getAllRecapturedParameters()) { 279 visitFieldInsn(Opcodes.GETSTATIC, capturedParamDesc.getContainingLambdaName(), 280 "$$$" + capturedParamDesc.getFieldName(), capturedParamDesc.getType().getDescriptor()); 281 } 282 super.visitMethodInsn(opcode, transformationInfo.getNewClassName(), name, 283 ((AnonymousObjectTransformationInfo) transformationInfo).getNewConstructorDescriptor(), itf); 284 285 //TODO: add new inner class also for other contexts 286 if (inliningContext.getParent() instanceof RegeneratedClassContext) { 287 inliningContext.getParent().typeRemapper.addAdditionalMappings(transformationInfo.getOldClassName(), transformationInfo.getNewClassName()); 288 } 289 290 transformationInfo = null; 291 } else { 292 super.visitMethodInsn(opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf); 293 } 294 } 295 else if (!inliningContext.isInliningLambda && ReifiedTypeInliner.isNeedClassReificationMarker(new MethodInsnNode(opcode, owner, name, desc, false))) { 296 //we shouldn't process here content of inlining lambda it should be reified at external level 297 } 298 else { 299 super.visitMethodInsn(opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf); 300 } 301 } 302 303 @Override 304 public void visitFieldInsn(int opcode, @NotNull String owner, @NotNull String name, @NotNull String desc) { 305 if (opcode == Opcodes.GETSTATIC && (isAnonymousSingletonLoad(owner, name) || isWhenMappingAccess(owner, name))) { 306 handleAnonymousObjectRegeneration(); 307 } 308 super.visitFieldInsn(opcode, owner, name, desc); 309 } 310 311 @Override 312 public void visitMaxs(int stack, int locals) { 313 lambdasFinallyBlocks = resultNode.tryCatchBlocks.size(); 314 super.visitMaxs(stack, locals); 315 } 316 317 }; 318 319 node.accept(lambdaInliner); 320 321 return resultNode; 322 } 323 324 @NotNull 325 public static CapturedParamInfo findCapturedField(FieldInsnNode node, FieldRemapper fieldRemapper) { 326 assert node.name.startsWith("$$$") : "Captured field template should start with $$$ prefix"; 327 FieldInsnNode fin = new FieldInsnNode(node.getOpcode(), node.owner, node.name.substring(3), node.desc); 328 CapturedParamInfo field = fieldRemapper.findField(fin); 329 if (field == null) { 330 throw new IllegalStateException("Couldn't find captured field " + node.owner + "." + node.name + " in " + fieldRemapper.getLambdaInternalName()); 331 } 332 return field; 333 } 334 335 @NotNull 336 public MethodNode prepareNode(@NotNull MethodNode node, int finallyDeepShift) { 337 final int capturedParamsSize = parameters.getCapturedArgsSizeOnStack(); 338 final int realParametersSize = parameters.getRealArgsSizeOnStack(); 339 Type[] types = Type.getArgumentTypes(node.desc); 340 Type returnType = Type.getReturnType(node.desc); 341 342 List<Type> capturedTypes = parameters.getCapturedTypes(); 343 Type[] allTypes = ArrayUtil.mergeArrays(types, capturedTypes.toArray(new Type[capturedTypes.size()])); 344 345 node.instructions.resetLabels(); 346 MethodNode transformedNode = new MethodNode(InlineCodegenUtil.API, node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null) { 347 348 private final boolean GENERATE_DEBUG_INFO = InlineCodegenUtil.GENERATE_SMAP && inlineOnlySmapSkipper == null; 349 350 private final boolean isInliningLambda = nodeRemapper.isInsideInliningLambda(); 351 352 private int getNewIndex(int var) { 353 return var + (var < realParametersSize ? 0 : capturedParamsSize); 354 } 355 356 @Override 357 public void visitVarInsn(int opcode, int var) { 358 super.visitVarInsn(opcode, getNewIndex(var)); 359 } 360 361 @Override 362 public void visitIincInsn(int var, int increment) { 363 super.visitIincInsn(getNewIndex(var), increment); 364 } 365 366 @Override 367 public void visitMaxs(int maxStack, int maxLocals) { 368 super.visitMaxs(maxStack, maxLocals + capturedParamsSize); 369 } 370 371 @Override 372 public void visitLineNumber(int line, @NotNull Label start) { 373 if(isInliningLambda || GENERATE_DEBUG_INFO) { 374 super.visitLineNumber(line, start); 375 } 376 } 377 378 @Override 379 public void visitLocalVariable( 380 @NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index 381 ) { 382 if (isInliningLambda || GENERATE_DEBUG_INFO) { 383 String varSuffix = inliningContext.isRoot() && 384 !InlineCodegenUtil.isFakeLocalVariableForInline(name) ? 385 INLINE_FUN_VAR_SUFFIX : ""; 386 String varName = !varSuffix.isEmpty() && name.equals("this") ? name + "_" : name; 387 super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index)); 388 } 389 } 390 }; 391 392 node.accept(transformedNode); 393 394 transformCaptured(transformedNode); 395 transformFinallyDeepIndex(transformedNode, finallyDeepShift); 396 397 return transformedNode; 398 } 399 400 @NotNull 401 protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node, int finallyDeepShift) { 402 node = prepareNode(node, finallyDeepShift); 403 404 try { 405 new MandatoryMethodTransformer().transform("fake", node); 406 } 407 catch (Throwable e) { 408 throw wrapException(e, node, "couldn't inline method call"); 409 } 410 411 Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()) { 412 @NotNull 413 @Override 414 protected Frame<SourceValue> newFrame( 415 int nLocals, int nStack 416 ) { 417 return new Frame<SourceValue>(nLocals, nStack) { 418 @Override 419 public void execute( 420 @NotNull AbstractInsnNode insn, Interpreter<SourceValue> interpreter 421 ) throws AnalyzerException { 422 if (insn.getOpcode() == Opcodes.RETURN) { 423 //there is exception on void non local return in frame 424 return; 425 } 426 super.execute(insn, interpreter); 427 } 428 }; 429 } 430 }; 431 432 Frame<SourceValue>[] sources; 433 try { 434 sources = analyzer.analyze("fake", node); 435 } 436 catch (AnalyzerException e) { 437 throw wrapException(e, node, "couldn't inline method call"); 438 } 439 Set<AbstractInsnNode> toDelete = SmartSet.create(); 440 InsnList instructions = node.instructions; 441 AbstractInsnNode cur = instructions.getFirst(); 442 443 boolean awaitClassReification = false; 444 int currentFinallyDeep = 0; 445 446 while (cur != null) { 447 Frame<SourceValue> frame = sources[instructions.indexOf(cur)]; 448 449 if (frame != null) { 450 if (ReifiedTypeInliner.isNeedClassReificationMarker(cur)) { 451 awaitClassReification = true; 452 } 453 else if (cur.getType() == AbstractInsnNode.METHOD_INSN) { 454 if (InlineCodegenUtil.isFinallyStart(cur)) { 455 //TODO deep index calc could be more precise 456 currentFinallyDeep = InlineCodegenUtil.getConstant(cur.getPrevious()); 457 } 458 459 MethodInsnNode methodInsnNode = (MethodInsnNode) cur; 460 String owner = methodInsnNode.owner; 461 String desc = methodInsnNode.desc; 462 String name = methodInsnNode.name; 463 //TODO check closure 464 Type[] argTypes = Type.getArgumentTypes(desc); 465 int paramCount = argTypes.length + 1;//non static 466 int firstParameterIndex = frame.getStackSize() - paramCount; 467 if (isInvokeOnLambda(owner, name) /*&& methodInsnNode.owner.equals(INLINE_RUNTIME)*/) { 468 SourceValue sourceValue = frame.getStack(firstParameterIndex); 469 470 LambdaInfo lambdaInfo = MethodInlinerUtilKt.getLambdaIfExistsAndMarkInstructions( 471 this, MethodInlinerUtilKt.singleOrNullInsn(sourceValue), true, instructions, sources, toDelete 472 ); 473 474 invokeCalls.add(new InvokeCall(lambdaInfo, currentFinallyDeep)); 475 } 476 else if (isAnonymousConstructorCall(owner, name)) { 477 Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>(); 478 479 int offset = 0; 480 for (int i = 0; i < paramCount; i++) { 481 SourceValue sourceValue = frame.getStack(firstParameterIndex + i); 482 LambdaInfo lambdaInfo = MethodInlinerUtilKt.getLambdaIfExistsAndMarkInstructions( 483 this, MethodInlinerUtilKt.singleOrNullInsn(sourceValue), false, instructions, sources, toDelete 484 ); 485 if (lambdaInfo != null) { 486 lambdaMapping.put(offset, lambdaInfo); 487 } 488 489 offset += i == 0 ? 1 : argTypes[i - 1].getSize(); 490 } 491 492 transformations.add( 493 buildConstructorInvocation( 494 owner, desc, lambdaMapping, awaitClassReification 495 ) 496 ); 497 awaitClassReification = false; 498 } 499 } 500 else if (cur.getOpcode() == Opcodes.GETSTATIC) { 501 FieldInsnNode fieldInsnNode = (FieldInsnNode) cur; 502 String className = fieldInsnNode.owner; 503 if (isAnonymousSingletonLoad(className, fieldInsnNode.name)) { 504 transformations.add( 505 new AnonymousObjectTransformationInfo( 506 className, awaitClassReification, isAlreadyRegenerated(className), true, 507 inliningContext.nameGenerator 508 ) 509 ); 510 awaitClassReification = false; 511 } 512 else if (isWhenMappingAccess(className, fieldInsnNode.name)) { 513 transformations.add( 514 new WhenMappingTransformationInfo( 515 className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode) 516 ); 517 } 518 519 } 520 } 521 AbstractInsnNode prevNode = cur; 522 cur = cur.getNext(); 523 524 //given frame is <tt>null</tt> if and only if the corresponding instruction cannot be reached (dead code). 525 if (frame == null) { 526 //clean dead code otherwise there is problems in unreachable finally block, don't touch label it cause try/catch/finally problems 527 if (prevNode.getType() == AbstractInsnNode.LABEL) { 528 //NB: Cause we generate exception table for default handler using gaps (see ExpressionCodegen.visitTryExpression) 529 //it may occurs that interval for default handler starts before catch start label, so this label seems as dead, 530 //but as result all this labels will be merged into one (see KT-5863) 531 } else { 532 toDelete.add(prevNode); 533 } 534 } 535 } 536 537 for (AbstractInsnNode insnNode : toDelete) { 538 instructions.remove(insnNode); 539 } 540 541 //clean dead try/catch blocks 542 List<TryCatchBlockNode> blocks = node.tryCatchBlocks; 543 for (Iterator<TryCatchBlockNode> iterator = blocks.iterator(); iterator.hasNext(); ) { 544 TryCatchBlockNode block = iterator.next(); 545 if (isEmptyTryInterval(block)) { 546 iterator.remove(); 547 } 548 } 549 550 return node; 551 } 552 553 private static boolean isEmptyTryInterval(@NotNull TryCatchBlockNode tryCatchBlockNode) { 554 LabelNode start = tryCatchBlockNode.start; 555 AbstractInsnNode end = tryCatchBlockNode.end; 556 while (end != start && end instanceof LabelNode) { 557 end = end.getPrevious(); 558 } 559 return start == end; 560 } 561 562 @NotNull 563 private AnonymousObjectTransformationInfo buildConstructorInvocation( 564 @NotNull String anonymousType, 565 @NotNull String desc, 566 @NotNull Map<Integer, LambdaInfo> lambdaMapping, 567 boolean needReification 568 ) { 569 return new AnonymousObjectTransformationInfo( 570 anonymousType, needReification, lambdaMapping, 571 inliningContext.classRegeneration, 572 isAlreadyRegenerated(anonymousType), 573 desc, 574 false, 575 inliningContext.nameGenerator 576 ); 577 } 578 579 private boolean isAlreadyRegenerated(@NotNull String owner) { 580 return inliningContext.typeRemapper.hasNoAdditionalMapping(owner); 581 } 582 583 @Nullable 584 LambdaInfo getLambdaIfExists(@Nullable AbstractInsnNode insnNode) { 585 if (insnNode == null) { 586 return null; 587 } 588 589 if (insnNode.getOpcode() == Opcodes.ALOAD) { 590 int varIndex = ((VarInsnNode) insnNode).var; 591 return getLambdaIfExists(varIndex); 592 } 593 else if (insnNode instanceof FieldInsnNode) { 594 FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode; 595 if (fieldInsnNode.name.startsWith("$$$")) { 596 return findCapturedField(fieldInsnNode, nodeRemapper).getLambda(); 597 } 598 } 599 600 return null; 601 } 602 603 private LambdaInfo getLambdaIfExists(int varIndex) { 604 if (varIndex < parameters.getArgsSizeOnStack()) { 605 return parameters.getParameterByDeclarationSlot(varIndex).getLambda(); 606 } 607 return null; 608 } 609 610 private static void removeClosureAssertions(MethodNode node) { 611 AbstractInsnNode cur = node.instructions.getFirst(); 612 while (cur != null && cur.getNext() != null) { 613 AbstractInsnNode next = cur.getNext(); 614 if (next.getType() == AbstractInsnNode.METHOD_INSN) { 615 MethodInsnNode methodInsnNode = (MethodInsnNode) next; 616 if (methodInsnNode.name.equals("checkParameterIsNotNull") && methodInsnNode.owner.equals(IntrinsicMethods.INTRINSICS_CLASS_NAME)) { 617 AbstractInsnNode prev = cur.getPrevious(); 618 619 assert cur.getOpcode() == Opcodes.LDC : "checkParameterIsNotNull should go after LDC but " + cur; 620 assert prev.getOpcode() == Opcodes.ALOAD : "checkParameterIsNotNull should be invoked on local var but " + prev; 621 622 node.instructions.remove(prev); 623 node.instructions.remove(cur); 624 cur = next.getNext(); 625 node.instructions.remove(next); 626 next = cur; 627 } 628 } 629 cur = next; 630 } 631 } 632 633 private void transformCaptured(@NotNull MethodNode node) { 634 if (nodeRemapper.isRoot()) { 635 return; 636 } 637 638 //Fold all captured variable chain - ALOAD 0 ALOAD this$0 GETFIELD $captured - to GETFIELD $$$$captured 639 //On future decoding this field could be inline or unfolded in another field access chain (it can differ in some missed this$0) 640 AbstractInsnNode cur = node.instructions.getFirst(); 641 while (cur != null) { 642 if (cur instanceof VarInsnNode && cur.getOpcode() == Opcodes.ALOAD) { 643 int varIndex = ((VarInsnNode) cur).var; 644 if (varIndex == 0 || nodeRemapper.processNonAload0FieldAccessChains(getLambdaIfExists(varIndex) != null)) { 645 List<AbstractInsnNode> accessChain = getCapturedFieldAccessChain((VarInsnNode) cur); 646 AbstractInsnNode insnNode = nodeRemapper.foldFieldAccessChainIfNeeded(accessChain, node); 647 if (insnNode != null) { 648 cur = insnNode; 649 } 650 } 651 } 652 cur = cur.getNext(); 653 } 654 } 655 656 private static void transformFinallyDeepIndex(@NotNull MethodNode node, int finallyDeepShift) { 657 if (finallyDeepShift == 0) { 658 return; 659 } 660 661 AbstractInsnNode cur = node.instructions.getFirst(); 662 while (cur != null) { 663 if (cur instanceof MethodInsnNode && InlineCodegenUtil.isFinallyMarker(cur)) { 664 AbstractInsnNode constant = cur.getPrevious(); 665 int curDeep = InlineCodegenUtil.getConstant(constant); 666 node.instructions.insert(constant, new LdcInsnNode(curDeep + finallyDeepShift)); 667 node.instructions.remove(constant); 668 } 669 cur = cur.getNext(); 670 } 671 } 672 673 @NotNull 674 public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) { 675 List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>(); 676 fieldAccessChain.add(aload0); 677 AbstractInsnNode next = aload0.getNext(); 678 while (next != null && next instanceof FieldInsnNode || next instanceof LabelNode) { 679 if (next instanceof LabelNode) { 680 next = next.getNext(); 681 continue; //it will be delete on transformation 682 } 683 fieldAccessChain.add(next); 684 if ("this$0".equals(((FieldInsnNode) next).name)) { 685 next = next.getNext(); 686 } 687 else { 688 break; 689 } 690 } 691 692 return fieldAccessChain; 693 } 694 695 public static void putStackValuesIntoLocals(List<Type> directOrder, int shift, InstructionAdapter iv, String descriptor) { 696 Type[] actualParams = Type.getArgumentTypes(descriptor); 697 assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!"; 698 699 int size = 0; 700 for (Type next : directOrder) { 701 size += next.getSize(); 702 } 703 704 shift += size; 705 int index = directOrder.size(); 706 707 for (Type next : Lists.reverse(directOrder)) { 708 shift -= next.getSize(); 709 Type typeOnStack = actualParams[--index]; 710 if (!typeOnStack.equals(next)) { 711 StackValue.onStack(typeOnStack).put(next, iv); 712 } 713 iv.store(shift, next); 714 } 715 } 716 717 //TODO: check it's external module 718 //TODO?: assert method exists in facade? 719 public String changeOwnerForExternalPackage(String type, int opcode) { 720 //if (isSameModule || (opcode & Opcodes.INVOKESTATIC) == 0) { 721 // return type; 722 //} 723 724 //JvmClassName name = JvmClassName.byInternalName(type); 725 //String packageClassInternalName = PackageClassUtils.getPackageClassInternalName(name.getPackageFqName()); 726 //if (type.startsWith(packageClassInternalName + '$')) { 727 // VirtualFile virtualFile = InlineCodegenUtil.findVirtualFile(inliningContext.state.getProject(), type); 728 // if (virtualFile != null) { 729 // KotlinJvmBinaryClass klass = KotlinBinaryClassCache.getKotlinBinaryClass(virtualFile); 730 // if (klass != null && klass.getClassHeader().getSyntheticClassKind() == KotlinSyntheticClass.Kind.PACKAGE_PART) { 731 // return packageClassInternalName; 732 // } 733 // } 734 //} 735 736 return type; 737 } 738 739 @NotNull 740 public RuntimeException wrapException(@NotNull Throwable originalException, @NotNull MethodNode node, @NotNull String errorSuffix) { 741 if (originalException instanceof InlineException) { 742 return new InlineException(errorPrefix + ": " + errorSuffix, originalException); 743 } 744 else { 745 return new InlineException(errorPrefix + ": " + errorSuffix + "\ncause: " + 746 getNodeText(node), originalException); 747 } 748 } 749 750 @NotNull 751 //process local and global returns (local substituted with goto end-label global kept unchanged) 752 public static List<PointForExternalFinallyBlocks> processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) { 753 if (!remapReturn) { 754 return Collections.emptyList(); 755 } 756 List<PointForExternalFinallyBlocks> result = new ArrayList<PointForExternalFinallyBlocks>(); 757 InsnList instructions = node.instructions; 758 AbstractInsnNode insnNode = instructions.getFirst(); 759 while (insnNode != null) { 760 if (InlineCodegenUtil.isReturnOpcode(insnNode.getOpcode())) { 761 AbstractInsnNode previous = insnNode.getPrevious(); 762 MethodInsnNode flagNode; 763 boolean isLocalReturn = true; 764 String labelName = null; 765 if (previous != null && previous instanceof MethodInsnNode && InlineCodegenUtil.NON_LOCAL_RETURN.equals(((MethodInsnNode) previous).owner)) { 766 flagNode = (MethodInsnNode) previous; 767 labelName = flagNode.name; 768 } 769 770 if (labelName != null) { 771 isLocalReturn = labelOwner.isMyLabel(labelName); 772 //remove global return flag 773 if (isLocalReturn) { 774 instructions.remove(previous); 775 } 776 } 777 778 if (isLocalReturn && endLabel != null) { 779 LabelNode labelNode = (LabelNode) endLabel.info; 780 JumpInsnNode jumpInsnNode = new JumpInsnNode(Opcodes.GOTO, labelNode); 781 instructions.insert(insnNode, jumpInsnNode); 782 instructions.remove(insnNode); 783 insnNode = jumpInsnNode; 784 } 785 786 //genetate finally block before nonLocalReturn flag/return/goto 787 LabelNode label = new LabelNode(); 788 instructions.insert(insnNode, label); 789 result.add(new PointForExternalFinallyBlocks(getInstructionToInsertFinallyBefore(insnNode, isLocalReturn), 790 getReturnType(insnNode.getOpcode()), 791 label)); 792 } 793 insnNode = insnNode.getNext(); 794 } 795 return result; 796 } 797 798 @NotNull 799 private static AbstractInsnNode getInstructionToInsertFinallyBefore(@NotNull AbstractInsnNode nonLocalReturnOrJump, boolean isLocal) { 800 return isLocal ? nonLocalReturnOrJump : nonLocalReturnOrJump.getPrevious(); 801 } 802 803 //Place to insert finally blocks from try blocks that wraps inline fun call 804 public static class PointForExternalFinallyBlocks { 805 806 final AbstractInsnNode beforeIns; 807 808 final Type returnType; 809 810 final LabelNode finallyIntervalEnd; 811 812 public PointForExternalFinallyBlocks( 813 @NotNull AbstractInsnNode beforeIns, 814 @NotNull Type returnType, 815 @NotNull LabelNode finallyIntervalEnd 816 ) { 817 this.beforeIns = beforeIns; 818 this.returnType = returnType; 819 this.finallyIntervalEnd = finallyIntervalEnd; 820 } 821 822 } 823 }