| // Copyright 2021 Code Intelligence GmbH |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.code_intelligence.jazzer.instrumentor |
| |
| import org.objectweb.asm.Type |
| import java.lang.reflect.Constructor |
| import java.lang.reflect.Executable |
| import java.lang.reflect.Method |
| |
| val Class<*>.descriptor: String |
| get() = Type.getDescriptor(this) |
| |
| val Executable.descriptor: String |
| get() = if (this is Method) { |
| Type.getMethodDescriptor(this) |
| } else { |
| Type.getConstructorDescriptor(this as Constructor<*>?) |
| } |
| |
| internal fun isPrimitiveType(typeDescriptor: String): Boolean { |
| return typeDescriptor in arrayOf("B", "C", "D", "F", "I", "J", "S", "V", "Z") |
| } |
| |
| private fun isPrimitiveType(typeDescriptor: Char) = isPrimitiveType(typeDescriptor.toString()) |
| |
| internal fun getWrapperTypeDescriptor(typeDescriptor: String): String = when (typeDescriptor) { |
| "B" -> "Ljava/lang/Byte;" |
| "C" -> "Ljava/lang/Character;" |
| "D" -> "Ljava/lang/Double;" |
| "F" -> "Ljava/lang/Float;" |
| "I" -> "Ljava/lang/Integer;" |
| "J" -> "Ljava/lang/Long;" |
| "S" -> "Ljava/lang/Short;" |
| "V" -> "Ljava/lang/Void;" |
| "Z" -> "Ljava/lang/Boolean;" |
| else -> typeDescriptor |
| } |
| |
| // Removes the 'L' and ';' prefix/suffix from signatures to get the full class name. |
| // Note that array signatures '[Ljava/lang/String;' already have the correct form. |
| internal fun extractInternalClassName(typeDescriptor: String): String { |
| return if (typeDescriptor.startsWith("L") && typeDescriptor.endsWith(";")) { |
| typeDescriptor.substring(1, typeDescriptor.length - 1) |
| } else { |
| typeDescriptor |
| } |
| } |
| |
| internal fun extractParameterTypeDescriptors(methodDescriptor: String): List<String> { |
| require(methodDescriptor.startsWith('(')) { "Method descriptor must start with '('" } |
| val endOfParameterPart = methodDescriptor.indexOf(')') - 1 |
| require(endOfParameterPart >= 0) { "Method descriptor must contain ')'" } |
| var remainingDescriptorList = methodDescriptor.substring(1..endOfParameterPart) |
| val parameterDescriptors = mutableListOf<String>() |
| while (remainingDescriptorList.isNotEmpty()) { |
| val nextDescriptor = extractNextTypeDescriptor(remainingDescriptorList) |
| parameterDescriptors.add(nextDescriptor) |
| remainingDescriptorList = remainingDescriptorList.removePrefix(nextDescriptor) |
| } |
| return parameterDescriptors |
| } |
| |
| internal fun extractReturnTypeDescriptor(methodDescriptor: String): String { |
| require(methodDescriptor.startsWith('(')) { "Method descriptor must start with '('" } |
| val endBracketPos = methodDescriptor.indexOf(')') |
| require(endBracketPos >= 0) { "Method descriptor must contain ')'" } |
| val startOfReturnValue = endBracketPos + 1 |
| return extractNextTypeDescriptor(methodDescriptor.substring(startOfReturnValue)) |
| } |
| |
| private fun extractNextTypeDescriptor(input: String): String { |
| require(input.isNotEmpty()) { "Type descriptor must not be empty" } |
| // Skip over arbitrarily many '[' to support multi-dimensional arrays. |
| val firstNonArrayPrefixCharPos = input.indexOfFirst { it != '[' } |
| require(firstNonArrayPrefixCharPos >= 0) { "Array descriptor must contain type" } |
| val firstTypeChar = input[firstNonArrayPrefixCharPos] |
| return when { |
| // Primitive type |
| isPrimitiveType(firstTypeChar) -> { |
| input.substring(0..firstNonArrayPrefixCharPos) |
| } |
| // Object type |
| firstTypeChar == 'L' -> { |
| val endOfClassNamePos = input.indexOf(';') |
| require(endOfClassNamePos > 0) { "Class type indicated by L must end with ;" } |
| input.substring(0..endOfClassNamePos) |
| } |
| // Invalid type |
| else -> { |
| throw IllegalArgumentException("Invalid type: $firstTypeChar") |
| } |
| } |
| } |