@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 List<CapturedParamInfo> extractParametersMappingAndPatchConstructor( @NotNull MethodNode constructor, @NotNull ParametersBuilder capturedParamBuilder, @NotNull ParametersBuilder constructorParamBuilder, @NotNull final AnonymousObjectGeneration anonymousObjectGen, @NotNull FieldRemapper parentFieldRemapper) { CapturedParamOwner owner = new CapturedParamOwner() { @Override public Type getType() { return Type.getObjectType(anonymousObjectGen.getOwnerInternalName()); } }; Set<LambdaInfo> capturedLambdas = new LinkedHashSet<LambdaInfo>(); // captured var of inlined parameter List<CapturedParamInfo> constructorAdditionalFakeParams = new ArrayList<CapturedParamInfo>(); Map<Integer, LambdaInfo> indexToLambda = anonymousObjectGen.getLambdasToInline(); Set<Integer> capturedParams = new HashSet<Integer>(); // load captured parameters and patch instruction list (NB: there is also could be object // fields) AbstractInsnNode cur = constructor.instructions.getFirst(); while (cur != null) { if (cur instanceof FieldInsnNode) { FieldInsnNode fieldNode = (FieldInsnNode) cur; String fieldName = fieldNode.name; if (fieldNode.getOpcode() == Opcodes.PUTFIELD && InlineCodegenUtil.isCapturedFieldName(fieldName)) { boolean isPrevVarNode = fieldNode.getPrevious() instanceof VarInsnNode; boolean isPrevPrevVarNode = isPrevVarNode && fieldNode.getPrevious().getPrevious() instanceof VarInsnNode; if (isPrevPrevVarNode) { VarInsnNode node = (VarInsnNode) fieldNode.getPrevious().getPrevious(); if (node.var == 0) { VarInsnNode previous = (VarInsnNode) fieldNode.getPrevious(); int varIndex = previous.var; LambdaInfo lambdaInfo = indexToLambda.get(varIndex); String newFieldName = isThis0(fieldName) && shouldRenameThis0(parentFieldRemapper, indexToLambda.values()) ? getNewFieldName(fieldName, true) : fieldName; CapturedParamInfo info = capturedParamBuilder.addCapturedParam( owner, fieldName, newFieldName, Type.getType(fieldNode.desc), lambdaInfo != null, null); if (lambdaInfo != null) { info.setLambda(lambdaInfo); capturedLambdas.add(lambdaInfo); } constructorAdditionalFakeParams.add(info); capturedParams.add(varIndex); constructor.instructions.remove(previous.getPrevious()); constructor.instructions.remove(previous); AbstractInsnNode temp = cur; cur = cur.getNext(); constructor.instructions.remove(temp); continue; } } } } cur = cur.getNext(); } constructorParamBuilder.addThis(oldObjectType, false); String constructorDesc = anonymousObjectGen.getConstructorDesc(); if (constructorDesc == null) { // in case of anonymous object with empty closure constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE); } Type[] types = Type.getArgumentTypes(constructorDesc); for (Type type : types) { LambdaInfo info = indexToLambda.get(constructorParamBuilder.getNextValueParameterIndex()); ParameterInfo parameterInfo = constructorParamBuilder.addNextParameter(type, info != null, null); parameterInfo.setLambda(info); if (capturedParams.contains(parameterInfo.getIndex())) { parameterInfo.setCaptured(true); } else { // otherwise it's super constructor parameter } } // For all inlined lambdas add their captured parameters // TODO: some of such parameters could be skipped - we should perform additional analysis Map<String, LambdaInfo> capturedLambdasToInline = new HashMap<String, LambdaInfo>(); // captured var of inlined parameter List<CapturedParamDesc> allRecapturedParameters = new ArrayList<CapturedParamDesc>(); boolean addCapturedNotAddOuter = parentFieldRemapper.isRoot() || (parentFieldRemapper instanceof InlinedLambdaRemapper && parentFieldRemapper.getParent().isRoot()); Map<String, CapturedParamInfo> alreadyAdded = new HashMap<String, CapturedParamInfo>(); for (LambdaInfo info : capturedLambdas) { if (addCapturedNotAddOuter) { for (CapturedParamDesc desc : info.getCapturedVars()) { String key = desc.getFieldName() + "$$$" + desc.getType().getClassName(); CapturedParamInfo alreadyAddedParam = alreadyAdded.get(key); CapturedParamInfo recapturedParamInfo = capturedParamBuilder.addCapturedParam( desc, alreadyAddedParam != null ? alreadyAddedParam.getNewFieldName() : getNewFieldName(desc.getFieldName(), false)); StackValue composed = StackValue.field( desc.getType(), oldObjectType, /*TODO owner type*/ recapturedParamInfo.getNewFieldName(), false, StackValue.LOCAL_0); recapturedParamInfo.setRemapValue(composed); allRecapturedParameters.add(desc); constructorParamBuilder .addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()) .setRemapValue(composed); if (alreadyAddedParam != null) { recapturedParamInfo.setSkipInConstructor(true); } if (isThis0(desc.getFieldName())) { alreadyAdded.put(key, recapturedParamInfo); } } } capturedLambdasToInline.put(info.getLambdaClassType().getInternalName(), info); } if (parentFieldRemapper instanceof InlinedLambdaRemapper && !capturedLambdas.isEmpty() && !addCapturedNotAddOuter) { // lambda with non InlinedLambdaRemapper already have outer FieldRemapper parent = parentFieldRemapper.getParent(); assert parent instanceof RegeneratedLambdaFieldRemapper; final Type ownerType = Type.getObjectType(parent.getLambdaInternalName()); CapturedParamDesc desc = new CapturedParamDesc( new CapturedParamOwner() { @Override public Type getType() { return ownerType; } }, InlineCodegenUtil.THIS, ownerType); CapturedParamInfo recapturedParamInfo = capturedParamBuilder.addCapturedParam( desc, InlineCodegenUtil.THIS$0 /*outer lambda/object*/); StackValue composed = StackValue.LOCAL_0; recapturedParamInfo.setRemapValue(composed); allRecapturedParameters.add(desc); constructorParamBuilder .addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()) .setRemapValue(composed); } anonymousObjectGen.setAllRecapturedParameters(allRecapturedParameters); anonymousObjectGen.setCapturedLambdasToInline(capturedLambdasToInline); return constructorAdditionalFakeParams; }