diff --git a/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/Scafi3Incarnation.scala b/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/Scafi3Incarnation.scala index 10624b54..0ac997ae 100644 --- a/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/Scafi3Incarnation.scala +++ b/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/Scafi3Incarnation.scala @@ -37,6 +37,20 @@ class Scafi3Incarnation[T, Position <: AlchemistPosition[Position]] extends Inca ) cl + private val reflectionCache: LoadingCache[(String, Option[URLClassLoader]), (Any, java.lang.reflect.Method)] = + Caffeine + .newBuilder() + .maximumSize(CACHE_SIZE) + .build: + case (programName, classLoader) => + val programPath = programName.split('.') + val classPath = programPath.take(programPath.length - 1).mkString("", ".", "$") + val clazz = classLoader.map(_.loadClass(classPath)).getOrElse(Class.forName(classPath)) + val module = clazz.getField("MODULE$").nn.get(clazz) + val methods = clazz.getMethods.nn + val method = methods.toList.find(_.nn.getName.nn == programPath.last).get.nn + (module, method) + override def createMolecule(s: String): Molecule = SimpleMolecule(s) @SuppressWarnings(Array("scalafix:DisableSyntax.asInstanceOf", "scalafix:DisableSyntax.null")) @@ -123,7 +137,9 @@ class Scafi3Incarnation[T, Position <: AlchemistPosition[Position]] extends Inca ): Action[T] = require(node != null, "Scafi3 requires a device and cannot execute in a Global Reaction") // scalafix:ok additionalParameters match - case params: String => RunScafi3Program[T, Position](node, environment, params) + case params: String => + val (module, method) = reflectionCache.get((params, None)).nn + RunScafi3Program[T, Position](node, environment, params, None, module, method) case params: java.util.Map[String, String] @unchecked => require( params.containsKey("code") && params.containsKey("entrypoint"), @@ -133,7 +149,8 @@ class Scafi3Incarnation[T, Position <: AlchemistPosition[Position]] extends Inca val entrypoint = params.get("entrypoint") val name = params.getOrDefault("name", "Scafi3Program") val classLoader = classLoaders.get(ProgramSource(name, code)) - RunScafi3Program[T, Position](node, environment, entrypoint, Some(classLoader)) + val (module, method) = reflectionCache.get((entrypoint, Some(classLoader))).nn + RunScafi3Program[T, Position](node, environment, entrypoint, Some(classLoader), module, method) case params => throw IllegalArgumentException( s"Invalid parameters for Scafi3. `String` required, but ${params.getClass} has been provided: $params", diff --git a/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/actions/RunScafi3Program.scala b/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/actions/RunScafi3Program.scala index 4cc7c9d3..e948b65b 100644 --- a/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/actions/RunScafi3Program.scala +++ b/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/actions/RunScafi3Program.scala @@ -24,15 +24,39 @@ class RunScafi3Program[T, Position <: AlchemistPosition[Position]]( node: Node[T], environment: Environment[T, Position], val programName: String, - val classLoader: Option[URLClassLoader] = None, + val classLoader: Option[URLClassLoader], + val module: Any, + val method: java.lang.reflect.Method, ) extends AbstractAction[T](node): + + def this( + node: Node[T], + environment: Environment[T, Position], + programName: String, + classLoader: Option[URLClassLoader], + ) = + this( + node, + environment, + programName, + classLoader, { + val programPath = programName.split('.') + val classPath = programPath.take(programPath.length - 1).mkString("", ".", "$") + val clazz = classLoader.map(_.loadClass(classPath)).getOrElse(Class.forName(classPath)) + clazz.getField("MODULE$").nn.get(clazz) + }, { + val programPath = programName.split('.') + val classPath = programPath.take(programPath.length - 1).mkString("", ".", "$") + val clazz = classLoader.map(_.loadClass(classPath)).getOrElse(Class.forName(classPath)) + val methods = clazz.getMethods.nn + methods.toList.find(_.nn.getName.nn == programPath.last).get.nn + }, + ) + + def this(node: Node[T], environment: Environment[T, Position], programName: String) = + this(node, environment, programName, None) + private val programIdentifier = SimpleMolecule(programName) - private val programPath: Array[String] = programName.split('.') - private val classPath: String = programPath.take(programPath.length - 1).mkString("", ".", "$") - private val clazz = classLoader.map(_.loadClass(classPath)).getOrElse(Class.forName(classPath)) - private val module = clazz.getField("MODULE$").nn.get(clazz) - private val methods = clazz.getMethods.nn - private val method = methods.toList.find(_.nn.getName.nn == programPath.last).get.nn val localDevice: Scafi3Device[T, Position] = node.asProperty(classOf[Scafi3Device[T, Position]]) @@ -46,7 +70,7 @@ class RunScafi3Program[T, Position <: AlchemistPosition[Position]]( override def getContext: Context = Context.NEIGHBORHOOD override def cloneAction(node: Node[T], reaction: Reaction[T]): Action[T] = - RunScafi3Program[T, Position](node, environment, programName, classLoader) + RunScafi3Program[T, Position](node, environment, programName, classLoader, module, method) override def execute(): Unit = val result = scafiProgram.cycle()