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