Androi Studio 3.0 and later support all Java 7 language features, as well as some Java 8 language features (depending on platform version).

Java 8 language functionality is supported by Desugar in the default toolchain, but the Java 8 language API needs to follow the Android SDK version, and Android 7.0 (SDK 24) starts to provide API support.

Desugar does not currently support methodHandle. invoke and methodHandle. invokeExact. If you want to use these two methods, you need to specify miniSdkVersion 26 or higher.

Retrolambda

Retrolambda transforms lambda expressions into internal class implementations at compile time through some mechanism of the JVM (Premain Agent ASM), but generates classes that result in an increase in the number of methods.

Jack compiler

Google released a new compiler Jack/Jill in Android SDK 21 (Android N 7.0) to build Android applications, but his implementation mechanism is to directly convert Java source code into Dalvik bytecode, not Java bytecode.

Later, Google realized that the implementation cost was too high. Before the Jack compiler, the compile chain of Android applications was:

Java_Source-.Javac.->Java_ByteCode
Java_ByteCode-.dx.->dex
Copy the code

After Jack:

Java_Source-.Jack_Compiler.-dex
Copy the code

Use Jack to compile Java source directly into dex files. Jack was introduced to replace Javac, and Jill’s function is to convert dependent libraries. Aar or. Jar into dex.

With Jack, Google can make more optimizations for Android and avoid copyright disputes with Oracal. Jack is the default toolchain for Android 6.0-8.1, but Google scrapped Jack after Android 8.1

Over time, we realized the cost of switching to Jack was too high for our community when we considered the annotation processors, bytecode analyzers and rewriters impacted.

The reason is simply that it takes a lot of time to build features of the Java language ecosystem, such as annotation processors, bytecode analysis, rewriting, and staking tools, and that the community’s existing third-party libraries based on these infrastructures do not smoothly switch to Jack.

Off topic: APT annotation processor tool

Android does not provide a default annotationProcessor before android-gradle-tools 2.2. Most projects use the third-party open source android-apt. After 2.2, Google began to support annotationProcessor annotationProcessor.

The D8 compiler

The Jack compiler is deprecated and Google introduced the D8 compiler in Android 3.1

graph LR
Java_Source-.Javac.->Java_ByteCode
Java_ByteCode-.D8.->dex
Copy the code

Lambda expressions

public class Java8 {
    public static void main(String[] args) {
        new Thread(()->System.out.println("test")).start(); }}Copy the code
Javac java8.java // compile bytecode dx --dex --output=./ java8.dex java8.class Uncaught translation Error: com.android.dx.cf.code.SimException: ERRORin Java8.main:([Ljava/lang/String;)V: invalid opcode ba - invokedynamic requires --min-sdk-version >= 26 (currently 13)
Copy the code

Lambda expressions use invokedynamic at the Java bytecode level, while Android supports invokedynamic only when SDK version 26 or older. However, Android does not directly use Invokedynamic. Instead, the Dex format was upgraded in Android 8.0 (Sdk 26) to provide dynamic call support through two new Dalvik directives, ekestorage-polymorphic and ekestor-Custom.

See instructions in Dalvik bytecode for more details

Let’s compare the generated bytecode files

Bytecode file

