@NotNull // process local and global returns (local substituted with goto end-label global kept unchanged) public static List<PointForExternalFinallyBlocks> processReturns( @NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) { if (!remapReturn) { return Collections.emptyList(); } List<PointForExternalFinallyBlocks> result = new ArrayList<PointForExternalFinallyBlocks>(); InsnList instructions = node.instructions; AbstractInsnNode insnNode = instructions.getFirst(); while (insnNode != null) { if (InlineCodegenUtil.isReturnOpcode(insnNode.getOpcode())) { AbstractInsnNode previous = insnNode.getPrevious(); MethodInsnNode flagNode; boolean isLocalReturn = true; String labelName = null; if (previous != null && previous instanceof MethodInsnNode && InlineCodegenUtil.NON_LOCAL_RETURN.equals(((MethodInsnNode) previous).owner)) { flagNode = (MethodInsnNode) previous; labelName = flagNode.name; } if (labelName != null) { isLocalReturn = labelOwner.isMyLabel(labelName); // remove global return flag if (isLocalReturn) { instructions.remove(previous); } } if (isLocalReturn && endLabel != null) { LabelNode labelNode = (LabelNode) endLabel.info; JumpInsnNode jumpInsnNode = new JumpInsnNode(Opcodes.GOTO, labelNode); instructions.insert(insnNode, jumpInsnNode); instructions.remove(insnNode); insnNode = jumpInsnNode; } // genetate finally block before nonLocalReturn flag/return/goto result.add( new PointForExternalFinallyBlocks( isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode()))); } insnNode = insnNode.getNext(); } return result; }
@NotNull public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) { List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>(); fieldAccessChain.add(aload0); AbstractInsnNode next = aload0.getNext(); while (next != null && next instanceof FieldInsnNode || next instanceof LabelNode) { if (next instanceof LabelNode) { next = next.getNext(); continue; // it will be delete on transformation } fieldAccessChain.add(next); if ("this$0".equals(((FieldInsnNode) next).name)) { next = next.getNext(); } else { break; } } return fieldAccessChain; }
public static void putStackValuesIntoLocals( List<Type> directOrder, int shift, InstructionAdapter iv, String descriptor) { Type[] actualParams = Type.getArgumentTypes(descriptor); assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!"; int size = 0; for (Type next : directOrder) { size += next.getSize(); } shift += size; int index = directOrder.size(); for (Type next : Lists.reverse(directOrder)) { shift -= next.getSize(); Type typeOnStack = actualParams[--index]; if (!typeOnStack.equals(next)) { StackValue.onStack(typeOnStack).put(next, iv); } iv.store(shift, next); } }
@NotNull protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) { node = prepareNode(node); Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()) { @NotNull @Override protected Frame<SourceValue> newFrame(int nLocals, int nStack) { return new Frame<SourceValue>(nLocals, nStack) { @Override public void execute( @NotNull AbstractInsnNode insn, Interpreter<SourceValue> interpreter) throws AnalyzerException { if (insn.getOpcode() == Opcodes.RETURN) { // there is exception on void non local return in frame return; } super.execute(insn, interpreter); } }; } }; Frame<SourceValue>[] sources; try { sources = analyzer.analyze("fake", node); } catch (AnalyzerException e) { throw wrapException(e, node, "couldn't inline method call"); } AbstractInsnNode cur = node.instructions.getFirst(); int index = 0; boolean awaitClassReification = false; Set<LabelNode> possibleDeadLabels = new HashSet<LabelNode>(); while (cur != null) { Frame<SourceValue> frame = sources[index]; if (frame != null) { if (ReifiedTypeInliner.isNeedClassReificationMarker(cur)) { awaitClassReification = true; } else if (cur.getType() == AbstractInsnNode.METHOD_INSN) { MethodInsnNode methodInsnNode = (MethodInsnNode) cur; String owner = methodInsnNode.owner; String desc = methodInsnNode.desc; String name = methodInsnNode.name; // TODO check closure int paramLength = Type.getArgumentTypes(desc).length + 1; // non static if (isInvokeOnLambda(owner, name) /*&& methodInsnNode.owner.equals(INLINE_RUNTIME)*/) { SourceValue sourceValue = frame.getStack(frame.getStackSize() - paramLength); LambdaInfo lambdaInfo = null; int varIndex = -1; if (sourceValue.insns.size() == 1) { AbstractInsnNode insnNode = sourceValue.insns.iterator().next(); lambdaInfo = getLambdaIfExists(insnNode); if (lambdaInfo != null) { // remove inlinable access node.instructions.remove(insnNode); } } invokeCalls.add(new InvokeCall(varIndex, lambdaInfo)); } else if (isAnonymousConstructorCall(owner, name)) { Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>(); int paramStart = frame.getStackSize() - paramLength; for (int i = 0; i < paramLength; i++) { SourceValue sourceValue = frame.getStack(paramStart + i); if (sourceValue.insns.size() == 1) { AbstractInsnNode insnNode = sourceValue.insns.iterator().next(); LambdaInfo lambdaInfo = getLambdaIfExists(insnNode); if (lambdaInfo != null) { lambdaMapping.put(i, lambdaInfo); node.instructions.remove(insnNode); } } } anonymousObjectGenerations.add( buildConstructorInvocation(owner, desc, lambdaMapping, awaitClassReification)); awaitClassReification = false; } } else if (cur.getOpcode() == Opcodes.GETSTATIC) { FieldInsnNode fieldInsnNode = (FieldInsnNode) cur; String owner = fieldInsnNode.owner; if (isAnonymousSingletonLoad(owner, fieldInsnNode.name)) { anonymousObjectGenerations.add( new AnonymousObjectGeneration( owner, isSameModule, awaitClassReification, isAlreadyRegenerated(owner), true)); awaitClassReification = false; } } } AbstractInsnNode prevNode = cur; cur = cur.getNext(); index++; // given frame is <tt>null</tt> if and only if the corresponding instruction cannot be reached // (dead code). if (frame == null) { // clean dead code otherwise there is problems in unreachable finally block, don't touch // label it cause try/catch/finally problems if (prevNode.getType() == AbstractInsnNode.LABEL) { // NB: Cause we generate exception table for default handler using gaps (see // ExpressionCodegen.visitTryExpression) // it may occurs that interval for default handler starts before catch start label, so // this label seems as dead, // but as result all this labels will be merged into one (see KT-5863) } else { node.instructions.remove(prevNode); } } } // clean dead try/catch blocks List<TryCatchBlockNode> blocks = node.tryCatchBlocks; for (Iterator<TryCatchBlockNode> iterator = blocks.iterator(); iterator.hasNext(); ) { TryCatchBlockNode block = iterator.next(); if (isEmptyTryInterval(block)) { iterator.remove(); } } return node; }
private MethodNode doInline(MethodNode node) { final Deque<InvokeCall> currentInvokes = new LinkedList<InvokeCall>(invokeCalls); final MethodNode resultNode = new MethodNode(node.access, node.name, node.desc, node.signature, null); final Iterator<AnonymousObjectGeneration> iterator = anonymousObjectGenerations.iterator(); RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter( resultNode.access, resultNode.desc, resultNode, new TypeRemapper(currentTypeMapping)); InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.totalSize()) { private AnonymousObjectGeneration anonymousObjectGen; private void handleAnonymousObjectGeneration() { anonymousObjectGen = iterator.next(); if (anonymousObjectGen.shouldRegenerate()) { // TODO: need poping of type but what to do with local funs??? Type newLambdaType = Type.getObjectType(inliningContext.nameGenerator.genLambdaClassName()); currentTypeMapping.put( anonymousObjectGen.getOwnerInternalName(), newLambdaType.getInternalName()); AnonymousObjectTransformer transformer = new AnonymousObjectTransformer( anonymousObjectGen.getOwnerInternalName(), inliningContext.subInlineWithClassRegeneration( inliningContext.nameGenerator, currentTypeMapping, anonymousObjectGen), isSameModule, newLambdaType); InlineResult transformResult = transformer.doTransform(anonymousObjectGen, nodeRemapper); result.addAllClassesToRemove(transformResult); if (inliningContext.isInliningLambda && !anonymousObjectGen.isStaticOrigin()) { // this class is transformed and original not used so we should remove original one // after inlining // Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC // within lambda // because it can be local function from outer scope result.addClassToRemove(anonymousObjectGen.getOwnerInternalName()); } if (transformResult.getReifiedTypeParametersUsages().wereUsedReifiedParameters()) { ReifiedTypeInliner.putNeedClassReificationMarker(mv); result .getReifiedTypeParametersUsages() .mergeAll(transformResult.getReifiedTypeParametersUsages()); } } } @Override public void anew(@NotNull Type type) { if (isAnonymousConstructorCall(type.getInternalName(), "<init>")) { handleAnonymousObjectGeneration(); } // in case of regenerated anonymousObjectGen type would be remapped to new one via // remappingMethodAdapter super.anew(type); } @Override public void visitMethodInsn( int opcode, String owner, String name, String desc, boolean itf) { if ( /*INLINE_RUNTIME.equals(owner) &&*/ isInvokeOnLambda(owner, name)) { // TODO add method assert !currentInvokes.isEmpty(); InvokeCall invokeCall = currentInvokes.remove(); LambdaInfo info = invokeCall.lambdaInfo; if (info == null) { // noninlinable lambda super.visitMethodInsn(opcode, owner, name, desc, itf); return; } int valueParamShift = getNextLocalIndex(); // NB: don't inline cause it changes putStackValuesIntoLocals( info.getInvokeParamsWithoutCaptured(), valueParamShift, this, desc); addInlineMarker(this, true); Parameters lambdaParameters = info.addAllParameters(nodeRemapper); InlinedLambdaRemapper newCapturedRemapper = new InlinedLambdaRemapper( info.getLambdaClassType().getInternalName(), nodeRemapper, lambdaParameters); setLambdaInlining(true); MethodInliner inliner = new MethodInliner( info.getNode(), lambdaParameters, inliningContext.subInlineLambda(info), newCapturedRemapper, true /*cause all calls in same module as lambda*/, "Lambda inlining " + info.getLambdaClassType().getInternalName()); LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift); InlineResult lambdaResult = inliner.doInline( this.mv, remapper, true, info); // TODO add skipped this and receiver result.addAllClassesToRemove(lambdaResult); // return value boxing/unboxing Method bridge = typeMapper .mapSignature( ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor())) .getAsmMethod(); Method delegate = typeMapper.mapSignature(info.getFunctionDescriptor()).getAsmMethod(); StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this); setLambdaInlining(false); addInlineMarker(this, false); } else if (isAnonymousConstructorCall(owner, name)) { // TODO add method assert anonymousObjectGen != null : "<init> call not corresponds to new call" + owner + " " + name; if (anonymousObjectGen.shouldRegenerate()) { // put additional captured parameters on stack for (CapturedParamDesc capturedParamDesc : anonymousObjectGen.getAllRecapturedParameters()) { visitFieldInsn( Opcodes.GETSTATIC, capturedParamDesc.getContainingLambdaName(), "$$$" + capturedParamDesc.getFieldName(), capturedParamDesc.getType().getDescriptor()); } super.visitMethodInsn( opcode, anonymousObjectGen.getNewLambdaType().getInternalName(), name, anonymousObjectGen.getNewConstructorDescriptor(), itf); anonymousObjectGen = null; } else { super.visitMethodInsn( opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf); } } else if (ReifiedTypeInliner.isNeedClassReificationMarker( new MethodInsnNode(opcode, owner, name, desc, false))) { // we will put it if needed in anew processing } else { super.visitMethodInsn( opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf); } } @Override public void visitFieldInsn( int opcode, @NotNull String owner, @NotNull String name, @NotNull String desc) { if (opcode == Opcodes.GETSTATIC && isAnonymousSingletonLoad(owner, name)) { handleAnonymousObjectGeneration(); } super.visitFieldInsn(opcode, owner, name, desc); } @Override public void visitMaxs(int stack, int locals) { lambdasFinallyBlocks = resultNode.tryCatchBlocks.size(); super.visitMaxs(stack, locals); } }; node.accept(lambdaInliner); return resultNode; }