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 }