1 /*
<lambda>null2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.xts.apimapper.asm
18
19 import org.objectweb.asm.MethodVisitor
20 import org.objectweb.asm.Opcodes
21 import org.objectweb.asm.Type
22
23 /** Name of the class initializer method. */
24 const val CLASS_INITIALIZER_NAME = "<clinit>"
25
26 /** Descriptor of the class initializer methods. */
27 const val CLASS_INITIALIZER_DESC = "()V"
28
29 /** Descriptor of the constructor. */
30 const val CTOR_DESC = "()V"
31
32 /** Name of the constructor. */
33 const val CTOR_NAME = "<init>"
34
35 /** Convert the class name to jvm format. */
36 fun String.toJvmClassName(): String = replace('.', '/')
37
38 /** Convert the class name from jvm format. */
39 fun String.toHumanReadableClassName() = replace(Regex("[/$]"), ".")
40
41 /** Convert method params from jvm format. */
42 fun String.toHumanReadableDesc(): String {
43 val params = ArrayList<String>()
44 Type.getArgumentTypes(this).forEach {
45 params.add(it.className.toHumanReadableClassName())
46 }
47 return params.joinToString(", ")
48 }
49
50 /** Extract the class name from a .class file. */
zipEntryNameToClassNamenull51 fun zipEntryNameToClassName(entryFilename: String): String? {
52 val suffix = ".class"
53 return if (entryFilename.endsWith(suffix)) {
54 entryFilename.removeSuffix(suffix)
55 } else {
56 null
57 }
58 }
59
60 /**
61 * Write bytecode to "RETURN" that matches the method's return type, according to
62 * [methodDescriptor].
63 */
writeByteCodeToReturnnull64 fun writeByteCodeToReturn(methodDescriptor: String, writer: MethodVisitor) {
65 Type.getReturnType(methodDescriptor).let { type ->
66 // See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions
67 when (type) {
68 Type.VOID_TYPE -> writer.visitInsn(Opcodes.RETURN)
69 Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE
70 -> writer.visitInsn(Opcodes.IRETURN)
71 Type.FLOAT_TYPE -> writer.visitInsn(Opcodes.FRETURN)
72 Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN)
73 Type.DOUBLE_TYPE -> writer.visitInsn(Opcodes.DRETURN)
74 else -> writer.visitInsn(Opcodes.ARETURN)
75 }
76 }
77 }
78
79 /** Given a method descriptor, insert an [argType] as the first argument to it. */
prependArgTypeToMethodDescriptornull80 fun prependArgTypeToMethodDescriptor(methodDescriptor: String, argType: Type): String {
81 val returnType = Type.getReturnType(methodDescriptor)
82 val argTypes = Type.getArgumentTypes(methodDescriptor).toMutableList()
83 argTypes.add(0, argType)
84 return Type.getMethodDescriptor(returnType, *argTypes.toTypedArray())
85 }
86
87 /**
88 * Write bytecode to push all the method arguments to the stack. The number of arguments and their
89 * type are taken from [methodDesc].
90 */
writeByteCodeToPushArgumentsnull91 fun writeByteCodeToPushArguments(
92 methodDesc: String,
93 mv: MethodVisitor,
94 offset: Int,
95 ) {
96 var i = offset
97 Type.getArgumentTypes(methodDesc).forEach { type ->
98 // See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions
99 // Long and Double will consume two local variable spaces.
100 when (type) {
101 Type.VOID_TYPE -> throw AsmGenException("VOID_TYPE not expected")
102 Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE
103 -> mv.visitVarInsn(Opcodes.ILOAD, i)
104 Type.FLOAT_TYPE -> mv.visitVarInsn(Opcodes.FLOAD, i)
105 Type.LONG_TYPE -> mv.visitVarInsn(Opcodes.LLOAD, i++)
106 Type.DOUBLE_TYPE -> mv.visitVarInsn(Opcodes.DLOAD, i++)
107 else -> mv.visitVarInsn(Opcodes.ALOAD, i)
108 }
109 i++
110 }
111 }
112