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 com.intellij.util.containers.SmartHashSet; 022 import org.jetbrains.annotations.NotNull; 023 import org.jetbrains.annotations.Nullable; 024 import org.jetbrains.kotlin.codegen.ClosureCodegen; 025 import org.jetbrains.kotlin.codegen.StackValue; 026 import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods; 027 import org.jetbrains.kotlin.codegen.optimization.MandatoryMethodTransformer; 028 import org.jetbrains.kotlin.codegen.state.JetTypeMapper; 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 // we will put it if needed in anew processing 290 } 291 else { 292 super.visitMethodInsn(opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf); 293 } 294 } 295 296 @Override 297 public void visitFieldInsn(int opcode, @NotNull String owner, @NotNull String name, @NotNull String desc) { 298 if (opcode == Opcodes.GETSTATIC && isAnonymousSingletonLoad(owner, name)) { 299 handleAnonymousObjectGeneration(); 300 } 301 super.visitFieldInsn(opcode, owner, name, desc); 302 } 303 304 @Override 305 public void visitMaxs(int stack, int locals) { 306 lambdasFinallyBlocks = resultNode.tryCatchBlocks.size(); 307 super.visitMaxs(stack, locals); 308 } 309 310 }; 311 312 node.accept(lambdaInliner); 313 314 return resultNode; 315 } 316 317 @NotNull 318 public static CapturedParamInfo findCapturedField(FieldInsnNode node, FieldRemapper fieldRemapper) { 319 assert node.name.startsWith("$$$") : "Captured field template should start with $$$ prefix"; 320 FieldInsnNode fin = new FieldInsnNode(node.getOpcode(), node.owner, node.name.substring(3), node.desc); 321 CapturedParamInfo field = fieldRemapper.findField(fin); 322 if (field == null) { 323 throw new IllegalStateException("Couldn't find captured field " + node.owner + "." + node.name + " in " + fieldRemapper.getLambdaInternalName()); 324 } 325 return field; 326 } 327 328 @NotNull 329 public MethodNode prepareNode(@NotNull MethodNode node, int finallyDeepShift) { 330 final int capturedParamsSize = parameters.getCapturedArgsSizeOnStack(); 331 final int realParametersSize = parameters.getRealArgsSizeOnStack(); 332 Type[] types = Type.getArgumentTypes(node.desc); 333 Type returnType = Type.getReturnType(node.desc); 334 335 List<Type> capturedTypes = parameters.getCapturedTypes(); 336 Type[] allTypes = ArrayUtil.mergeArrays(types, capturedTypes.toArray(new Type[capturedTypes.size()])); 337 338 node.instructions.resetLabels(); 339 MethodNode transformedNode = new MethodNode(InlineCodegenUtil.API, node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null) { 340 341 private final boolean isInliningLambda = nodeRemapper.isInsideInliningLambda(); 342 343 private int getNewIndex(int var) { 344 return var + (var < realParametersSize ? 0 : capturedParamsSize); 345 } 346 347 @Override 348 public void visitVarInsn(int opcode, int var) { 349 super.visitVarInsn(opcode, getNewIndex(var)); 350 } 351 352 @Override 353 public void visitIincInsn(int var, int increment) { 354 super.visitIincInsn(getNewIndex(var), increment); 355 } 356 357 @Override 358 public void visitMaxs(int maxStack, int maxLocals) { 359 super.visitMaxs(maxStack, maxLocals + capturedParamsSize); 360 } 361 362 @Override 363 public void visitLineNumber(int line, @NotNull Label start) { 364 if(isInliningLambda || InlineCodegenUtil.GENERATE_SMAP) { 365 super.visitLineNumber(line, start); 366 } 367 } 368 369 @Override 370 public void visitLocalVariable( 371 @NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index 372 ) { 373 if (isInliningLambda || InlineCodegenUtil.GENERATE_SMAP) { 374 String varSuffix = inliningContext.isRoot() && 375 !((RootInliningContext) inliningContext).isDefaultCompilation && 376 !InlineCodegenUtil.isFakeLocalVariableForInline(name) ? 377 INLINE_FUN_VAR_SUFFIX : ""; 378 String varName = !varSuffix.isEmpty() && name.equals("this") ? name + "_" : name; 379 super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index)); 380 } 381 } 382 }; 383 384 node.accept(transformedNode); 385 386 transformCaptured(transformedNode); 387 transformFinallyDeepIndex(transformedNode, finallyDeepShift); 388 389 return transformedNode; 390 } 391 392 @NotNull 393 protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node, int finallyDeepShift) { 394 node = prepareNode(node, finallyDeepShift); 395 396 try { 397 new MandatoryMethodTransformer().transform("fake", node); 398 } 399 catch (Throwable e) { 400 throw wrapException(e, node, "couldn't inline method call"); 401 } 402 403 Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()) { 404 @NotNull 405 @Override 406 protected Frame<SourceValue> newFrame( 407 int nLocals, int nStack 408 ) { 409 return new Frame<SourceValue>(nLocals, nStack) { 410 @Override 411 public void execute( 412 @NotNull AbstractInsnNode insn, Interpreter<SourceValue> interpreter 413 ) throws AnalyzerException { 414 if (insn.getOpcode() == Opcodes.RETURN) { 415 //there is exception on void non local return in frame 416 return; 417 } 418 super.execute(insn, interpreter); 419 } 420 }; 421 } 422 }; 423 424 Frame<SourceValue>[] sources; 425 try { 426 sources = analyzer.analyze("fake", node); 427 } 428 catch (AnalyzerException e) { 429 throw wrapException(e, node, "couldn't inline method call"); 430 } 431 Set<AbstractInsnNode> toDelete = new SmartHashSet<AbstractInsnNode>(); 432 InstructionsAndFrames instructionsAndFrames = new InstructionsAndFrames(sources, node.instructions); 433 AbstractInsnNode cur = node.instructions.getFirst(); 434 int index = 0; 435 436 boolean awaitClassReification = false; 437 int currentFinallyDeep = 0; 438 439 while (cur != null) { 440 Frame<SourceValue> frame = sources[index]; 441 442 if (frame != null) { 443 if (ReifiedTypeInliner.isNeedClassReificationMarker(cur)) { 444 awaitClassReification = true; 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 = null; 464 int varIndex = -1; 465 466 if (sourceValue.insns.size() == 1) { 467 AbstractInsnNode insnNode = sourceValue.insns.iterator().next(); 468 AbstractInsnNode processingInstruction = insnNode; 469 470 if (insnNode.getOpcode() == Opcodes.SWAP) { 471 processingInstruction = InlineCodegenUtil.getPrevMeaningful(insnNode); 472 } 473 lambdaInfo = getLambdaIfExistsAndMarkInstructions(processingInstruction, frame, instructionsAndFrames, toDelete); 474 if (lambdaInfo != null) { 475 //remove inlinable access 476 assert processingInstruction != null; 477 InlineCodegenUtil.removeInterval(node, processingInstruction, insnNode); 478 } 479 } 480 481 invokeCalls.add(new InvokeCall(varIndex, lambdaInfo, currentFinallyDeep)); 482 } 483 else if (isAnonymousConstructorCall(owner, name)) { 484 Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>(); 485 486 int offset = 0; 487 for (int i = 0; i < paramCount; i++) { 488 SourceValue sourceValue = frame.getStack(firstParameterIndex + i); 489 if (sourceValue.insns.size() == 1) { 490 AbstractInsnNode insnNode = sourceValue.insns.iterator().next(); 491 LambdaInfo lambdaInfo = getLambdaIfExistsAndMarkInstructions(insnNode, frame, instructionsAndFrames, toDelete); 492 if (lambdaInfo != null) { 493 lambdaMapping.put(offset, lambdaInfo); 494 node.instructions.remove(insnNode); 495 } 496 } 497 offset += i == 0 ? 1 : argTypes[i - 1].getSize(); 498 } 499 500 anonymousObjectGenerations.add( 501 buildConstructorInvocation( 502 owner, desc, lambdaMapping, awaitClassReification 503 ) 504 ); 505 awaitClassReification = false; 506 } 507 } 508 else if (cur.getOpcode() == Opcodes.GETSTATIC) { 509 FieldInsnNode fieldInsnNode = (FieldInsnNode) cur; 510 String owner = fieldInsnNode.owner; 511 if (isAnonymousSingletonLoad(owner, fieldInsnNode.name)) { 512 anonymousObjectGenerations.add( 513 new AnonymousObjectGeneration( 514 owner, isSameModule, awaitClassReification, isAlreadyRegenerated(owner), true 515 ) 516 ); 517 awaitClassReification = false; 518 } 519 } 520 } 521 AbstractInsnNode prevNode = cur; 522 cur = cur.getNext(); 523 index++; 524 525 //given frame is <tt>null</tt> if and only if the corresponding instruction cannot be reached (dead code). 526 if (frame == null) { 527 //clean dead code otherwise there is problems in unreachable finally block, don't touch label it cause try/catch/finally problems 528 if (prevNode.getType() == AbstractInsnNode.LABEL) { 529 //NB: Cause we generate exception table for default handler using gaps (see ExpressionCodegen.visitTryExpression) 530 //it may occurs that interval for default handler starts before catch start label, so this label seems as dead, 531 //but as result all this labels will be merged into one (see KT-5863) 532 } else { 533 node.instructions.remove(prevNode); 534 } 535 } 536 } 537 538 for (AbstractInsnNode insnNode : toDelete) { 539 node.instructions.remove(insnNode); 540 } 541 542 //clean dead try/catch blocks 543 List<TryCatchBlockNode> blocks = node.tryCatchBlocks; 544 for (Iterator<TryCatchBlockNode> iterator = blocks.iterator(); iterator.hasNext(); ) { 545 TryCatchBlockNode block = iterator.next(); 546 if (isEmptyTryInterval(block)) { 547 iterator.remove(); 548 } 549 } 550 551 return node; 552 } 553 554 private static boolean isEmptyTryInterval(@NotNull TryCatchBlockNode tryCatchBlockNode) { 555 LabelNode start = tryCatchBlockNode.start; 556 AbstractInsnNode end = tryCatchBlockNode.end; 557 while (end != start && end instanceof LabelNode) { 558 end = end.getPrevious(); 559 } 560 return start == end; 561 } 562 563 @NotNull 564 private AnonymousObjectGeneration buildConstructorInvocation( 565 @NotNull String owner, 566 @NotNull String desc, 567 @NotNull Map<Integer, LambdaInfo> lambdaMapping, 568 boolean needReification 569 ) { 570 return new AnonymousObjectGeneration( 571 owner, needReification, isSameModule, lambdaMapping, 572 inliningContext.classRegeneration, 573 isAlreadyRegenerated(owner), 574 desc, 575 false 576 ); 577 } 578 579 private boolean isAlreadyRegenerated(@NotNull String owner) { 580 return inliningContext.typeRemapper.hasNoAdditionalMapping(owner); 581 } 582 583 @Nullable 584 private LambdaInfo getLambdaIfExistsAndMarkInstructions( 585 @Nullable AbstractInsnNode insnNode, 586 @NotNull Frame<SourceValue> localFrame, 587 @NotNull InstructionsAndFrames insAndFrames, 588 @NotNull Set<AbstractInsnNode> toDelete 589 ) { 590 LambdaInfo lambdaInfo = getLambdaIfExists(insnNode); 591 592 if (lambdaInfo == null && insnNode instanceof VarInsnNode && insnNode.getOpcode() == Opcodes.ALOAD) { 593 int varIndex = ((VarInsnNode) insnNode).var; 594 SourceValue local = localFrame.getLocal(varIndex); 595 if (local.insns.size() == 1) { 596 AbstractInsnNode storeIns = local.insns.iterator().next(); 597 if (storeIns instanceof VarInsnNode && storeIns.getOpcode() == Opcodes.ASTORE) { 598 Frame<SourceValue> frame = insAndFrames.get(storeIns); 599 if (frame != null) { 600 SourceValue topOfStack = frame.getStack(frame.getStackSize() - 1); 601 if(topOfStack.insns.size() == 1) { 602 AbstractInsnNode lambdaAload = topOfStack.insns.iterator().next(); 603 lambdaInfo = getLambdaIfExistsAndMarkInstructions(lambdaAload, frame, insAndFrames, toDelete); 604 if (lambdaInfo != null) { 605 toDelete.add(storeIns); 606 toDelete.add(lambdaAload); 607 } 608 } 609 } 610 } 611 } 612 } 613 614 return lambdaInfo; 615 } 616 617 @Nullable 618 private LambdaInfo getLambdaIfExists(@Nullable AbstractInsnNode insnNode) { 619 if (insnNode == null) { 620 return null; 621 } 622 623 if (insnNode.getOpcode() == Opcodes.ALOAD) { 624 int varIndex = ((VarInsnNode) insnNode).var; 625 return getLambdaIfExists(varIndex); 626 } 627 else if (insnNode instanceof FieldInsnNode) { 628 FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode; 629 if (fieldInsnNode.name.startsWith("$$$")) { 630 return findCapturedField(fieldInsnNode, nodeRemapper).getLambda(); 631 } 632 } 633 634 return null; 635 } 636 637 private LambdaInfo getLambdaIfExists(int varIndex) { 638 if (varIndex < parameters.getArgsSizeOnStack()) { 639 return parameters.getParameterByDeclarationSlot(varIndex).getLambda(); 640 } 641 return null; 642 } 643 644 private static void removeClosureAssertions(MethodNode node) { 645 AbstractInsnNode cur = node.instructions.getFirst(); 646 while (cur != null && cur.getNext() != null) { 647 AbstractInsnNode next = cur.getNext(); 648 if (next.getType() == AbstractInsnNode.METHOD_INSN) { 649 MethodInsnNode methodInsnNode = (MethodInsnNode) next; 650 if (methodInsnNode.name.equals("checkParameterIsNotNull") && methodInsnNode.owner.equals(IntrinsicMethods.INTRINSICS_CLASS_NAME)) { 651 AbstractInsnNode prev = cur.getPrevious(); 652 653 assert cur.getOpcode() == Opcodes.LDC : "checkParameterIsNotNull should go after LDC but " + cur; 654 assert prev.getOpcode() == Opcodes.ALOAD : "checkParameterIsNotNull should be invoked on local var but " + prev; 655 656 node.instructions.remove(prev); 657 node.instructions.remove(cur); 658 cur = next.getNext(); 659 node.instructions.remove(next); 660 next = cur; 661 } 662 } 663 cur = next; 664 } 665 } 666 667 private void transformCaptured(@NotNull MethodNode node) { 668 if (nodeRemapper.isRoot()) { 669 return; 670 } 671 672 //Fold all captured variable chain - ALOAD 0 ALOAD this$0 GETFIELD $captured - to GETFIELD $$$$captured 673 //On future decoding this field could be inline or unfolded in another field access chain (it can differ in some missed this$0) 674 AbstractInsnNode cur = node.instructions.getFirst(); 675 while (cur != null) { 676 if (cur instanceof VarInsnNode && cur.getOpcode() == Opcodes.ALOAD) { 677 int varIndex = ((VarInsnNode) cur).var; 678 if (varIndex == 0 || nodeRemapper.processNonAload0FieldAccessChains(getLambdaIfExists(varIndex) != null)) { 679 List<AbstractInsnNode> accessChain = getCapturedFieldAccessChain((VarInsnNode) cur); 680 AbstractInsnNode insnNode = nodeRemapper.foldFieldAccessChainIfNeeded(accessChain, node); 681 if (insnNode != null) { 682 cur = insnNode; 683 } 684 } 685 } 686 cur = cur.getNext(); 687 } 688 } 689 690 private static void transformFinallyDeepIndex(@NotNull MethodNode node, int finallyDeepShift) { 691 if (finallyDeepShift == 0) { 692 return; 693 } 694 695 AbstractInsnNode cur = node.instructions.getFirst(); 696 while (cur != null) { 697 if (cur instanceof MethodInsnNode && InlineCodegenUtil.isFinallyMarker(cur)) { 698 AbstractInsnNode constant = cur.getPrevious(); 699 int curDeep = InlineCodegenUtil.getConstant(constant); 700 node.instructions.insert(constant, new LdcInsnNode(curDeep + finallyDeepShift)); 701 node.instructions.remove(constant); 702 } 703 cur = cur.getNext(); 704 } 705 } 706 707 @NotNull 708 public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) { 709 List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>(); 710 fieldAccessChain.add(aload0); 711 AbstractInsnNode next = aload0.getNext(); 712 while (next != null && next instanceof FieldInsnNode || next instanceof LabelNode) { 713 if (next instanceof LabelNode) { 714 next = next.getNext(); 715 continue; //it will be delete on transformation 716 } 717 fieldAccessChain.add(next); 718 if ("this$0".equals(((FieldInsnNode) next).name)) { 719 next = next.getNext(); 720 } 721 else { 722 break; 723 } 724 } 725 726 return fieldAccessChain; 727 } 728 729 public static void putStackValuesIntoLocals(List<Type> directOrder, int shift, InstructionAdapter iv, String descriptor) { 730 Type[] actualParams = Type.getArgumentTypes(descriptor); 731 assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!"; 732 733 int size = 0; 734 for (Type next : directOrder) { 735 size += next.getSize(); 736 } 737 738 shift += size; 739 int index = directOrder.size(); 740 741 for (Type next : Lists.reverse(directOrder)) { 742 shift -= next.getSize(); 743 Type typeOnStack = actualParams[--index]; 744 if (!typeOnStack.equals(next)) { 745 StackValue.onStack(typeOnStack).put(next, iv); 746 } 747 iv.store(shift, next); 748 } 749 } 750 751 //TODO: check it's external module 752 //TODO?: assert method exists in facade? 753 public String changeOwnerForExternalPackage(String type, int opcode) { 754 //if (isSameModule || (opcode & Opcodes.INVOKESTATIC) == 0) { 755 // return type; 756 //} 757 758 //JvmClassName name = JvmClassName.byInternalName(type); 759 //String packageClassInternalName = PackageClassUtils.getPackageClassInternalName(name.getPackageFqName()); 760 //if (type.startsWith(packageClassInternalName + '$')) { 761 // VirtualFile virtualFile = InlineCodegenUtil.findVirtualFile(inliningContext.state.getProject(), type); 762 // if (virtualFile != null) { 763 // KotlinJvmBinaryClass klass = KotlinBinaryClassCache.getKotlinBinaryClass(virtualFile); 764 // if (klass != null && klass.getClassHeader().getSyntheticClassKind() == KotlinSyntheticClass.Kind.PACKAGE_PART) { 765 // return packageClassInternalName; 766 // } 767 // } 768 //} 769 770 return type; 771 } 772 773 @NotNull 774 public RuntimeException wrapException(@NotNull Throwable originalException, @NotNull MethodNode node, @NotNull String errorSuffix) { 775 if (originalException instanceof InlineException) { 776 return new InlineException(errorPrefix + ": " + errorSuffix, originalException); 777 } 778 else { 779 return new InlineException(errorPrefix + ": " + errorSuffix + "\ncause: " + 780 getNodeText(node), originalException); 781 } 782 } 783 784 @NotNull 785 //process local and global returns (local substituted with goto end-label global kept unchanged) 786 public static List<PointForExternalFinallyBlocks> processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) { 787 if (!remapReturn) { 788 return Collections.emptyList(); 789 } 790 List<PointForExternalFinallyBlocks> result = new ArrayList<PointForExternalFinallyBlocks>(); 791 InsnList instructions = node.instructions; 792 AbstractInsnNode insnNode = instructions.getFirst(); 793 while (insnNode != null) { 794 if (InlineCodegenUtil.isReturnOpcode(insnNode.getOpcode())) { 795 AbstractInsnNode previous = insnNode.getPrevious(); 796 MethodInsnNode flagNode; 797 boolean isLocalReturn = true; 798 String labelName = null; 799 if (previous != null && previous instanceof MethodInsnNode && InlineCodegenUtil.NON_LOCAL_RETURN.equals(((MethodInsnNode) previous).owner)) { 800 flagNode = (MethodInsnNode) previous; 801 labelName = flagNode.name; 802 } 803 804 if (labelName != null) { 805 isLocalReturn = labelOwner.isMyLabel(labelName); 806 //remove global return flag 807 if (isLocalReturn) { 808 instructions.remove(previous); 809 } 810 } 811 812 if (isLocalReturn && endLabel != null) { 813 LabelNode labelNode = (LabelNode) endLabel.info; 814 JumpInsnNode jumpInsnNode = new JumpInsnNode(Opcodes.GOTO, labelNode); 815 instructions.insert(insnNode, jumpInsnNode); 816 instructions.remove(insnNode); 817 insnNode = jumpInsnNode; 818 } 819 820 //genetate finally block before nonLocalReturn flag/return/goto 821 LabelNode label = new LabelNode(); 822 instructions.insert(insnNode, label); 823 result.add(new PointForExternalFinallyBlocks(getInstructionToInsertFinallyBefore(insnNode, isLocalReturn), 824 getReturnType(insnNode.getOpcode()), 825 label)); 826 } 827 insnNode = insnNode.getNext(); 828 } 829 return result; 830 } 831 832 @NotNull 833 private static AbstractInsnNode getInstructionToInsertFinallyBefore(@NotNull AbstractInsnNode nonLocalReturnOrJump, boolean isLocal) { 834 return isLocal ? nonLocalReturnOrJump : nonLocalReturnOrJump.getPrevious(); 835 } 836 837 //Place to insert finally blocks from try blocks that wraps inline fun call 838 public static class PointForExternalFinallyBlocks { 839 840 final AbstractInsnNode beforeIns; 841 842 final Type returnType; 843 844 final LabelNode finallyIntervalEnd; 845 846 public PointForExternalFinallyBlocks( 847 @NotNull AbstractInsnNode beforeIns, 848 @NotNull Type returnType, 849 @NotNull LabelNode finallyIntervalEnd 850 ) { 851 this.beforeIns = beforeIns; 852 this.returnType = returnType; 853 this.finallyIntervalEnd = finallyIntervalEnd; 854 } 855 856 } 857 858 }