public LambdaInfo getLambdaIfExists(AbstractInsnNode insnNode) { if (insnNode.getOpcode() == Opcodes.ALOAD) { int varIndex = ((VarInsnNode) insnNode).var; if (varIndex < parameters.totalSize()) { return parameters.get(varIndex).getLambda(); } } else if (insnNode instanceof FieldInsnNode) { FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode; if (fieldInsnNode.name.startsWith("$$$")) { return findCapturedField(fieldInsnNode, nodeRemapper).getLambda(); } } return null; }
@NotNull public MethodNode prepareNode(@NotNull MethodNode node) { final int capturedParamsSize = parameters.getCaptured().size(); final int realParametersSize = parameters.getReal().size(); Type[] types = Type.getArgumentTypes(node.desc); Type returnType = Type.getReturnType(node.desc); ArrayList<Type> capturedTypes = parameters.getCapturedTypes(); Type[] allTypes = ArrayUtil.mergeArrays(types, capturedTypes.toArray(new Type[capturedTypes.size()])); node.instructions.resetLabels(); MethodNode transformedNode = new MethodNode( InlineCodegenUtil.API, node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null) { private final boolean isInliningLambda = nodeRemapper.isInsideInliningLambda(); private int getNewIndex(int var) { return var + (var < realParametersSize ? 0 : capturedParamsSize); } @Override public void visitVarInsn(int opcode, int var) { super.visitVarInsn(opcode, getNewIndex(var)); } @Override public void visitIincInsn(int var, int increment) { super.visitIincInsn(getNewIndex(var), increment); } @Override public void visitMaxs(int maxStack, int maxLocals) { super.visitMaxs(maxStack, maxLocals + capturedParamsSize); } @Override public void visitLineNumber(int line, @NotNull Label start) { if (isInliningLambda) { super.visitLineNumber(line, start); } } @Override public void visitLocalVariable( @NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index) { if (isInliningLambda) { super.visitLocalVariable(name, desc, signature, start, end, getNewIndex(index)); } } }; node.accept(transformedNode); transformCaptured(transformedNode); return transformedNode; }
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; }