In Scala even without reflective compilation there are bugs in combination of scala-java interop and F-bounded polymorphism:
scalac reports error on valid Java class: illegal cyclic reference involving type T
and others.
And parents of com.google.protobuf.DynamicMessage
explore F-bounds:
DynamicMessage
<: AbstractMessage
<: AbstractMessageLite[_,_] (such inheritance is allowed in Java but not in Scala)
[M <: AbstractMessageLite[M, B],
B <: AbstractMessageLite.Builder[M, B]]
[M <: AbstractMessageLite[M, B],
B <: AbstractMessageLite.Builder[M, B]]
<: MessageLite.Builder
<: MessageLiteOrBuilder
<: Cloneable
<: MessageLite
<: MessageLiteOrBuilder
<: Message
<: MessageLite...
<: MessageOrBuilder
<: MessageLiteOrBuilder
But without reflective compilation your code compiles. So this is a bug in combination of reflective compilation, scala-java interop and F-bounded polymorphism.
A workaround is to use real compiler instead of toolbox:
import akka.actor.{ActorSystem, Props}
// libraryDependencies += "com.github.os72" % "protobuf-dynamic" % "1.0.1"
import com.github.os72.protobuf.dynamic.{DynamicSchema, MessageDefinition}
import com.google.protobuf.DynamicMessage
import scala.reflect.internal.util.{AbstractFileClassLoader, BatchSourceFile}
import scala.reflect.io.{AbstractFile, VirtualDirectory}
import scala.reflect.runtime
import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._
import scala.tools.nsc.{Global, Settings}
val actorCode = """
|import akka.actor._
|import com.google.protobuf._
|
|class SimpleActor extends Actor {
| override def receive: Receive = {
| case dynamicMessage: DynamicMessage => println("Dynamic message received!")
| case _ => println("Whatever!") // the default, catch-all
| }
|}
|
|object SimpleActor {
| def props() : Props = Props(new SimpleActor())
|}
|""".stripMargin
val directory = new VirtualDirectory("(memory)", None)
val runtimeMirror = createRuntimeMirror(directory, runtime.currentMirror)
compileCode(actorCode, List(), directory)
val props = runObjectMethod("SimpleActor", runtimeMirror, "props")
.asInstanceOf[Props]
val actorSystem = ActorSystem("actorSystem")
val actor = actorSystem.actorOf(props, "helloActor")
val msg = makeDynamicMessage()
actor ! "hello" // Whatever!
actor ! msg // Dynamic message received!
actorSystem.terminate()
//see (*)
def makeDynamicMessage(): DynamicMessage = {
val schemaBuilder = DynamicSchema.newBuilder
schemaBuilder.setName("PersonSchemaDynamic.proto")
val msgDef = MessageDefinition.newBuilder("Person")
.addField("required", "int32", "id", 1)
.build
schemaBuilder.addMessageDefinition(msgDef)
val schema = schemaBuilder.build
val msgBuilder = schema.newMessageBuilder("Person")
val msgDesc = msgBuilder.getDescriptorForType
msgBuilder
.setField(msgDesc.findFieldByName("id"), 1)
.build
}
def compileCode(
code: String,
classpathDirectories: List[AbstractFile],
outputDirectory: AbstractFile
): Unit = {
val settings = new Settings
classpathDirectories.foreach(dir => settings.classpath.prepend(dir.toString))
settings.outputDirs.setSingleOutput(outputDirectory)
settings.usejavacp.value = true
val global = new Global(settings)
(new global.Run).compileSources(List(new BatchSourceFile("(inline)", code)))
}
def runObjectMethod(
objectName: String,
runtimeMirror: Mirror,
methodName: String,
arguments: Any*
): Any = {
val objectSymbol = runtimeMirror.staticModule(objectName)
val objectModuleMirror = runtimeMirror.reflectModule(objectSymbol)
val objectInstance = objectModuleMirror.instance
val objectType = objectSymbol.typeSignature
val methodSymbol = objectType.decl(TermName(methodName)).asMethod
val objectInstanceMirror = runtimeMirror.reflect(objectInstance)
val methodMirror = objectInstanceMirror.reflectMethod(methodSymbol)
methodMirror(arguments: _*)
}
def createRuntimeMirror(directory: AbstractFile, parentMirror: Mirror): Mirror = {
val classLoader = new AbstractFileClassLoader(directory, parentMirror.classLoader)
universe.runtimeMirror(classLoader)
}
Tensorflow in Scala reflection (here was a similar situation with a bug in combination of reflective compilation, Scala-Java interop, and path-dependent types)
Dynamic compilation of multiple Scala classes at runtime
How to eval code that uses InterfaceStability annotation (that fails with "illegal cyclic reference involving class InterfaceStability")? (also "illegal cyclic reference" during reflective compilation)
Scala Presentation Compiler - Minimal Example
(*) Protocol buffer objects generated at runtime