/** * Creates the print * * @return The print as an ArrayList */ public ArrayList<String> createPrint() { ArrayList<String> info = new ArrayList<String>(); ListIterator<?> it = mNode.instructions.iterator(); boolean firstLabel = false; while (it.hasNext()) { AbstractInsnNode ain = (AbstractInsnNode) it.next(); String line = ""; if (ain instanceof VarInsnNode) { line = printVarInsnNode((VarInsnNode) ain, it); } else if (ain instanceof IntInsnNode) { line = printIntInsnNode((IntInsnNode) ain, it); } else if (ain instanceof FieldInsnNode) { line = printFieldInsnNode((FieldInsnNode) ain, it); } else if (ain instanceof MethodInsnNode) { line = printMethodInsnNode((MethodInsnNode) ain, it); } else if (ain instanceof LdcInsnNode) { line = printLdcInsnNode((LdcInsnNode) ain, it); } else if (ain instanceof InsnNode) { line = printInsnNode((InsnNode) ain, it); } else if (ain instanceof JumpInsnNode) { line = printJumpInsnNode((JumpInsnNode) ain, it); } else if (ain instanceof LineNumberNode) { line = printLineNumberNode((LineNumberNode) ain, it); } else if (ain instanceof LabelNode) { if (firstLabel && Decompiler.BYTECODE .getSettings() .isSelected(ClassNodeDecompiler.Settings.APPEND_BRACKETS_TO_LABELS)) info.add("}"); line = printLabelnode((LabelNode) ain); if (Decompiler.BYTECODE .getSettings() .isSelected(ClassNodeDecompiler.Settings.APPEND_BRACKETS_TO_LABELS)) { if (!firstLabel) firstLabel = true; line += " {"; } } else if (ain instanceof TypeInsnNode) { line = printTypeInsnNode((TypeInsnNode) ain); } else if (ain instanceof FrameNode) { line = ""; } else if (ain instanceof IincInsnNode) { line = printIincInsnNode((IincInsnNode) ain); } else if (ain instanceof TableSwitchInsnNode) { line = printTableSwitchInsnNode((TableSwitchInsnNode) ain); } else if (ain instanceof LookupSwitchInsnNode) { line = printLookupSwitchInsnNode((LookupSwitchInsnNode) ain); } else if (ain instanceof InvokeDynamicInsnNode) { line = printInvokeDynamicInsNode((InvokeDynamicInsnNode) ain); } else { line += "UNADDED OPCODE: " + nameOpcode(ain.opcode()) + " " + ain.toString(); } if (!line.equals("")) { if (match) if (matchedInsns.contains(ain)) line = " -> " + line; info.add(line); } } if (firstLabel && Decompiler.BYTECODE .getSettings() .isSelected(ClassNodeDecompiler.Settings.APPEND_BRACKETS_TO_LABELS)) info.add("}"); return info; }
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; }
// marked return could be either non-local or local in case of labeled lambda self-returns public static boolean isMarkedReturn(@NotNull AbstractInsnNode returnIns) { if (!isReturnOpcode(returnIns.getOpcode())) { return false; } AbstractInsnNode globalFlag = returnIns.getPrevious(); return globalFlag instanceof MethodInsnNode && NON_LOCAL_RETURN.equals(((MethodInsnNode) globalFlag).owner); }
private static boolean isEmptyTryInterval(@NotNull TryCatchBlockNode tryCatchBlockNode) { LabelNode start = tryCatchBlockNode.start; AbstractInsnNode end = tryCatchBlockNode.end; while (end != start && end instanceof LabelNode) { end = end.getPrevious(); } return start == end; }
@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; }
@Override public BasicValue naryOperation(AbstractInsnNode insn, List<? extends BasicValue> values) throws AnalyzerException { int opcode = insn.getOpcode(); if (opcode == MULTIANEWARRAY) { return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc)); } else if (opcode == INVOKEDYNAMIC) { return newValue(Type.getReturnType(((InvokeDynamicInsnNode) insn).desc)); } else { return newValue(Type.getReturnType(((MethodInsnNode) insn).desc)); } }
public static boolean isInlineMarker(AbstractInsnNode insn, String name) { if (insn instanceof MethodInsnNode) { MethodInsnNode methodInsnNode = (MethodInsnNode) insn; return insn.getOpcode() == Opcodes.INVOKESTATIC && methodInsnNode.owner.equals(INLINE_MARKER_CLASS_NAME) && (name != null ? methodInsnNode.name.equals(name) : methodInsnNode.name.equals(INLINE_MARKER_BEFORE_METHOD_NAME) || methodInsnNode.name.equals(INLINE_MARKER_AFTER_METHOD_NAME)); } else { return false; } }
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(); } }
public static int getConstant(AbstractInsnNode ins) { int opcode = ins.getOpcode(); Integer value; if (opcode >= Opcodes.ICONST_0 && opcode <= Opcodes.ICONST_5) { value = opcode - Opcodes.ICONST_0; } else if (opcode == Opcodes.BIPUSH || opcode == Opcodes.SIPUSH) { IntInsnNode index = (IntInsnNode) ins; value = index.operand; } else { LdcInsnNode index = (LdcInsnNode) ins; value = (Integer) index.cst; } return value; }
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; }
@Override public AnnotationVisitor visitInsnAnnotation( int typeRef, TypePath typePath, String desc, boolean visible) { // Finds the last real instruction, i.e. the instruction targeted by // this annotation. AbstractInsnNode insn = instructions.getLast(); while (insn.getOpcode() == -1) { insn = insn.getPrevious(); } // Adds the annotation to this instruction. TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); if (visible) { if (insn.visibleTypeAnnotations == null) { insn.visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1); } insn.visibleTypeAnnotations.add(an); } else { if (insn.invisibleTypeAnnotations == null) { insn.invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1); } insn.invisibleTypeAnnotations.add(an); } return an; }
@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 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; }
@Override public BasicValue binaryOperation( @NotNull AbstractInsnNode insn, @NotNull BasicValue value1, @NotNull BasicValue value2) throws AnalyzerException { if (insn.getOpcode() == Opcodes.AALOAD) { Type arrayType = value1.getType(); if (arrayType != null && arrayType.getSort() == Type.ARRAY) { return new StrictBasicValue(arrayType.getElementType()); } } switch (insn.getOpcode()) { case IALOAD: case BALOAD: case CALOAD: case SALOAD: case IADD: case ISUB: case IMUL: case IDIV: case IREM: case ISHL: case ISHR: case IUSHR: case IAND: case IOR: case IXOR: return StrictBasicValue.INT_VALUE; case FALOAD: case FADD: case FSUB: case FMUL: case FDIV: case FREM: return StrictBasicValue.FLOAT_VALUE; case LALOAD: case LADD: case LSUB: case LMUL: case LDIV: case LREM: case LSHL: case LSHR: case LUSHR: case LAND: case LOR: case LXOR: return StrictBasicValue.LONG_VALUE; case DALOAD: case DADD: case DSUB: case DMUL: case DDIV: case DREM: return StrictBasicValue.DOUBLE_VALUE; case AALOAD: return StrictBasicValue.REFERENCE_VALUE; case LCMP: case FCMPL: case FCMPG: case DCMPL: case DCMPG: return StrictBasicValue.INT_VALUE; case IF_ICMPEQ: case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPGE: case IF_ICMPGT: case IF_ICMPLE: case IF_ACMPEQ: case IF_ACMPNE: case PUTFIELD: return null; default: throw new Error("Internal error."); } }
@Override public BasicValue newOperation(@NotNull AbstractInsnNode insn) throws AnalyzerException { if (insn.getOpcode() == Opcodes.ACONST_NULL) { return newValue(Type.getObjectType("java/lang/Object")); } switch (insn.getOpcode()) { case ACONST_NULL: return newValue(Type.getObjectType("null")); case ICONST_M1: case ICONST_0: case ICONST_1: case ICONST_2: case ICONST_3: case ICONST_4: case ICONST_5: return StrictBasicValue.INT_VALUE; case LCONST_0: case LCONST_1: return StrictBasicValue.LONG_VALUE; case FCONST_0: case FCONST_1: case FCONST_2: return StrictBasicValue.FLOAT_VALUE; case DCONST_0: case DCONST_1: return StrictBasicValue.DOUBLE_VALUE; case BIPUSH: case SIPUSH: return StrictBasicValue.INT_VALUE; case LDC: Object cst = ((LdcInsnNode) insn).cst; if (cst instanceof Integer) { return StrictBasicValue.INT_VALUE; } else if (cst instanceof Float) { return StrictBasicValue.FLOAT_VALUE; } else if (cst instanceof Long) { return StrictBasicValue.LONG_VALUE; } else if (cst instanceof Double) { return StrictBasicValue.DOUBLE_VALUE; } else if (cst instanceof String) { return newValue(Type.getObjectType("java/lang/String")); } else if (cst instanceof Type) { int sort = ((Type) cst).getSort(); if (sort == Type.OBJECT || sort == Type.ARRAY) { return newValue(Type.getObjectType("java/lang/Class")); } else if (sort == Type.METHOD) { return newValue(Type.getObjectType("java/lang/invoke/MethodType")); } else { throw new IllegalArgumentException("Illegal LDC constant " + cst); } } else if (cst instanceof Handle) { return newValue(Type.getObjectType("java/lang/invoke/MethodHandle")); } else { throw new IllegalArgumentException("Illegal LDC constant " + cst); } case GETSTATIC: return newValue(Type.getType(((FieldInsnNode) insn).desc)); case NEW: return newValue(Type.getObjectType(((TypeInsnNode) insn).desc)); default: throw new Error("Internal error."); } }
@Override public BasicValue unaryOperation(AbstractInsnNode insn, BasicValue value) throws AnalyzerException { switch (insn.getOpcode()) { case INEG: case IINC: case L2I: case F2I: case D2I: case I2B: case I2C: case I2S: return StrictBasicValue.INT_VALUE; case FNEG: case I2F: case L2F: case D2F: return StrictBasicValue.FLOAT_VALUE; case LNEG: case I2L: case F2L: case D2L: return StrictBasicValue.LONG_VALUE; case DNEG: case I2D: case L2D: case F2D: return StrictBasicValue.DOUBLE_VALUE; case IFEQ: case IFNE: case IFLT: case IFGE: case IFGT: case IFLE: case TABLESWITCH: case LOOKUPSWITCH: case IRETURN: case LRETURN: case FRETURN: case DRETURN: case ARETURN: case PUTSTATIC: return null; case GETFIELD: return newValue(Type.getType(((FieldInsnNode) insn).desc)); case NEWARRAY: switch (((IntInsnNode) insn).operand) { case T_BOOLEAN: return newValue(Type.getType("[Z")); case T_CHAR: return newValue(Type.getType("[C")); case T_BYTE: return newValue(Type.getType("[B")); case T_SHORT: return newValue(Type.getType("[S")); case T_INT: return newValue(Type.getType("[I")); case T_FLOAT: return newValue(Type.getType("[F")); case T_DOUBLE: return newValue(Type.getType("[D")); case T_LONG: return newValue(Type.getType("[J")); default: throw new AnalyzerException(insn, "Invalid array type"); } case ANEWARRAY: String desc = ((TypeInsnNode) insn).desc; return newValue(Type.getType("[" + Type.getObjectType(desc))); case ARRAYLENGTH: return StrictBasicValue.INT_VALUE; case ATHROW: return null; case CHECKCAST: desc = ((TypeInsnNode) insn).desc; return newValue(Type.getObjectType(desc)); case INSTANCEOF: return StrictBasicValue.INT_VALUE; case MONITORENTER: case MONITOREXIT: case IFNULL: case IFNONNULL: return null; default: throw new Error("Internal error."); } }
private InsnList fixInstructions(MethodNode originalMethod, CloneMap cloneMap) { InsnList instructions = new InsnList(); for (int k = 0; k < originalMethod.instructions.size(); k++) { AbstractInsnNode originalInsn = originalMethod.instructions.get(k); switch (originalInsn.getOpcode()) { // the put on the field granular field is transformed to a fieldref.set case PUTFIELD: { FieldInsnNode fieldInsn = (FieldInsnNode) originalInsn; ClassMetadata ownerMetadata = metadataRepository.loadClassMetadata(classLoader, fieldInsn.owner); FieldMetadata fieldMetadata = ownerMetadata.getFieldMetadata(fieldInsn.name); Type originalFieldType = Type.getType(fieldMetadata.getDesc()); if (fieldMetadata.hasFieldGranularity()) { boolean fieldIsCategory2 = isCategory2(fieldMetadata.getDesc()); if (fieldIsCategory2) { // value(category2), owner,.. instructions.add(new InsnNode(DUP2_X1)); // [value(category2), owner, value(category2),...] instructions.add(new InsnNode(POP2)); // [owner, value(category2), ...] } else { // [value(category1), owner, instructions.add(new InsnNode(SWAP)); // [owner, value(category1),.. } String referenceDesc = findReferenceDesc(fieldMetadata.getDesc()); String referenceName = Type.getType(referenceDesc).getInternalName(); instructions.add( new FieldInsnNode(GETFIELD, fieldInsn.owner, fieldInsn.name, referenceDesc)); if (fieldIsCategory2) { // [owner, value(category2),.. instructions.add(new InsnNode(DUP_X2)); // [owner, value(category2), owner instructions.add(new InsnNode(POP)); // [value(category2), owner } else { // [owner, value(category1) instructions.add(new InsnNode(SWAP)); // [value(category1), owner.. } // call the set if (originalFieldType.getSort() == Type.ARRAY || originalFieldType.getSort() == Type.OBJECT) { String objectDesc = Type.getDescriptor(Object.class); MethodInsnNode methodInsn = new MethodInsnNode( INVOKEVIRTUAL, referenceName, "set", format("(%s)%s", objectDesc, objectDesc)); instructions.add(methodInsn); } else { MethodInsnNode methodInsn = new MethodInsnNode( INVOKEVIRTUAL, referenceName, "set", format("(%s)%s", fieldMetadata.getDesc(), fieldMetadata.getDesc())); instructions.add(methodInsn); } // pop the unused return value of the set. if (fieldIsCategory2) { instructions.add(new InsnNode(POP2)); } else { instructions.add(new InsnNode(POP)); } } else { instructions.add(originalInsn.clone(cloneMap)); } } break; // the get on the field granular field is transformed to a fieldref.get case GETFIELD: { FieldInsnNode fieldInsn = (FieldInsnNode) originalInsn; FieldMetadata fieldMetadata = metadataRepository .loadClassMetadata(classLoader, fieldInsn.owner) .getFieldMetadata(fieldInsn.name); if (!fieldMetadata.hasFieldGranularity()) { // if it is not getter on a field granular field instructions.add(originalInsn.clone(cloneMap)); } else { // it is a getter on a field granular field. String referenceDesc = findReferenceDesc(fieldMetadata.getDesc()); String referenceName = Type.getType(referenceDesc).getInternalName(); // place the fieldref on the stack. instructions.add( new FieldInsnNode(GETFIELD, fieldInsn.owner, fieldInsn.name, referenceDesc)); Type originalFieldType = Type.getType(fieldMetadata.getDesc()); if (originalFieldType.getSort() == Type.ARRAY || originalFieldType.getSort() == Type.OBJECT) { instructions.add( new MethodInsnNode( INVOKEVIRTUAL, referenceName, "get", format("()%s", getDescriptor(Object.class)))); if (!originalFieldType.equals(Type.getType(Object.class))) { instructions.add( new TypeInsnNode(CHECKCAST, originalFieldType.getInternalName())); } } else { instructions.add( new MethodInsnNode( INVOKEVIRTUAL, referenceName, "get", format("()%s", fieldMetadata.getDesc()))); } } } break; default: instructions.add(originalInsn.clone(cloneMap)); break; } } return instructions; }
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; }