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; }
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; }
private InlineResult inlineCall(SMAPAndMethodNode nodeAndSmap) { MethodNode node = nodeAndSmap.getNode(); ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node.instructions); generateClosuresBodies(); // through generation captured parameters will be added to invocationParamBuilder putClosureParametersOnStack(); addInlineMarker(codegen.v, true); Parameters parameters = invocationParamBuilder.buildParameters(); InliningContext info = new RootInliningContext( expressionMap, state, codegen.getInlineNameGenerator().subGenerator(functionDescriptor.getName().asString()), codegen.getContext(), callElement, codegen.getParentCodegen().getClassName(), reifiedTypeInliner); MethodInliner inliner = new MethodInliner( node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule, "Method inlining " + callElement.getText(), createNestedSourceMapper(nodeAndSmap)); // with captured LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize); MethodNode adapter = InlineCodegenUtil.createEmptyMethodNode(); // hack to keep linenumber info, otherwise jdi will skip begin of linenumber chain adapter.visitInsn(Opcodes.NOP); InlineResult result = inliner.doInline(adapter, remapper, true, LabelOwner.SKIP_ALL); result.getReifiedTypeParametersUsages().mergeAll(reificationResult); CallableMemberDescriptor descriptor = codegen.getContext().getContextDescriptor(); final Set<String> labels = getDeclarationLabels( DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor); LabelOwner labelOwner = new LabelOwner() { @Override public boolean isMyLabel(@NotNull String name) { return labels.contains(name); } }; List<MethodInliner.PointForExternalFinallyBlocks> infos = MethodInliner.processReturns(adapter, labelOwner, true, null); generateAndInsertFinallyBlocks( adapter, infos, ((StackValue.Local) remapper.remap(parameters.totalSize() + 1).value).index); removeFinallyMarkers(adapter); adapter.accept(new InliningInstructionAdapter(codegen.v)); addInlineMarker(codegen.v, false); return result; }