// 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 { 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() 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") } } }