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