diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java index d89cb2eab5b..686482e02a4 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -91,6 +91,9 @@ public class JadxCLIArgs { @Parameter(names = { "--no-inline-methods" }, description = "disable methods inline") protected boolean inlineMethods = true; + @Parameter(names = { "--no-inline-kotlin-lambda" }, description = "disable inline for Kotlin lambdas") + protected boolean allowInlineKotlinLambda = true; + @Parameter(names = "--no-finally", description = "don't extract finally block") protected boolean extractFinally = true; @@ -287,6 +290,7 @@ public JadxArgs toJadxArgs() { args.setInsertDebugLines(addDebugLines); args.setInlineAnonymousClasses(inlineAnonymousClasses); args.setInlineMethods(inlineMethods); + args.setAllowInlineKotlinLambda(allowInlineKotlinLambda); args.setExtractFinally(extractFinally); args.setRenameFlags(renameFlags); args.setFsCaseSensitive(fsCaseSensitive); @@ -368,6 +372,10 @@ public boolean isInlineMethods() { return inlineMethods; } + public boolean isAllowInlineKotlinLambda() { + return allowInlineKotlinLambda; + } + public boolean isExtractFinally() { return extractFinally; } diff --git a/jadx-core/src/main/java/jadx/api/JadxArgs.java b/jadx-core/src/main/java/jadx/api/JadxArgs.java index aa94c978f95..8f32e8acd5e 100644 --- a/jadx-core/src/main/java/jadx/api/JadxArgs.java +++ b/jadx-core/src/main/java/jadx/api/JadxArgs.java @@ -53,6 +53,7 @@ public class JadxArgs { private boolean extractFinally = true; private boolean inlineAnonymousClasses = true; private boolean inlineMethods = true; + private boolean allowInlineKotlinLambda = true; private boolean skipResources = false; private boolean skipSources = false; @@ -263,6 +264,14 @@ public void setInlineMethods(boolean inlineMethods) { this.inlineMethods = inlineMethods; } + public boolean isAllowInlineKotlinLambda() { + return allowInlineKotlinLambda; + } + + public void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) { + this.allowInlineKotlinLambda = allowInlineKotlinLambda; + } + public boolean isExtractFinally() { return extractFinally; } diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index e5250e82f62..285f9a63e7e 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -17,6 +17,7 @@ import jadx.api.plugins.input.data.annotations.EncodedValue; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.FieldInitInsnAttr; import jadx.core.dex.attributes.nodes.FieldReplaceAttr; import jadx.core.dex.attributes.nodes.GenericInfoAttr; import jadx.core.dex.attributes.nodes.LoopLabelAttr; @@ -210,7 +211,31 @@ private void instanceField(ICodeWriter code, FieldInfo field, InsnArg arg) throw } } + protected void staticField(ICodeWriter code, FieldInfo field) throws CodegenException { + FieldNode fieldNode = root.resolveField(field); + if (fieldNode != null + && fieldNode.contains(AFlag.INLINE_INSTANCE_FIELD) + && fieldNode.getParentClass().contains(AType.ANONYMOUS_CLASS)) { + FieldInitInsnAttr initInsnAttr = fieldNode.get(AType.FIELD_INIT_INSN); + if (initInsnAttr != null) { + InsnNode insn = initInsnAttr.getInsn(); + if (insn instanceof ConstructorInsn) { + fieldNode.add(AFlag.DONT_GENERATE); + inlineAnonymousConstructor(code, fieldNode.getParentClass(), (ConstructorInsn) insn); + return; + } + } + } + makeStaticFieldAccess(code, field, fieldNode, mgen.getClassGen()); + } + public static void makeStaticFieldAccess(ICodeWriter code, FieldInfo field, ClassGen clsGen) { + FieldNode fieldNode = clsGen.getClassNode().root().resolveField(field); + makeStaticFieldAccess(code, field, fieldNode, clsGen); + } + + private static void makeStaticFieldAccess(ICodeWriter code, + FieldInfo field, @Nullable FieldNode fieldNode, ClassGen clsGen) { ClassInfo declClass = field.getDeclClass(); // TODO boolean fieldFromThisClass = clsGen.getClassNode().getClassInfo().equals(declClass); @@ -221,7 +246,6 @@ public static void makeStaticFieldAccess(ICodeWriter code, FieldInfo field, Clas } code.add('.'); } - FieldNode fieldNode = clsGen.getClassNode().root().resolveField(field); if (fieldNode != null) { code.attachAnnotation(fieldNode); } @@ -232,10 +256,6 @@ public static void makeStaticFieldAccess(ICodeWriter code, FieldInfo field, Clas } } - protected void staticField(ICodeWriter code, FieldInfo field) { - makeStaticFieldAccess(code, field, mgen.getClassGen()); - } - public void useClass(ICodeWriter code, ArgType type) { mgen.getClassGen().useClass(code, type); } @@ -695,9 +715,7 @@ private void filledNewArray(FilledNewArrayNode insn, ICodeWriter code) throws Co private void makeConstructor(ConstructorInsn insn, ICodeWriter code) throws CodegenException { ClassNode cls = mth.root().resolveClass(insn.getClassType()); if (cls != null && cls.isAnonymous() && !fallback) { - cls.ensureProcessed(); inlineAnonymousConstructor(code, cls, insn); - mth.getParentClass().addInlinedClass(cls); return; } if (insn.isSelf()) { @@ -748,6 +766,7 @@ private void makeConstructor(ConstructorInsn insn, ICodeWriter code) throws Code } private void inlineAnonymousConstructor(ICodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException { + cls.ensureProcessed(); if (this.mth.getParentClass() == cls) { cls.remove(AType.ANONYMOUS_CLASS); cls.remove(AFlag.DONT_GENERATE); @@ -786,6 +805,8 @@ private void inlineAnonymousConstructor(ICodeWriter code, ClassNode cls, Constru ClassGen classGen = new ClassGen(cls, mgen.getClassGen().getParentGen()); classGen.setOuterNameGen(mgen.getNameGen()); classGen.addClassBody(code, true); + + mth.getParentClass().addInlinedClass(cls); } private void makeInvoke(InvokeNode insn, ICodeWriter code) throws CodegenException { diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java index 03bd5e73daf..6cb28eaa31d 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java @@ -270,7 +270,7 @@ public void makeSwitch(SwitchRegion sw, ICodeWriter code) throws CodegenExceptio code.startLine('}'); } - private void addCaseKey(ICodeWriter code, InsnArg arg, Object k) { + private void addCaseKey(ICodeWriter code, InsnArg arg, Object k) throws CodegenException { if (k instanceof FieldNode) { FieldNode fn = (FieldNode) k; if (fn.getParentClass().isEnum()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java index 113888f08ee..181577405da 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java @@ -37,7 +37,9 @@ public enum AFlag { SKIP_FIRST_ARG, SKIP_ARG, // skip argument in invoke call NO_SKIP_ARGS, + ANONYMOUS_CONSTRUCTOR, + INLINE_INSTANCE_FIELD, THIS, SUPER, diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/AnonymousClassAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/AnonymousClassAttr.java index 0f34bf10d7d..ad4a08aaaaa 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/AnonymousClassAttr.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/AnonymousClassAttr.java @@ -7,12 +7,19 @@ public class AnonymousClassAttr extends PinnedAttribute { + public enum InlineType { + CONSTRUCTOR, + INSTANCE_FIELD, + } + private final ClassNode outerCls; private final ArgType baseType; + private final InlineType inlineType; - public AnonymousClassAttr(ClassNode outerCls, ArgType baseType) { + public AnonymousClassAttr(ClassNode outerCls, ArgType baseType, InlineType inlineType) { this.outerCls = outerCls; this.baseType = baseType; + this.inlineType = inlineType; } public ClassNode getOuterCls() { @@ -23,6 +30,10 @@ public ArgType getBaseType() { return baseType; } + public InlineType getInlineType() { + return inlineType; + } + @Override public AType getAttrType() { return AType.ANONYMOUS_CLASS; @@ -30,6 +41,6 @@ public AType getAttrType() { @Override public String toString() { - return "AnonymousClass{" + outerCls + ", base: " + baseType + '}'; + return "AnonymousClass{" + outerCls + ", base: " + baseType + ", inline type: " + inlineType + '}'; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java index 4a7eb8b0cea..864d95b00e6 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/AccessInfo.java @@ -1,5 +1,7 @@ package jadx.core.dex.info; +import org.intellij.lang.annotations.MagicConstant; + import jadx.api.plugins.input.data.AccessFlags; import jadx.core.Consts; import jadx.core.utils.exceptions.JadxRuntimeException; @@ -20,10 +22,21 @@ public AccessInfo(int accessFlags, AFType type) { this.type = type; } + @MagicConstant(valuesFromClass = AccessFlags.class) public boolean containsFlag(int flag) { return (accFlags & flag) != 0; } + @MagicConstant(valuesFromClass = AccessFlags.class) + public boolean containsFlags(int... flags) { + for (int flag : flags) { + if ((accFlags & flag) == 0) { + return false; + } + } + return true; + } + public AccessInfo remove(int flag) { if (containsFlag(flag)) { return new AccessInfo(accFlags & ~flag, type); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ExtractFieldInit.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ExtractFieldInit.java index e83fd442164..6ba3fbb67c8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ExtractFieldInit.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ExtractFieldInit.java @@ -384,8 +384,14 @@ private static List getConstructorsList(ClassNode cls) { return list; } - private static void addFieldInitAttr(MethodNode mth, FieldNode field, InsnNode insn) { - InsnNode assignInsn = InsnNode.wrapArg(insn.getArg(0)); + private static void addFieldInitAttr(MethodNode mth, FieldNode field, IndexInsnNode putInsn) { + InsnNode assignInsn; + InsnArg fldArg = putInsn.getArg(0); + if (fldArg.isInsnWrap()) { + assignInsn = ((InsnWrapArg) fldArg).getWrapInsn(); + } else { + assignInsn = InsnNode.wrapArg(fldArg); + } field.addAttr(new FieldInitInsnAttr(mth, assignInsn)); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java index 1b10ac10ef4..cb5350f3ad6 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java @@ -10,9 +10,11 @@ import org.jetbrains.annotations.Nullable; +import jadx.api.plugins.input.data.AccessFlags; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.AnonymousClassAttr; +import jadx.core.dex.attributes.nodes.AnonymousClassAttr.InlineType; import jadx.core.dex.info.AccessInfo; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.ClassNode; @@ -30,6 +32,7 @@ UsageInfoVisitor.class } ) +@SuppressWarnings("BooleanMethodIsAlwaysInverted") public class ProcessAnonymous extends AbstractVisitor { private boolean inlineAnonymousClasses; @@ -64,17 +67,26 @@ private static void markAnonymousClass(ClassNode cls) { if (!canBeAnonymous(cls)) { return; } - MethodNode anonymousConstructor = checkUsage(cls); + MethodNode anonymousConstructor = ListUtils.filterOnlyOne(cls.getMethods(), MethodNode::isConstructor); if (anonymousConstructor == null) { return; } + InlineType inlineType = checkUsage(cls, anonymousConstructor); + if (inlineType == null) { + return; + } ArgType baseType = getBaseType(cls); if (baseType == null) { return; } - ClassNode outerCls = anonymousConstructor.getUseIn().get(0).getParentClass(); + ClassNode outerCls; + if (inlineType == InlineType.INSTANCE_FIELD) { + outerCls = cls.getUseInMth().get(0).getParentClass(); + } else { + outerCls = anonymousConstructor.getUseIn().get(0).getParentClass(); + } outerCls.addInlinedClass(cls); - cls.addAttr(new AnonymousClassAttr(outerCls, baseType)); + cls.addAttr(new AnonymousClassAttr(outerCls, baseType, inlineType)); cls.add(AFlag.DONT_GENERATE); anonymousConstructor.add(AFlag.ANONYMOUS_CONSTRUCTOR); @@ -202,14 +214,11 @@ private static boolean canBeAnonymous(ClassNode cls) { * Checks: * - class have only one constructor which used only once (allow common code for field init) * - methods or fields not used outside (allow only nested inner classes with synthetic usage) + * - if constructor used only in class init check if possible inline by instance field * - * @return anonymous constructor method + * @return decided inline type */ - private static MethodNode checkUsage(ClassNode cls) { - MethodNode ctr = ListUtils.filterOnlyOne(cls.getMethods(), MethodNode::isConstructor); - if (ctr == null) { - return null; - } + private static InlineType checkUsage(ClassNode cls, MethodNode ctr) { if (ctr.getUseIn().size() != 1) { // check if used in common field init in all constructors if (!checkForCommonFieldInit(ctr)) { @@ -219,6 +228,9 @@ private static MethodNode checkUsage(ClassNode cls) { MethodNode ctrUseMth = ctr.getUseIn().get(0); ClassNode ctrUseCls = ctrUseMth.getParentClass(); if (ctrUseCls.equals(cls)) { + if (checkForInstanceFieldUsage(cls, ctr)) { + return InlineType.INSTANCE_FIELD; + } // exclude self usage return null; } @@ -226,6 +238,20 @@ private static MethodNode checkUsage(ClassNode cls) { // exclude usage inside inner classes return null; } + if (!checkMethodsUsage(cls, ctr, ctrUseMth)) { + return null; + } + for (FieldNode field : cls.getFields()) { + for (MethodNode useMth : field.getUseIn()) { + if (badMethodUsage(cls, useMth, field.getAccessFlags())) { + return null; + } + } + } + return InlineType.CONSTRUCTOR; + } + + private static boolean checkMethodsUsage(ClassNode cls, MethodNode ctr, MethodNode ctrUseMth) { for (MethodNode mth : cls.getMethods()) { if (mth == ctr) { continue; @@ -235,18 +261,46 @@ private static MethodNode checkUsage(ClassNode cls) { continue; } if (badMethodUsage(cls, useMth, mth.getAccessFlags())) { - return null; + return false; } } } + return true; + } + + private static boolean checkForInstanceFieldUsage(ClassNode cls, MethodNode ctr) { + MethodNode ctrUseMth = ctr.getUseIn().get(0); + if (!ctrUseMth.getMethodInfo().isClassInit()) { + return false; + } + FieldNode instFld = ListUtils.filterOnlyOne(cls.getFields(), + f -> f.getAccessFlags().containsFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL) + && f.getFieldInfo().getType().equals(cls.getClassInfo().getType())); + if (instFld == null) { + return false; + } + List instFldUseIn = instFld.getUseIn(); + if (instFldUseIn.size() != 2 + || !instFldUseIn.contains(ctrUseMth) // initialized in class init + || !instFldUseIn.containsAll(cls.getUseInMth()) // class used only with this field + ) { + return false; + } + if (!checkMethodsUsage(cls, ctr, ctrUseMth)) { + return false; + } for (FieldNode field : cls.getFields()) { + if (field == instFld) { + continue; + } for (MethodNode useMth : field.getUseIn()) { if (badMethodUsage(cls, useMth, field.getAccessFlags())) { - return null; + return false; } } } - return ctr; + instFld.add(AFlag.INLINE_INSTANCE_FIELD); + return true; } private static boolean badMethodUsage(ClassNode cls, MethodNode useMth, AccessInfo accessFlags) { @@ -297,6 +351,13 @@ private static ArgType getBaseType(ClassNode cls) { if (cls.root().getClsp().isImplements(superCls.getObject(), interfaceType.getObject())) { return superCls; } + if (cls.root().getArgs().isAllowInlineKotlinLambda()) { + if (superCls.getObject().equals("kotlin.jvm.internal.Lambda")) { + // Inline such class with have different semantic: missing 'arity' property. + // For now, it is unclear how it may affect code execution. + return interfaceType; + } + } return null; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java index 37fc056055b..afbe2bb5317 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java @@ -325,7 +325,7 @@ private ArgType replaceAnonymousType(ConstructorInsn ctr) { ClassNode ctrCls = root.resolveClass(ctr.getClassType()); if (ctrCls != null && ctrCls.contains(AFlag.DONT_GENERATE)) { AnonymousClassAttr baseTypeAttr = ctrCls.get(AType.ANONYMOUS_CLASS); - if (baseTypeAttr != null) { + if (baseTypeAttr != null && baseTypeAttr.getInlineType() == AnonymousClassAttr.InlineType.CONSTRUCTOR) { return baseTypeAttr.getBaseType(); } } diff --git a/jadx-core/src/test/java/jadx/tests/integration/inline/TestInstanceLambda.java b/jadx-core/src/test/java/jadx/tests/integration/inline/TestInstanceLambda.java index fce8482a1a5..43e232e4429 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/inline/TestInstanceLambda.java +++ b/jadx-core/src/test/java/jadx/tests/integration/inline/TestInstanceLambda.java @@ -3,12 +3,13 @@ import java.util.List; import java.util.Map; import java.util.function.Function; -import java.util.stream.Collectors; import org.junit.jupiter.api.Test; -import jadx.NotYetImplemented; +import jadx.core.dex.attributes.AFlag; import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.visitors.ProcessAnonymous; +import jadx.core.utils.ListUtils; import jadx.tests.api.SmaliTest; import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; @@ -19,14 +20,17 @@ public class TestInstanceLambda extends SmaliTest { public static class TestCls { public Map test(List list) { - return toMap(list, Lambda.INSTANCE); + return toMap(list, Lambda$1.INSTANCE); } /** * Smali test missing 'T' definition in 'Lambda' + * Note: use '$1' so class looks like generated by compiler and pass check in + * {@link ProcessAnonymous#canBeAnonymous(ClassNode)} */ - private static class Lambda implements Function { - public static final Lambda INSTANCE = new Lambda(); + @SuppressWarnings({ "CheckStyle", "checkstyle:TypeName" }) + private static class Lambda$1 implements Function { + public static final Lambda$1 INSTANCE = new Lambda$1(); @Override public T apply(T t) { @@ -35,12 +39,13 @@ public T apply(T t) { } private static Map toMap(List list, Function valueMap) { - return list.stream().collect(Collectors.toMap(k -> k, valueMap)); + return null; } } @Test public void test() { + useJavaInput(); noDebugInfo(); assertThat(getClassNode(TestCls.class)) .code(); @@ -50,23 +55,23 @@ public void test() { public void testSmaliDisableInline() { args.setInlineAnonymousClasses(false); List classNodes = loadFromSmaliFiles(); - assertThat(searchTestCls(classNodes, "Lambda")) + assertThat(searchTestCls(classNodes, "Lambda$1")) .code() - .containsOne("class Lambda implements Function {"); + .containsOne("class Lambda$1 implements Function {"); assertThat(searchTestCls(classNodes, "TestCls")) .code() - .containsOne("Lambda.INSTANCE"); + .containsOne("Lambda$1.INSTANCE"); } - @NotYetImplemented("Inline lambda by instance field") @Test public void testSmali() { List classNodes = loadFromSmaliFiles(); - assertThat(classNodes) + assertThat(ListUtils.filter(classNodes, c -> !c.contains(AFlag.DONT_GENERATE))) .describedAs("Expect lambda to be inlined") .hasSize(1); assertThat(searchTestCls(classNodes, "TestCls")) .code() - .doesNotContain("Lambda.INSTANCE"); + .doesNotContain("Lambda$1.INSTANCE") + .containsOne("toMap(list, new Function() {"); } } diff --git a/jadx-core/src/test/smali/inline/TestInstanceLambda/Lambda.smali b/jadx-core/src/test/smali/inline/TestInstanceLambda/Lambda.smali index d4554375302..79c83b1b092 100644 --- a/jadx-core/src/test/smali/inline/TestInstanceLambda/Lambda.smali +++ b/jadx-core/src/test/smali/inline/TestInstanceLambda/Lambda.smali @@ -1,4 +1,4 @@ -.class public Linline/Lambda; +.class public Linline/Lambda$1; .super Ljava/lang/Object; .implements Ljava/util/function/Function; @@ -12,13 +12,13 @@ } .end annotation -.field public static final INSTANCE:Linline/Lambda; +.field public static final INSTANCE:Linline/Lambda$1; .method static constructor ()V .registers 1 - new-instance v0, Linline/Lambda; - invoke-direct {v0}, Linline/Lambda;->()V - sput-object v0, Linline/Lambda;->INSTANCE:Linline/Lambda; + new-instance v0, Linline/Lambda$1; + invoke-direct {v0}, Linline/Lambda$1;->()V + sput-object v0, Linline/Lambda$1;->INSTANCE:Linline/Lambda$1; return-void .end method diff --git a/jadx-core/src/test/smali/inline/TestInstanceLambda/TestCls.smali b/jadx-core/src/test/smali/inline/TestInstanceLambda/TestCls.smali index 1e6a2899181..325ab0c0649 100644 --- a/jadx-core/src/test/smali/inline/TestInstanceLambda/TestCls.smali +++ b/jadx-core/src/test/smali/inline/TestInstanceLambda/TestCls.smali @@ -15,18 +15,12 @@ } .end annotation - sget-object v0, Linline/Lambda;->INSTANCE:Linline/Lambda; + sget-object v0, Linline/Lambda$1;->INSTANCE:Linline/Lambda$1; invoke-static {p1, v0}, Linline/TestCls;->toMap(Ljava/util/List;Ljava/util/function/Function;)Ljava/util/Map; move-result-object v0 return-object v0 .end method - -.method private static synthetic lambda$toMap$0(Ljava/lang/Object;)Ljava/lang/Object; - .registers 1 - return-object p0 -.end method - .method private static toMap(Ljava/util/List;Ljava/util/function/Function;)Ljava/util/Map; .registers 4 .annotation system Ldalvik/annotation/Signature; @@ -43,14 +37,6 @@ } .end annotation - invoke-interface {p0}, Ljava/util/List;->stream()Ljava/util/stream/Stream; - move-result-object v0 - invoke-custom {}, call_site_0("apply", ()Ljava/util/function/Function;, (Ljava/lang/Object;)Ljava/lang/Object;, invoke-static@Linline/TestCls;->lambda$toMap$0(Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Object;)Ljava/lang/Object;)@Ljava/lang/invoke/LambdaMetafactory;->metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; - move-result-object v1 - invoke-static {v1, p1}, Ljava/util/stream/Collectors;->toMap(Ljava/util/function/Function;Ljava/util/function/Function;)Ljava/util/stream/Collector; - move-result-object v1 - invoke-interface {v0, v1}, Ljava/util/stream/Stream;->collect(Ljava/util/stream/Collector;)Ljava/lang/Object; - move-result-object v0 - check-cast v0, Ljava/util/Map; + const/4 v0, 0x0 return-object v0 .end method diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java index 9876e2d2d69..6a8359a5d46 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java @@ -401,6 +401,10 @@ public void setInlineMethods(boolean inlineMethods) { this.inlineMethods = inlineMethods; } + public void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) { + this.allowInlineKotlinLambda = allowInlineKotlinLambda; + } + public void setExtractFinally(boolean extractFinally) { this.extractFinally = extractFinally; } diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java index c1702ecd604..ab748e02958 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java @@ -538,6 +538,13 @@ private SettingsGroup makeDecompilationGroup() { needReload(); }); + JCheckBox inlineKotlinLambdas = new JCheckBox(); + inlineKotlinLambdas.setSelected(settings.isAllowInlineKotlinLambda()); + inlineKotlinLambdas.addItemListener(e -> { + settings.setAllowInlineKotlinLambda(e.getStateChange() == ItemEvent.SELECTED); + needReload(); + }); + JCheckBox extractFinally = new JCheckBox(); extractFinally.setSelected(settings.isExtractFinally()); extractFinally.addItemListener(e -> { @@ -581,6 +588,7 @@ private SettingsGroup makeDecompilationGroup() { other.addRow(NLS.str("preferences.useDebugInfo"), useDebugInfo); other.addRow(NLS.str("preferences.inlineAnonymous"), inlineAnonymous); other.addRow(NLS.str("preferences.inlineMethods"), inlineMethods); + other.addRow(NLS.str("preferences.inlineKotlinLambdas"), inlineKotlinLambdas); other.addRow(NLS.str("preferences.extractFinally"), extractFinally); other.addRow(NLS.str("preferences.fsCaseSensitive"), fsCaseSensitive); other.addRow(NLS.str("preferences.useDx"), useDx); diff --git a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties index c2d0c5d6110..8b0290c4462 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties @@ -158,6 +158,7 @@ preferences.useImports=Import statements generieren preferences.useDebugInfo=Debug-Infos verwenden preferences.inlineAnonymous=Anonyme Inline-Klassen preferences.inlineMethods=Inline-Methoden +#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas #preferences.extractFinally=Extract finally block preferences.fsCaseSensitive=Dateisystem unterscheidet zwischen Groß/Kleinschreibung preferences.skipResourcesDecode=Keine Ressourcen dekodieren diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties index f0db06ce5b3..acc8aa8fd11 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -158,6 +158,7 @@ preferences.useImports=Use import statements preferences.useDebugInfo=Use debug info preferences.inlineAnonymous=Inline anonymous classes preferences.inlineMethods=Inline methods +preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas preferences.extractFinally=Extract finally block preferences.fsCaseSensitive=File system is case-sensitive preferences.skipResourcesDecode=Don't decode resources diff --git a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties index 3c0bb25f228..207efbb4f89 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -158,6 +158,7 @@ preferences.replaceConsts=Reemplazar constantes #preferences.useDebugInfo=Use debug info #preferences.inlineAnonymous= #preferences.inlineMethods=Inline methods +#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas #preferences.extractFinally=Extract finally block #preferences.fsCaseSensitive= preferences.skipResourcesDecode=No descodificar recursos diff --git a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties index e1834ae04fd..ad759e0f00f 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties @@ -158,6 +158,7 @@ preferences.useImports=import 문 사용 preferences.useDebugInfo=디버그 정보 사용 preferences.inlineAnonymous=인라인 익명 클래스 preferences.inlineMethods=인라인 메서드 +#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas preferences.extractFinally=finally 블록 추출 preferences.fsCaseSensitive=파일 시스템 대소문자 구별 preferences.skipResourcesDecode=리소스 디코딩 하지 않기 diff --git a/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties b/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties index 260b937c685..2eb132016d4 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties @@ -158,6 +158,7 @@ preferences.useImports=Utilizar declaração de imports preferences.useDebugInfo=Utilizar informação de depuração preferences.inlineAnonymous=Classes anônimas de uma linha preferences.inlineMethods=Métodos de uma linha +#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas preferences.extractFinally=Extrair blocos finally preferences.fsCaseSensitive=Sistema de arquivo diferencia maiúsculas de minúsculas preferences.skipResourcesDecode=Não decodificar recursos diff --git a/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties b/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties index 487ce468a9c..9b7d17a7c0f 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties @@ -158,6 +158,7 @@ preferences.useImports=Использовать импорты preferences.useDebugInfo=Отладочная информация preferences.inlineAnonymous=Объединять анонимные классы preferences.inlineMethods=Объединять методы +#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas preferences.extractFinally=Вычленять finally блоки preferences.fsCaseSensitive=Учитывать регистр в файловой системе preferences.skipResourcesDecode=Не декодировать ресурсы diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties index 4beea1cf9cb..25ff76ccd53 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -158,6 +158,7 @@ preferences.useImports=使用 import 语句 preferences.useDebugInfo=启用调试信息 preferences.inlineAnonymous=内联匿名类 preferences.inlineMethods=内联方法 +#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas preferences.extractFinally=提取finally块 preferences.fsCaseSensitive=文件系统区分大小写 preferences.skipResourcesDecode=不反编译资源文件 diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties index fa787b22aa9..2284708ed8a 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties @@ -158,6 +158,7 @@ preferences.useImports=使用 import 陳述式 preferences.useDebugInfo=使用除錯資訊 preferences.inlineAnonymous=內嵌匿名類別 preferences.inlineMethods=內嵌方式 +#preferences.inlineKotlinLambdas=Allow to inline Kotlin Lambdas preferences.extractFinally=擷取 finally 區塊 preferences.fsCaseSensitive=檔案系統區分大小寫 preferences.skipResourcesDecode=不要為資源解碼