Javap verbose Java8 / / view the bytecode Classfile/Users/gentrio/Desktop/Java8 class Last modified the 2020-7-24; size 1043 bytes MD5 checksum 6ddd92fd44495106d002b3595320fbd5 Compiled from"java8.java"
public class Java8
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref #10.#20 // java/lang/Object."
      
       ":()V
      
   #2 = Class #21 // java/lang/Thread
   #3 = InvokeDynamic #0:#26 // #0:run:()Ljava/lang/Runnable;
   #4 = Methodref #2.#27 // java/lang/Thread."
      
       ":(Ljava/lang/Runnable;) V
      
   #5 = Methodref #2.#28 // java/lang/Thread.start:()V
   #6 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream;
   #7 = String #31 // test
   #8 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/String;) V
   #9 = Class #34 // Java8
  #10 = Class #35 // java/lang/Object
  #11 = Utf8 
      
  #12 = Utf8 ()V
  #13 = Utf8 Code
  #14 = Utf8 LineNumberTable
  #15 = Utf8 main
  #16 = Utf8 ([Ljava/lang/String;)V
  #17 = Utf8 lambda$main$0
  #18 = Utf8 SourceFile
  #19 = Utf8 java8.java
  #20 = NameAndType #11:#12 // "
      
       ":()V
      
  #21 = Utf8 java/lang/Thread
  #22 = Utf8 BootstrapMethods
  #23 = MethodHandle #6:#36 // invokestatic java/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;
  #24 = MethodType #12 // ()V
  #25 = MethodHandle #6:#37 // invokestatic Java8.lambda$main$0:()V
  #26 = NameAndType #38:#39 // run:()Ljava/lang/Runnable;
  #27 = NameAndType #11:#40 // "
      
       ":(Ljava/lang/Runnable;) V
      
  #28 = NameAndType #41:#12 // start:()V
  #29 = Class #42 // java/lang/System
  #30 = NameAndType #43:#44 // out:Ljava/io/PrintStream;
  #31 = Utf8 test
  #32 = Class #45 // java/io/PrintStream
  #33 = NameAndType #46:#47 // println:(Ljava/lang/String;) V
  #34 = Utf8 Java8
  #35 = Utf8 java/lang/Object
  #36 = Methodref #48.#49 // java/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;
  #37 = Methodref #9.#50 // Java8.lambda$main$0:()V
  #38 = Utf8 run
  #39 = Utf8 ()Ljava/lang/Runnable;
  #40 = Utf8 (Ljava/lang/Runnable;) V
  #41 = Utf8 start
  #42 = Utf8 java/lang/System
  #43 = Utf8 out
  #44 = Utf8 Ljava/io/PrintStream;
  #45 = Utf8 java/io/PrintStream
  #46 = Utf8 println
  #47 = Utf8 (Ljava/lang/String;) V
  #48 = Class #51 // java/lang/invoke/LambdaMetafactory
  #49 = NameAndType #52:#56 // 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;
  #50 = NameAndType #17:#12 // lambda$main$0:()V
  #51 = Utf8 java/lang/invoke/LambdaMetafactory
  #52 = Utf8 metafactory
  #53 = Class #58 // java/lang/invoke/MethodHandles$Lookup
  #54 = Utf8 Lookup
  #55 = Utf8 InnerClasses
  #56 = Utf8 (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;
  #57 = Class #59 // java/lang/invoke/MethodHandles
  #58 = Utf8 java/lang/invoke/MethodHandles$Lookup
  #59 = Utf8 java/lang/invoke/MethodHandles
{
  public Java8();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1 // Method java/lang/Object."
      
       ":()V
      
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=1
         0: new           #2 // class java/lang/Thread
         3: dup
         4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
         9: invokespecial #4 // Method java/lang/Thread."
      
       ":(Ljava/lang/Runnable;) V
      
        12: invokevirtual #5 // Method java/lang/Thread.start:()V
        15: return
      LineNumberTable:
        line 4: 0
        line 5: 15
}
SourceFile: "java8.java"
InnerClasses:
     public static final #54= #53 of #57; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #23 invokestatic java/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;
    Method arguments:
      #24 ()V
      #25 invokestatic Java8.lambda$main$0:()V
      #24 ()V
Copy the code

Dex file compiled through D8

Processing 'classes.dex'. Opened'classes.dex', DEX version '035'
Class # 0 -
  Class descriptor  : 'L-? Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM; '
  Access flags      : 0x1011 (PUBLIC FINAL SYNTHETIC)
  Superclass        : 'Ljava/lang/Object; '
  Interfaces        -
    #0 : 'Ljava/lang/Runnable; '
  Static fields     -
    #0 : (in L-? Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM;)
      name          : 'INSTANCE'
      type          : 'L-? Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM; '
      access        : 0x1019 (PUBLIC STATIC FINAL SYNTHETIC)
  Instance fields   -
  Direct methods    -
    #0 : (in L-? Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM;)
      name          : '<clinit>'
      type          : '()V'
      access        : 0x11008 (STATIC SYNTHETIC CONSTRUCTOR)
      code          -
      registers     : 1
      ins           : 0
      outs          : 1
      insns size    : 8 16-bit code units
      catches       : (none)
      positions     :
      locals        :
    #1 : (in L-? Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM;)
      name          : '<init>'
      type          : '()V'
      access        : 0x11002 (PRIVATE SYNTHETIC CONSTRUCTOR)
      code          -
      registers     : 1
      ins           : 1
      outs          : 1
      insns size    : 4 16-bit code units
      catches       : (none)
      positions     :
      locals        :
  Virtual methods   -
    #0 : (in L-? Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM;)
      name          : 'run'
      type          : '()V'
      access        : 0x0011 (PUBLIC FINAL)
      code          -
      registers     : 1
      ins           : 1
      outs          : 0
      insns size    : 4 16-bit code units
      catches       : (none)
      positions     :
      locals        :
  source_file_idx   : 15 (lambda)

