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 }