Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bytebuddy cogen (java 17) #1737

Merged
merged 6 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .jvmopts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-Xmx3G
-Xmx12G
-XX:ReservedCodeCacheSize=256M
-XX:MaxMetaspaceSize=2560M

Expand Down
12 changes: 6 additions & 6 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -1374,7 +1374,7 @@ lazy val `distage-core-api` = project.in(file("distage/distage-core-api"))
)
.disablePlugins(AssemblyPlugin)

lazy val `distage-core-proxy-cglib` = project.in(file("distage/distage-core-proxy-cglib"))
lazy val `distage-core-proxy-bytebuddy` = project.in(file("distage/distage-core-proxy-bytebuddy"))
.dependsOn(
`distage-core-api` % "test->compile;compile->compile"
)
Expand All @@ -1383,7 +1383,7 @@ lazy val `distage-core-proxy-cglib` = project.in(file("distage/distage-core-prox
compilerPlugin("org.typelevel" % "kind-projector" % V.kind_projector cross CrossVersion.full),
"org.scala-lang.modules" %% "scala-collection-compat" % V.collection_compat,
"org.scalatest" %% "scalatest" % V.scalatest % Test,
"cglib" % "cglib-nodep" % V.cglib_nodep
"net.bytebuddy" % "byte-buddy" % V.bytebuddy
)
)
.settings(
Expand Down Expand Up @@ -1506,7 +1506,7 @@ lazy val `distage-core-proxy-cglib` = project.in(file("distage/distage-core-prox
lazy val `distage-core` = project.in(file("distage/distage-core"))
.dependsOn(
`distage-core-api` % "test->compile;compile->compile",
`distage-core-proxy-cglib` % "test->compile;compile->compile"
`distage-core-proxy-bytebuddy` % "test->compile"
)
.settings(
libraryDependencies ++= Seq(
Expand Down Expand Up @@ -3289,7 +3289,7 @@ lazy val `microsite` = project.in(file("doc/microsite"))
`fundamentals-orphans` % "test->compile;compile->compile",
`fundamentals-literals` % "test->compile;compile->compile",
`distage-core-api` % "test->compile;compile->compile",
`distage-core-proxy-cglib` % "test->compile;compile->compile",
`distage-core-proxy-bytebuddy` % "test->compile;compile->compile",
`distage-core` % "test->compile;compile->compile",
`distage-extension-config` % "test->compile;compile->compile",
`distage-extension-plugins` % "test->compile;compile->compile",
Expand Down Expand Up @@ -3681,7 +3681,7 @@ lazy val `distage` = (project in file(".agg/distage-distage"))
.disablePlugins(AssemblyPlugin)
.aggregate(
`distage-core-api`,
`distage-core-proxy-cglib`,
`distage-core-proxy-bytebuddy`,
`distage-core`,
`distage-extension-config`,
`distage-extension-plugins`,
Expand All @@ -3705,7 +3705,7 @@ lazy val `distage-jvm` = (project in file(".agg/distage-distage-jvm"))
.disablePlugins(AssemblyPlugin)
.aggregate(
`distage-core-api`,
`distage-core-proxy-cglib`,
`distage-core-proxy-bytebuddy`,
`distage-core`,
`distage-extension-config`,
`distage-extension-plugins`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package izumi.distage.provisioning.strategies.cglib.exceptions
package izumi.distage.model.exceptions.interpretation

import izumi.distage.model.exceptions.DIException
import izumi.distage.model.plan.ExecutableOp
import izumi.distage.model.provisioning.proxies.ProxyProvider.ProxyParams

class CgLibInstantiationOpException(message: String, val target: Class[?], val params: ProxyParams, val op: ExecutableOp, cause: Throwable)
class ProxyInstantiationException(message: String, val target: Class[?], val params: ProxyParams, val op: ExecutableOp, cause: Throwable)
extends DIException(message, cause)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package izumi.distage.provisioning.strategies.cglib
package izumi.distage.model.provisioning.proxies

trait DistageProxy {
def _distageProxyReference: AnyRef
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ trait ProxyProvider {
}

object ProxyProvider {
class ProxyProviderFailingImpl extends ProxyProvider {
object ProxyProviderFailingImpl extends ProxyProvider {
override def makeCycleProxy(deferredKey: DIKey, proxyContext: ProxyContext): DeferredInit = {
throw new ProxyProviderFailingImplCalledException(s"ProxyProviderFailingImpl can't create cycle-breaking proxies, failed op: ${proxyContext.op}", this)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
package izumi.distage.provisioning.strategies.cglib

import java.lang.reflect.Method
package izumi.distage.provisioning.strategies.bytebuddyproxy

import izumi.distage.model.provisioning.proxies.DistageProxy
import izumi.distage.model.provisioning.proxies.ProxyDispatcher.AtomicProxyDispatcher
import net.sf.cglib.proxy.{MethodInterceptor, MethodProxy}

import java.lang.reflect.{InvocationHandler, Method}

// dynamic dispatching is not optimal, uhu
private[distage] class CglibAtomicRefDispatcher(
private[distage] class ByteBuddyAtomicRefDispatcher(
nullProxy: AnyRef
) extends AtomicProxyDispatcher
with MethodInterceptor {

override def intercept(o: scala.Any, method: Method, objects: Array[AnyRef], methodProxy: MethodProxy): AnyRef = {
with InvocationHandler {
override def invoke(o: scala.Any, method: Method, objects: Array[AnyRef]): AnyRef = {
val methodName = method.getName
if (methodName == "equals" && (method.getParameterTypes sameElements Array(classOf[AnyRef]))) {
objects.headOption match {
case Some(r: DistageProxy) =>
Boolean.box(getRef() == r._distageProxyReference)
Boolean.box(getRef == r._distageProxyReference)

case _ =>
method.invoke(getRef(), objects: _*)
method.invoke(getRef, objects: _*)
}
} else if (methodName == "_distageProxyReference" && method.getParameterCount == 0) {
getRef()
getRef
} else {
method.invoke(getRef(), objects: _*)
method.invoke(getRef, objects: _*)
}
}

@inline private[this] final def getRef(): AnyRef = {
@inline private[this] final def getRef: AnyRef = {
val value = reference.get()
if (value ne null) value else nullProxy
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package izumi.distage.provisioning.strategies.cglib
package izumi.distage.provisioning.strategies.bytebuddyproxy

import izumi.distage.model.exceptions.interpretation.MissingRefException

import java.lang.reflect.Method
import izumi.distage.model.reflection.DIKey
import net.sf.cglib.proxy.{MethodInterceptor, MethodProxy}

import java.lang.reflect.{InvocationHandler, Method}

// we use this to be able to display something for uninitialized proxies
private[distage] class CglibNullMethodInterceptor(
private[distage] class ByteBuddyNullMethodInterceptor(
key: DIKey
) extends MethodInterceptor {
override def intercept(o: Any, method: Method, objects: Array[AnyRef], methodProxy: MethodProxy): AnyRef = {
) extends InvocationHandler {
override def invoke(o: Any, method: Method, objects: Array[AnyRef]): AnyRef = {
if (method.getName == "toString" && method.getParameterCount == 0) {
s"__UninitializedProxy__:$key"
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package izumi.distage.provisioning.strategies.dynamicproxy

import izumi.distage.model.exceptions.interpretation.ProxyInstantiationException
import izumi.distage.model.provisioning.proxies.ProxyProvider.ProxyParams.{Empty, Params}
import izumi.distage.model.provisioning.proxies.ProxyProvider.{DeferredInit, ProxyContext}
import izumi.distage.model.provisioning.proxies.{DistageProxy, ProxyProvider}
import izumi.distage.model.reflection.DIKey
import izumi.distage.provisioning.strategies.bytebuddyproxy.{ByteBuddyAtomicRefDispatcher, ByteBuddyNullMethodInterceptor}
import net.bytebuddy.ByteBuddy
import net.bytebuddy.dynamic.scaffold.TypeValidation
import net.bytebuddy.implementation.InvocationHandlerAdapter
import net.bytebuddy.matcher.{ElementMatcher, ElementMatchers}
import izumi.fundamentals.platform.exceptions.IzThrowable.*
import net.bytebuddy.description.method.MethodDescription

import java.lang.reflect.InvocationHandler

object DynamicProxyProvider extends ProxyProvider {

override def makeCycleProxy(deferredKey: DIKey, proxyContext: ProxyContext): DeferredInit = {
val nullDispatcher = new ByteBuddyNullMethodInterceptor(deferredKey)
val nullProxy = mkDynamic(nullDispatcher, proxyContext)

val realDispatcher = new ByteBuddyAtomicRefDispatcher(nullProxy)
val realProxy = mkDynamic(realDispatcher, proxyContext)

DeferredInit(realDispatcher, realProxy)
}

private def mkDynamic(dispatcher: InvocationHandler, proxyContext: ProxyContext): AnyRef = {
val clazz = proxyContext.runtimeClass.asInstanceOf[Class[AnyRef]]

val constructedProxyClass: Class[AnyRef] = new ByteBuddy()
.`with`(TypeValidation.DISABLED)
.subclass(clazz)
.method(ElementMatchers.isMethod.asInstanceOf[ElementMatcher[MethodDescription]])
.intercept(InvocationHandlerAdapter.of(dispatcher))
.implement(classOf[DistageProxy])
.make()
.load(clazz.getClassLoader)
.getLoaded.asInstanceOf[Class[AnyRef]]

try {
proxyContext.params match {
case Empty =>
constructedProxyClass.getDeclaredConstructor().newInstance()
case Params(types, values) =>
val c = constructedProxyClass.getDeclaredConstructor(types: _*)
c.newInstance(values.map(_.asInstanceOf[AnyRef]): _*)
}
} catch {
case f: Throwable =>
throw new ProxyInstantiationException(
s"Failed to instantiate class with CGLib, make sure you don't dereference proxied parameters in constructors: " +
s"class=${proxyContext.runtimeClass}, params=${proxyContext.params}, exception=${f.stackTrace}",
clazz,
proxyContext.params,
proxyContext.op,
f,
)
}
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package izumi.distage.bootstrap

import izumi.distage.model.provisioning.proxies.ProxyProvider
import izumi.distage.model.provisioning.proxies.ProxyProvider.ProxyProviderFailingImpl

object DynamicProxyBootstrap {
val DynamicProxyProvider: ProxyProvider = ProxyProviderFailingImpl
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package izumi.distage.bootstrap

import izumi.distage.model.provisioning.proxies.ProxyProvider
import izumi.fundamentals.reflection.TypeUtil

import scala.util.Try

object DynamicProxyBootstrap {
val dynProxyProviderName = "izumi.distage.provisioning.strategies.dynamicproxy.DynamicProxyProvider$"

val DynamicProxyProvider: ProxyProvider =
Try(TypeUtil.instantiateObject[ProxyProvider](Class.forName(dynProxyProviderName))).toOption match {
case Some(value) =>
value
case None =>
import izumi.distage.model.provisioning.proxies.ProxyProvider.ProxyProviderFailingImpl
ProxyProviderFailingImpl
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import distage.DIKey
import izumi.distage.model.PlannerInput
import izumi.distage.model.definition.{Activation, ModuleDef}
import izumi.distage.model.plan.Roots
import izumi.distage.model.provisioning.proxies.DistageProxy
import org.scalatest.wordspec.AnyWordSpec

import scala.collection.immutable
Expand All @@ -30,10 +31,20 @@ class GcBasicTestsJvm extends AnyWordSpec with MkGcInjector {

val result = injector.produce(plan).unsafeGet()
assert(result.find[Trash].isEmpty)
assert(result.get[Circular1].c2 != null)
assert(result.get[Circular2].c1 != null)
assert(result.get[Circular1].c2.isInstanceOf[Circular2])
assert(result.get[Circular2].c1.isInstanceOf[Circular1])
val c1 = result.get[Circular1]
val c2 = result.get[Circular2]

assert(c1.c2 != null)
assert(c2.c1 != null)

assert(c2.test == 1)

assert(c1.c2.isInstanceOf[Circular2])
assert(c2.c1.isInstanceOf[Circular1])

assert(c2.isInstanceOf[DistageProxy] || c1.isInstanceOf[DistageProxy])
val p = List(c1, c2).collect { case p: DistageProxy => p }
assert(p.forall(_._distageProxyReference != null))
}

"keep by-name loops alive" in {
Expand Down
Loading