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