Class # 1 -
  Class descriptor  : 'LJava8; '
  Access flags      : 0x0001 (PUBLIC)
  Superclass        : 'Ljava/lang/Object; '
  Interfaces        -
  Static fields     -
  Instance fields   -
  Direct methods    -
    #0 : (in LJava8;)
      name          : '<init>'
      type          : '()V'
      access        : 0x10001 (PUBLIC CONSTRUCTOR)
      code          -
      registers     : 1
      ins           : 1
      outs          : 1
      insns size    : 4 16-bit code units
      catches       : (none)
      positions     :
        0x0000 line=1
      locals        :
        0x0000 - 0x0004 reg=0 this LJava8;
    #1 : (in LJava8;)
      name          : 'lambda$main$0'
      type          : '()V'
      access        : 0x1008 (STATIC SYNTHETIC)
      code          -
      registers     : 2
      ins           : 0
      outs          : 2
      insns size    : 8 16-bit code units
      catches       : (none)
      positions     :
        0x0000 line=4
      locals        :
    #2 : (in LJava8;)
      name          : 'main'
      type          : '([Ljava/lang/String;)V'
      access        : 0x0009 (PUBLIC STATIC)
      code          -
      registers     : 2
      ins           : 1
      outs          : 2
      insns size    : 11 16-bit code units
      catches       : (none)
      positions     :
        0x0000 line=4
        0x000a line=5
      locals        :
  Virtual methods   -
  source_file_idx   : 14 (java8.java)
Copy the code

Specify the dex file compiled by min-sdK-version =26 with dx

dexdump Java8-dx.dex

Processing 'Java8.dex'. Opened'Java8.dex', DEX version '038'
Class # 0 -
  Class descriptor  : 'LJava8; '
  Access flags      : 0x0001 (PUBLIC)
  Superclass        : 'Ljava/lang/Object; '
  Interfaces        -
  Static fields     -
  Instance fields   -
  Direct methods    -
    #0 : (in LJava8;)
      name          : '<init>'
      type          : '()V'
      access        : 0x10001 (PUBLIC CONSTRUCTOR)
      code          -
      registers     : 1
      ins           : 1
      outs          : 1
      insns size    : 4 16-bit code units
      catches       : (none)
      positions     :
        0x0000 line=1
      locals        :
        0x0000 - 0x0004 reg=0 this LJava8;
    #1 : (in LJava8;)
      name          : 'lambda$main$0'
      type          : '()V'
      access        : 0x100a (PRIVATE STATIC SYNTHETIC)
      code          -
      registers     : 2
      ins           : 0
      outs          : 2
      insns size    : 8 16-bit code units
      catches       : (none)
      positions     :
        0x0000 line=4
      locals        :
    #2 : (in LJava8;)
      name          : 'main'
      type          : '([Ljava/lang/String;)V'
      access        : 0x0009 (PUBLIC STATIC)
      code          -
      registers     : 3
      ins           : 1
      outs          : 2
      insns size    : 13 16-bit code units
      catches       : (none)
      positions     :
        0x0000 line=4
        0x000c line=5
      locals        :
  Virtual methods   -
  source_file_idx   : 18 (java8.java)

Method Handle # 0:
  type        : invoke-static
  target      : LJava8; lambda$main$0
  target_type : ()V
