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