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