Method Handle # 1:
  type        : invoke-static
  target      : Ljava/lang/invoke/LambdaMetafactory; metafactory
  target_type : (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; Call Site#0 // offset 1059
  link_argument[0] : 1 (MethodHandle)
  link_argument[1] : run (String)
  link_argument[2] : ()Ljava/lang/Runnable; (MethodType)
  link_argument[3] : ()V (MethodType)
  link_argument[4] : 0 (MethodHandle)
  link_argument[5] : ()V (MethodType)
Copy the code

Currently, Android uses Desugaring to support Lambda functions on all versions of devices.

Desugaring is the conversion of syntactic features that are not supported by the underlying bytecode to the underlying bytecode structure at compile time. (For example, the desugaring of generic types on List is actually Object at bytecode level;

Java bytecode shows that Lambda is implemented by invokedynamic instructions on Jvm, but not in dex bytecode files. Here are the differences between Jvm and Dalvik:

Invokedynamic instruction

The Invokedynamic directive is a bytecode invocation directive added to Java 7 as part of Java’s enhancements to the dynamically typed language. With invokevirtual, Invokestatic, InvokeInterface, Invokespecial instructions constitute the Virtual machine level Java method allocation call instruction set.

  • The last four instructions, in the class file generated during compilation, through the Constant Pool MethodRef Constant has fixed the symbol information of the target method, virtual machine using symbol information can directly explain the specific method, directly call.
  • However, in the class file generated by invokedynamic instruction during compilation, the symbol information stored by invokedynamic_Info Constant in the Constant Pool does not contain the method description and its type, instead, BoostrapMethod information is used. The name and type of a method are dynamically determined at run time through the BootstrapMethod mechanism.
Constant pool: #1 = Methodref #10.#20 // java/lang/Object."<init>":()V #2 = Class #21 // java/lang/Thread #3 = InvokeDynamic #0:#26 // #0:run:()Ljava/lang/Runnable; #4 = Methodref #2.#27 // java/lang/Thread."<init>":(Ljava/lang/Runnable;) V #5 = Methodref #2.#28 // java/lang/Thread.start:()V #6 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream; #7 = String #31 // test #8 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/String;) V #9 = Class #34 // Java8 #10 = Class #35 // java/lang/Object #11 = Utf8 <init> #12 = Utf8 ()V #13 = Utf8 Code #14 = Utf8 LineNumberTable #15 = Utf8 main #16 = Utf8 ([Ljava/lang/String;)V #17 = Utf8 lambda$main$0 #18 = Utf8 SourceFile #19 = Utf8 java8.java #20 = NameAndType #11:#12 // "<init>":()V #21 = Utf8 java/lang/Thread #22 = Utf8 BootstrapMethods # 23 = MethodHandle # 6: # 36 / / invokestatic............................................................................................................................................................................................. BootstrapMethods: 0: #23 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invo ke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invok e/CallSite; Method arguments: #24 ()V #25 invokestatic Java8.lambda$main$0:()V #24 ()VCopy the code

Analysis of bytecode shows that the invokedynamic invocation steps, Compile time generate the private static methods – > invokedynamic instruction invoking LambdaMetafactory. Metafactory – > LambdaMetafactory. Metafactory in memory dynamically generates a corresponding functional interface instance of Lambda expressions and private static method calls generated in the implementation

Focus should be on LambdametaFactory metafactory, by looking at the Android SDK found that although the Android SDK 7.0 after 24, run Android SDK LambdametaFactory but it is in the air

package java.lang.invoke;

public class LambdaMetafactory {

    public static final int FLAG_SERIALIZABLE = 1 << 0;

    public static final int FLAG_MARKERS = 1 << 1;

    public static final int FLAG_BRIDGES = 1 << 2;

    public static CallSite metafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType)
            throws LambdaConversionException { return null; }

    public static CallSite altMetafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, Object... args)
            throws LambdaConversionException { return null; }}Copy the code

To verify that LambdaMetafactory is not implemented at runtime, try running the dex package with min-sdK-version dx on an Android 10 SDK 29 machine.

adb push Java8.dex /sdcard/
adb shell dalvikvm -cp /sdcard/Java8.dex Java8

Exception in thread "main" java.lang.BootstrapMethodError: Exception from call site #0 bootstrap method
	at Java8.main(java8.java:4)
Caused by: java.lang.ClassCastException: Bootstrap method returned null
	... 1 more
Copy the code

So even if min-sdK-version is set to 26, the package printed by dx will not run. For dx through like javac is through invokedynamic to invoke LambdaMetafactory. Metafactory to realize the lambda dynamic invocation, and at present the latest Android version also does not have the concrete implementation, Currently, the only solution is to support Lambda expressions by desugaring them.

RetroLambda, Jack, and D8 all work in the same way. They all generate instance types of Labmda interfaces, and then call private static methods generated by the concrete implementation, javAC, through the type. The difference between them is the timing of desugarization:

  • RetroLambda is compiled by Transform after javAC and dex before.
  • Jack in the compilation process of Jack processing output is dex, without going through javAC, direct source to dex
  • The output of D8 through D8 compilation process is dex, which occurs after Javac, the concrete Gradle tasktransformDexArchiveWithDexMergerForDebugThe resulting dex is inbuild/intermediates/transforms/dexMergerIn the

Hook Lambda

Methods a

Android Studio 3.1 uses the D8 compiler by default, and the deicing operation cannot find the Hook point in D8. Therefore, by turning off the deicing function of D8, desugarTransform is used for deicing.

Close the d8 sugar function, through the gradle. Properties configuration android. EnableD8. Desugaring = false to shut down.

Method 2

The invokedynamic instruction is used to find the handle of the Lambda method, replace it with the generated Hook method, and then implement the code logic in the InvokeDynamic instruction in the Hook method.

Implementation idea: There are two implementations of the API Tree API or the Visitor API, which is implemented using the visitInvokeDynamicInsn method in MethodVisitor, The Tree API uses MethodNode by accessing the InvokeDynamicInsnNode in Instructions.

