Skip to content
This repository was archived by the owner on Mar 26, 2024. It is now read-only.

Commit 3f07c02

Browse files
committed
Add jcef support
1 parent 6d62d67 commit 3f07c02

File tree

26 files changed

+1940
-56
lines changed

26 files changed

+1940
-56
lines changed

projector-agent-common/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ kotlin {
3636
publishToSpace("java")
3737

3838
val javassistVersion: String by project
39+
val kotlinVersion: String by project
3940

4041
dependencies {
4142
implementation(project(":projector-util-loading"))
43+
44+
implementation(kotlin("reflect", kotlinVersion))
4245
implementation("org.javassist:javassist:$javassistVersion")
4346
testImplementation(kotlin("test"))
4447
}

projector-agent-common/src/main/kotlin/org/jetbrains/projector/agent/common/Misc.kt

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,126 @@
2727
package org.jetbrains.projector.agent.common
2828

2929
import javassist.*
30+
import org.jetbrains.projector.util.loading.ProjectorClassLoader
3031
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
3140

3241
internal fun getClassFromClassfileBuffer(pool: ClassPool, className: String, classfileBuffer: ByteArray): CtClass {
3342
pool.insertClassPath(ByteArrayClassPath(className, classfileBuffer))
3443
return pool.get(className).apply(CtClass::defrost)
3544
}
3645

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)) } }
3847

3948
private fun CtClass.getDeclaredMethodImpl(name: String, classPool: ClassPool, params: Array<out Class<*>>): CtMethod =
4049
getDeclaredMethod(name, params.map { classPool[it.name] }.toTypedArray())
4150

4251
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()

projector-agent-ij-injector/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ val intellijJcefVersion: String by project
4242

4343
dependencies {
4444
implementation(project(":projector-agent-common"))
45+
implementation(project(":projector-common"))
4546
implementation(project(":projector-ij-common"))
4647
implementation(project(":projector-util-loading"))
4748
implementation(project(":projector-util-logging"))

projector-agent-ij-injector/src/main/kotlin/org/jetbrains/projector/agent/ijInjector/IjInjector.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package org.jetbrains.projector.agent.ijInjector
2525

2626
import org.jetbrains.projector.agent.init.IjArgs
27+
import org.jetbrains.projector.common.intellij.buildAtLeast
2728
import org.jetbrains.projector.util.loading.UseProjectorLoader
2829
import java.lang.instrument.Instrumentation
2930

@@ -34,7 +35,9 @@ internal object IjInjector {
3435
class AgentParameters(
3536
val isAgent: Boolean,
3637
val markdownPanelClassName: String,
37-
)
38+
) {
39+
val jcefTransformerInUse by lazy { buildAtLeast("202") }
40+
}
3841

3942
private fun parametersFromArgs(args: Map<String, String>): AgentParameters {
4043

@@ -52,6 +55,7 @@ internal object IjInjector {
5255
val transformers = listOf(
5356
IjLigaturesDisablerTransformer,
5457
IjMdTransformer,
58+
IjJcefTransformer,
5559
IjBrowserUtilTransformer,
5660
IjUiUtilsTransformer,
5761
IjFastNodeCellRendererTransformer,

0 commit comments

Comments
 (0)