private static void removeClosureAssertions(MethodNode node) { AbstractInsnNode cur = node.instructions.getFirst(); while (cur != null && cur.getNext() != null) { AbstractInsnNode next = cur.getNext(); if (next.getType() == AbstractInsnNode.METHOD_INSN) { MethodInsnNode methodInsnNode = (MethodInsnNode) next; if (methodInsnNode.name.equals("checkParameterIsNotNull") && methodInsnNode.owner.equals(IntrinsicMethods.INTRINSICS_CLASS_NAME)) { AbstractInsnNode prev = cur.getPrevious(); assert cur.getOpcode() == Opcodes.LDC : "checkParameterIsNotNull should go after LDC but " + cur; assert prev.getOpcode() == Opcodes.ALOAD : "checkParameterIsNotNull should be invoked on local var but " + prev; node.instructions.remove(prev); node.instructions.remove(cur); cur = next.getNext(); node.instructions.remove(next); next = cur; } } cur = next; } }
private boolean scanDecrypter(MethodNode decryptermethodnode, int newHashCode) { InsnList iList = decryptermethodnode.instructions; AbstractInsnNode insn = null, removeInsn = null; for (AbstractInsnNode i : iList.toArray()) { if (i instanceof MethodInsnNode) { MethodInsnNode methodi = ((MethodInsnNode) i); if ("currentThread".equals(methodi.name)) { // find code form this instruction insn = i; break; } } } if (insn == null) { return false; } while (insn != null) { if (insn instanceof MethodInsnNode) { MethodInsnNode methodi = ((MethodInsnNode) insn); if ("hashCode".equals(methodi.name)) { // to this instruction break; } } removeInsn = insn; insn = insn.getNext(); iList.remove(removeInsn); // and remove it } if (insn == null) return false; iList.set(insn, new LdcInsnNode(newHashCode)); // then replace it with pre-computed key LDC return true; }
@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; }
@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; }
private void transformCaptured(@NotNull MethodNode node) { if (nodeRemapper.isRoot()) { return; } // Fold all captured variable chain - ALOAD 0 ALOAD this$0 GETFIELD $captured - to GETFIELD // $$$$captured // On future decoding this field could be inline or unfolded in another field access chain (it // can differ in some missed this$0) AbstractInsnNode cur = node.instructions.getFirst(); while (cur != null) { if (cur instanceof VarInsnNode && cur.getOpcode() == Opcodes.ALOAD) { if (((VarInsnNode) cur).var == 0) { List<AbstractInsnNode> accessChain = getCapturedFieldAccessChain((VarInsnNode) cur); AbstractInsnNode insnNode = nodeRemapper.foldFieldAccessChainIfNeeded(accessChain, node); if (insnNode != null) { cur = insnNode; } } } cur = cur.getNext(); } }
@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; }