This is implemented using the Visitor API, with the following example code:

class CustomClassNode(cv: ClassVisitor) : ClassVisitor(Opcodes.ASM7, cv) {

    private var className: String? = null

    override fun visit(
        version: Int,
        access: Int,
        name: String? , signature:String? , superName:String? , interfaces:Array<out String>? {
        this.className = name
        super.visit(version, access, name, signature, superName, interfaces)
    }

    override fun visitMethod(
        access: Int,
        name: String? , descriptor:String? , signature:String? , exceptions:Array<String>?: MethodVisitor {
        return CustomMethodVisitor(
            this.className,
            cv,
            super.visitMethod(access, name, descriptor, signature, exceptions)
        )
    }
}

class CustomMethodVisitor(private valclassName: String? .private val cv: ClassVisitor, methodVisitor: MethodVisitor) :
    MethodVisitor(Opcodes.ASM7, methodVisitor) {

    override fun visitInvokeDynamicInsn(
        name: String? , descriptor:String? , bootstrapMethodHandle:Handle? .vararg bootstrapMethodArguments: Any?). {
        val argumentList = bootstrapMethodArguments.toMutableList()
        // Functional interface descriptor
        val descriptorType: Type = Type.getType(descriptor)
        // Method parameter descriptor
        val methodType: Type? = argumentList[0] as? Type
        // Implement method parameter descriptors
        val methodImplType: Type? = argumentList[2] as? Type
        // Method description descriptor
        valmethodDesc: String? = methodType? .descriptor// Method signature
        val methodNameAndDesc: String = name + methodDesc
      
	/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- functional interface filter -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

        // Function interface parameter descriptors
        val type: Array<Type> = descriptorType.argumentTypes
        val middleMethodDesc = if(type.isEmpty()) { methodImplType? .descriptor }else {
            "(" + type.joinToString {
                it.descriptor
            } + methodImplType?.descriptor?.replace("("."")}val middleMethodName = "lambdaThe ${'$'}${name}The ${'$'}trace0"

        // invokeDynamic the original MethodHandle
        val oldMethodHandle = argumentList[1] as? Handle
        // The MethodHandle to replace
        val newMethodHandle = Handle(Opcodes.H_INVOKESTATIC, className, middleMethodName, middleMethodDesc, false)
        argumentList[1] = newMethodHandle

        // Generate an intermediate method
        val methodNode =
            MethodNode(Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC, middleMethodName, middleMethodDesc, null.null)
        methodNode.visitCode()
        val opcode = when(oldMethodHandle? .tag) { Opcodes.H_INVOKEINTERFACE -> Opcodes.INVOKEINTERFACE Opcodes.H_INVOKESPECIAL -> Opcodes.INVOKESPECIAL Opcodes.H_NEWINVOKESPECIAL -> { methodNode.visitTypeInsn(Opcodes.NEW, oldMethodHandle.owner) methodNode.visitInsn(Opcodes.DUP) Opcodes.INVOKESPECIAL } Opcodes.H_INVOKESTATIC -> Opcodes.INVOKESTATIC Opcodes.H_INVOKEVIRTUAL -> Opcodes.INVOKEVIRTUALelse -> 0
        }

        val middleMethodType = Type.getType(middleMethodDesc)
        val argumentType = middleMethodType.argumentTypes
        if (argumentType.isNotEmpty()) {
            var loadIndex = 0
            argumentType.forEach {
                methodNode.visitVarInsn(it.getOpcode(Opcodes.ILOAD), loadIndex)
                loadIndex += it.size
            }
        }
      
      	/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - Hook point can be inserted into the custom method -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

        // Invoke the original lambda implementationmethodNode.visitMethodInsn(opcode, oldMethodHandle? .owner, oldMethodHandle? .name, oldMethodHandle?.desc,false)
        val returnType = middleMethodType.returnType
        val returnOpcodes = returnType.getOpcode(Opcodes.IRETURN)
        methodNode.visitInsn(returnOpcodes)
        methodNode.visitEnd()
        methodNode.accept(this.cv)
        super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, *argumentList.toTypedArray())
    }
}
Copy the code

Decompile the original and desugared bytecode

/ / the source code
public class Java8 {
    public Java8(a) {}public static void main(String[] args) {(new Thread(() -> {
            System.out.println("test"); })).start(); }}/ / to take off the sugar after
public class Java8 {
    public Java8(a) {}public static void main(String[] args) {(new Thread(Java8::lambda$run$trace0)).start();
    }

    private static void lambda$run$trace0() {
        lambda$main$0();
    }
}
Copy the code