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

Commit

Permalink
PRJ-285 Add jcef support
Browse files Browse the repository at this point in the history
  • Loading branch information
ARTI1208 committed Feb 15, 2022
1 parent 2a90f33 commit 0beb53f
Show file tree
Hide file tree
Showing 26 changed files with 1,922 additions and 42 deletions.
3 changes: 3 additions & 0 deletions projector-agent-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ kotlin {
publishToSpace("java")

val javassistVersion: String by project
val kotlinVersion: String by project

dependencies {
implementation(project(":projector-util-loading"))

implementation(kotlin("reflect", kotlinVersion))
implementation("org.javassist:javassist:$javassistVersion")
testImplementation(kotlin("test"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,126 @@
package org.jetbrains.projector.agent.common

import javassist.*
import org.jetbrains.projector.util.loading.ProjectorClassLoader
import org.jetbrains.projector.util.loading.UseProjectorLoader
import java.lang.IllegalArgumentException
import java.lang.reflect.Constructor
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import kotlin.reflect.KFunction
import kotlin.reflect.full.createType
import kotlin.reflect.jvm.javaConstructor
import kotlin.reflect.jvm.javaMethod

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

private val currentClassPool: ClassPool by lazy { ClassPool().apply { appendClassPath(LoaderClassPath(object {}.javaClass.classLoader)) } }
public val projectorClassPool: ClassPool by lazy { ClassPool().apply { appendClassPath(LoaderClassPath(ProjectorClassLoader.instance)) } }

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

public fun CtClass.getDeclaredMethod(name: String, vararg params: Class<*>): CtMethod =
getDeclaredMethodImpl(name, currentClassPool, params)
getDeclaredMethodImpl(name, projectorClassPool, params)

private fun CtClass.getDeclaredConstructorImpl(classPool: ClassPool, params: Array<out Class<*>>): CtConstructor =
getDeclaredConstructor(params.map { classPool[it.name] }.toTypedArray())

public fun CtClass.getDeclaredConstructor(vararg params: Class<*>): CtConstructor =
getDeclaredConstructorImpl(projectorClassPool, params)

public inline operator fun <reified T> ClassPool.invoke(): CtClass = this[T::class.java.name]

public fun Method.toGetDeclaredMethodFormat(): String {

//val parameterClasses = parameterTypes.joinToString(", ") { "${it.kotlin.javaObjectType.name}.class" }
val parameterClasses = parameterTypes.joinToString(", ") { "${it.name}.class" }
val parametersString = "new Class[] { $parameterClasses }"

return "\"$name\", $parametersString"
}

public fun Constructor<*>.toGetDeclaredMethodFormat(): String {
val parameterClasses = parameterTypes.joinToString(", ") { "${it.name}.class" }
return "new Class[] { $parameterClasses }"
}

// language=java prefix="class DummyClass { void dummyMethod() {" suffix="}}"
public inline fun <reified T> loadClassWithProjectorLoader(): String = loadClassWithProjectorLoader(T::class.java)

public fun loadClassWithProjectorLoader(clazz: Class<*>): String = loadClassWithProjectorLoader(clazz.name, true)

private fun loadClassWithProjectorLoader(className: String, trim: Boolean): String = """
$commonClassLoadCode
.loadClass("$className")
""".let { if (!trim) it.trimIndent() else it }

private val unitType by lazy { Unit::class.createType() }

public fun <T : KFunction<*>> T.getJavaCallString(
vararg params: String,
finishedExpression: Boolean = this.returnType == unitType,
autoCast: Boolean = true,
): String {

val mainPart = when {
javaMethod != null -> getJavaCallString(javaMethod!!, autoCast, *params)
javaConstructor != null -> getJavaCallString(javaConstructor!!, autoCast, *params)
else -> throw IllegalArgumentException("Cannot convert Kotlin function $this to Java method")
}

return if (finishedExpression)
"""
{
$mainPart;
}
""".trimIndent()
else mainPart
}

private fun getJavaCallString(asJavaMethod: Method, cast: Boolean = true, vararg params: String): String {

val isStatic = Modifier.isStatic(asJavaMethod.modifiers)
val instance = if (isStatic) "null" else params.first()
val otherParams = if (isStatic) params.toList() else params.drop(1)

require(otherParams.size == asJavaMethod.parameterCount) { "Cannot create Java method call string: expected ${asJavaMethod.parameterCount} parameters, got ${otherParams.size}" }

val castString = if (!cast || asJavaMethod.returnType == Void.TYPE) "" else "(${asJavaMethod.returnType.objectType.name})"

return """
($castString ${loadClassWithProjectorLoader(asJavaMethod.declaringClass.name, false)}
.getDeclaredMethod(${asJavaMethod.toGetDeclaredMethodFormat()})
.invoke($instance, new Object[] { ${otherParams.joinToString(", ")} }))
""".trimIndent()
}

private fun getJavaCallString(asJavaCtor: Constructor<*>, cast: Boolean = true, vararg params: String): String {
require(params.size == asJavaCtor.parameterCount) { "Cannot create Java method call string: expected ${asJavaCtor.parameterCount} parameters, got ${params.size}" }

val castString = if (!cast) "" else "(${asJavaCtor.declaringClass.name})"

return """
($castString ${loadClassWithProjectorLoader(asJavaCtor.declaringClass)}
.getDeclaredConstructor(${asJavaCtor.toGetDeclaredMethodFormat()})
.newInstance(new Object[] { ${params.joinToString(", ")} }))
""".trimIndent()
}

private val Class<*>.objectType get() = kotlin.javaObjectType

public const val assign: String = "${'$'}_ = "

private val PROJECTOR_LOADER_CLASS_NAME: String = ProjectorClassLoader::class.java.name

private val PROJECTOR_LOADER_INSTANCE_GETTER_NAME: String = ProjectorClassLoader.Companion::instance.getter.javaMethod!!.name

private val commonClassLoadCode = """
((ClassLoader) ClassLoader
.getSystemClassLoader()
.loadClass("$PROJECTOR_LOADER_CLASS_NAME")
.getDeclaredMethod("$PROJECTOR_LOADER_INSTANCE_GETTER_NAME", new Class[0])
.invoke(null, new Object[0]))
""".trimIndent()
1 change: 1 addition & 0 deletions projector-agent-ij-injector/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ val intellijJcefVersion: String by project

dependencies {
implementation(project(":projector-agent-common"))
implementation(project(":projector-common"))
implementation(project(":projector-ij-common"))
implementation(project(":projector-util-loading"))
implementation(project(":projector-util-logging"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package org.jetbrains.projector.agent.ijInjector

import org.jetbrains.projector.agent.init.IjArgs
import org.jetbrains.projector.common.intellij.buildAtLeast
import org.jetbrains.projector.util.loading.UseProjectorLoader
import java.lang.instrument.Instrumentation

Expand All @@ -34,7 +35,9 @@ internal object IjInjector {
class AgentParameters(
val isAgent: Boolean,
val markdownPanelClassName: String,
)
) {
val jcefTransformerInUse by lazy { buildAtLeast("202") }
}

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

Expand All @@ -52,6 +55,7 @@ internal object IjInjector {
val transformers = listOf(
IjLigaturesDisablerTransformer,
IjMdTransformer,
IjJcefTransformer,
IjBrowserUtilTransformer,
IjUiUtilsTransformer,
IjFastNodeCellRendererTransformer,
Expand Down
Loading

0 comments on commit 0beb53f

Please sign in to comment.