|
27 | 27 | package org.jetbrains.projector.agent.common
|
28 | 28 |
|
29 | 29 | import javassist.*
|
| 30 | +import org.jetbrains.projector.util.loading.ProjectorClassLoader |
30 | 31 | import org.jetbrains.projector.util.loading.UseProjectorLoader
|
| 32 | +import java.lang.IllegalArgumentException |
| 33 | +import java.lang.reflect.Constructor |
| 34 | +import java.lang.reflect.Method |
| 35 | +import java.lang.reflect.Modifier |
| 36 | +import kotlin.reflect.KFunction |
| 37 | +import kotlin.reflect.full.createType |
| 38 | +import kotlin.reflect.jvm.javaConstructor |
| 39 | +import kotlin.reflect.jvm.javaMethod |
31 | 40 |
|
32 | 41 | internal fun getClassFromClassfileBuffer(pool: ClassPool, className: String, classfileBuffer: ByteArray): CtClass {
|
33 | 42 | pool.insertClassPath(ByteArrayClassPath(className, classfileBuffer))
|
34 | 43 | return pool.get(className).apply(CtClass::defrost)
|
35 | 44 | }
|
36 | 45 |
|
37 |
| -private val currentClassPool: ClassPool by lazy { ClassPool().apply { appendClassPath(LoaderClassPath(object {}.javaClass.classLoader)) } } |
| 46 | +public val projectorClassPool: ClassPool by lazy { ClassPool().apply { appendClassPath(LoaderClassPath(ProjectorClassLoader.instance)) } } |
38 | 47 |
|
39 | 48 | private fun CtClass.getDeclaredMethodImpl(name: String, classPool: ClassPool, params: Array<out Class<*>>): CtMethod =
|
40 | 49 | getDeclaredMethod(name, params.map { classPool[it.name] }.toTypedArray())
|
41 | 50 |
|
42 | 51 | public fun CtClass.getDeclaredMethod(name: String, vararg params: Class<*>): CtMethod =
|
43 |
| - getDeclaredMethodImpl(name, currentClassPool, params) |
| 52 | + getDeclaredMethodImpl(name, projectorClassPool, params) |
| 53 | + |
| 54 | +private fun CtClass.getDeclaredConstructorImpl(classPool: ClassPool, params: Array<out Class<*>>): CtConstructor = |
| 55 | + getDeclaredConstructor(params.map { classPool[it.name] }.toTypedArray()) |
| 56 | + |
| 57 | +public fun CtClass.getDeclaredConstructor(vararg params: Class<*>): CtConstructor = |
| 58 | + getDeclaredConstructorImpl(projectorClassPool, params) |
| 59 | + |
| 60 | +public inline operator fun <reified T> ClassPool.invoke(): CtClass = this[T::class.java.name] |
| 61 | + |
| 62 | +public fun Method.toGetDeclaredMethodFormat(): String { |
| 63 | + |
| 64 | + //val parameterClasses = parameterTypes.joinToString(", ") { "${it.kotlin.javaObjectType.name}.class" } |
| 65 | + val parameterClasses = parameterTypes.joinToString(", ") { "${it.name}.class" } |
| 66 | + val parametersString = "new Class[] { $parameterClasses }" |
| 67 | + |
| 68 | + return "\"$name\", $parametersString" |
| 69 | +} |
| 70 | + |
| 71 | +public fun Constructor<*>.toGetDeclaredMethodFormat(): String { |
| 72 | + val parameterClasses = parameterTypes.joinToString(", ") { "${it.name}.class" } |
| 73 | + return "new Class[] { $parameterClasses }" |
| 74 | +} |
| 75 | + |
| 76 | +// language=java prefix="class DummyClass { void dummyMethod() {" suffix="}}" |
| 77 | +public inline fun <reified T> loadClassWithProjectorLoader(): String = loadClassWithProjectorLoader(T::class.java) |
| 78 | + |
| 79 | +public fun loadClassWithProjectorLoader(clazz: Class<*>): String = loadClassWithProjectorLoader(clazz.name, true) |
| 80 | + |
| 81 | +private fun loadClassWithProjectorLoader(className: String, trim: Boolean): String = """ |
| 82 | + $commonClassLoadCode |
| 83 | + .loadClass("$className") |
| 84 | + """.let { if (!trim) it.trimIndent() else it } |
| 85 | + |
| 86 | +private val unitType by lazy { Unit::class.createType() } |
| 87 | + |
| 88 | +public fun <T : KFunction<*>> T.getJavaCallString( |
| 89 | + vararg params: String, |
| 90 | + finishedExpression: Boolean = this.returnType == unitType, |
| 91 | + autoCast: Boolean = true, |
| 92 | +): String { |
| 93 | + |
| 94 | + val mainPart = when { |
| 95 | + javaMethod != null -> getJavaCallString(javaMethod!!, autoCast, *params) |
| 96 | + javaConstructor != null -> getJavaCallString(javaConstructor!!, autoCast, *params) |
| 97 | + else -> throw IllegalArgumentException("Cannot convert Kotlin function $this to Java method") |
| 98 | + } |
| 99 | + |
| 100 | + return if (finishedExpression) |
| 101 | + """ |
| 102 | + { |
| 103 | + $mainPart; |
| 104 | + } |
| 105 | + """.trimIndent() |
| 106 | + else mainPart |
| 107 | +} |
| 108 | + |
| 109 | +private fun getJavaCallString(asJavaMethod: Method, cast: Boolean = true, vararg params: String): String { |
| 110 | + |
| 111 | + val isStatic = Modifier.isStatic(asJavaMethod.modifiers) |
| 112 | + val instance = if (isStatic) "null" else params.first() |
| 113 | + val otherParams = if (isStatic) params.toList() else params.drop(1) |
| 114 | + |
| 115 | + require(otherParams.size == asJavaMethod.parameterCount) { "Cannot create Java method call string: expected ${asJavaMethod.parameterCount} parameters, got ${otherParams.size}" } |
| 116 | + |
| 117 | + val castString = if (!cast || asJavaMethod.returnType == Void.TYPE) "" else "(${asJavaMethod.returnType.objectType.name})" |
| 118 | + |
| 119 | + return """ |
| 120 | + ($castString ${loadClassWithProjectorLoader(asJavaMethod.declaringClass.name, false)} |
| 121 | + .getDeclaredMethod(${asJavaMethod.toGetDeclaredMethodFormat()}) |
| 122 | + .invoke($instance, new Object[] { ${otherParams.joinToString(", ")} })) |
| 123 | + """.trimIndent() |
| 124 | +} |
| 125 | + |
| 126 | +private fun getJavaCallString(asJavaCtor: Constructor<*>, cast: Boolean = true, vararg params: String): String { |
| 127 | + require(params.size == asJavaCtor.parameterCount) { "Cannot create Java method call string: expected ${asJavaCtor.parameterCount} parameters, got ${params.size}" } |
| 128 | + |
| 129 | + val castString = if (!cast) "" else "(${asJavaCtor.declaringClass.name})" |
| 130 | + |
| 131 | + return """ |
| 132 | + ($castString ${loadClassWithProjectorLoader(asJavaCtor.declaringClass)} |
| 133 | + .getDeclaredConstructor(${asJavaCtor.toGetDeclaredMethodFormat()}) |
| 134 | + .newInstance(new Object[] { ${params.joinToString(", ")} })) |
| 135 | + """.trimIndent() |
| 136 | +} |
| 137 | + |
| 138 | +private val Class<*>.objectType get() = kotlin.javaObjectType |
| 139 | + |
| 140 | +public const val assign: String = "${'$'}_ = " |
| 141 | + |
| 142 | +private val PROJECTOR_LOADER_CLASS_NAME: String = ProjectorClassLoader::class.java.name |
| 143 | + |
| 144 | +private val PROJECTOR_LOADER_INSTANCE_GETTER_NAME: String = ProjectorClassLoader.Companion::instance.getter.javaMethod!!.name |
| 145 | + |
| 146 | +private val commonClassLoadCode = """ |
| 147 | + ((ClassLoader) ClassLoader |
| 148 | + .getSystemClassLoader() |
| 149 | + .loadClass("$PROJECTOR_LOADER_CLASS_NAME") |
| 150 | + .getDeclaredMethod("$PROJECTOR_LOADER_INSTANCE_GETTER_NAME", new Class[0]) |
| 151 | + .invoke(null, new Object[0])) |
| 152 | + """.trimIndent() |
0 commit comments