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

PRJ-285 JCEF support #130

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ subprojects {
repositories {
mavenCentral()
maven("https://www.jetbrains.com/intellij-repository/releases")
maven("https://www.jetbrains.com/intellij-repository/snapshots")
maven("https://cache-redirector.jetbrains.com/intellij-dependencies")
}

Expand Down
21 changes: 21 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,33 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import java.util.*

plugins {
`kotlin-dsl`
}

repositories {
mavenCentral()
maven("https://www.jetbrains.com/intellij-repository/releases")
maven("https://www.jetbrains.com/intellij-repository/snapshots")
maven("https://cache-redirector.jetbrains.com/intellij-dependencies")
}


val gradleProperties = Properties()
val gradlePropertiesFile = project.file("../gradle.properties")
if (gradlePropertiesFile.canRead()) {
gradleProperties.load(gradlePropertiesFile.inputStream())
}

val intellijPlatformVersion: String by gradleProperties

dependencies {
implementation("com.jetbrains.intellij.platform:core:$intellijPlatformVersion") {
exclude(group = "org.jetbrains.kotlin") // cannot find these dependencies
}
}

kotlin {
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ dnsjavaVersion=3.4.3
electronVersion=^16.0.6
electronPackagerVersion=^15.1.0
gradleMkdocsPluginVersion=2.2.0
intellijPlatformVersion=213.6461.23
intellijMarkdownPluginVersion=213.5744.223
intellijPlatformVersion=213.6777.52
intellijMarkdownPluginVersion=213.6777.52
intellijJcefVersion=89.0.12-g2b76680-chromium-89.0.4389.90-api-1.6
istanbulInstrumenterLoaderVersion=3.0.1
javassistVersion=3.28.0-GA
Expand Down
3 changes: 3 additions & 0 deletions projector-agent-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@ publishToSpace("java")

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

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

implementation(kotlin("reflect", kotlinVersion))
implementation("org.javassist:javassist:$javassistVersion")
testImplementation("io.kotest:kotest-assertions-core:$kotestVersion")
testImplementation("io.kotest:kotest-runner-junit5:$kotestVersion")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,127 @@
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 }"
}

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 @@ -43,6 +43,7 @@ val kotestVersion: 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.ij.jcef.isCefAvailable
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 { isCefAvailable() }
}

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