1

I would like to understand is there a way to write a method to existing class at runtime and to create a jar dynamically in scala.

So far i tried to create a class dynamically and able to run it thru reflection, however the class is dynamic class which isnt generated.

val mirror = runtimeMirror(getClass.getClassLoader)
val tb = ToolBox(mirror).mkToolBox() 

val function = q"def function(x: Int): Int = x + 2"
    val functionWrapper = "object FunctionWrapper { " + function + "}"
data.map(x => tb.eval(q"$functionSymbol.function($x)"))

i got this from other source, however the class is available only for this run and will not be generated.

i would like to add a function to the existing class at runtime and able to compile it and create a jar for it.

Kindly suggest me the way

Thanks in advance.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
user3569397
  • 27
  • 1
  • 8
  • Why do you need this? Are you sure this is the only, or the best, solution your problem? – Luis Miguel Mejía Suárez Aug 21 '20 at 02:59
  • 1
    This sounds like a terrible idea. – Matthias Berndt Aug 21 '20 at 03:00
  • 1
    @LuisMiguelMejíaSuárez we will receive a string, based on it we need to add this as method to the class at runtime and need to generate a jar. so i started to first create a dynamic class, but not able to generate here. however i need to add the function to existing class. – user3569397 Aug 21 '20 at 03:03
  • Why do you need to receive arbitrary code as a **String**? Are you aware that this approach is very insecure. As well as unsafe from a type perspective and the overall process will be slow. - Again, I ask, what is the outer problem that lead to this design, there should be alternatives. – Luis Miguel Mejía Suárez Aug 21 '20 at 03:08
  • 1
    We might actually receive an expression, based on the expression we need to create it a method for further use for spark udf creation. – user3569397 Aug 21 '20 at 03:12
  • 1
    So what could these expressions look like? It's usually better to come up with a restrictive language that allows users to express exactly what is needed and no more. This expression can then be compiled into a function object using a technique known as partial evaluation. – Matthias Berndt Aug 21 '20 at 08:39
  • This will be my last comment and I won't insist more since you came here wanting help not to be told what to do but. Who are the ones writing those udfs? Someone with a programming background, if so why not juts programming and deploying new jars? Someone without programming back ground, so maybe you should just write a small parser for a simple grammar and instead of compiling classes and growing bigger a jar, you could save those on some db and access them by some id. Also, it may be good to give a look to **Apache Zeppelin**. – Luis Miguel Mejía Suárez Aug 21 '20 at 13:10
  • 1
    Someone with programming background will use the udf once the udfs are available. i would like to understand on how can we generate a class and bind it with jar dynamically. – user3569397 Aug 21 '20 at 15:05

1 Answers1

2

I guess the code snippet you provided should actually look like

import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox

val mirror = runtimeMirror(getClass.getClassLoader)
val tb = ToolBox(mirror).mkToolBox()

val function: Tree = q"def function(x: Int): Int = x + 2"
val functionWrapper: Symbol = tb.define(q"object FunctionWrapper { $function }".asInstanceOf[ImplDef])
val data: List[Tree] = List(q"1", q"2")
data.map(x => tb.eval(q"$functionWrapper.function($x)")) // List(3, 4)

... however the class is dynamic class which isnt generated.

... however the class is available only for this run and will not be generated.

How did you check that the class is not generated? (Which class, FunctionWrapper?)

is there a way to write a method to existing class at runtime and to create a jar dynamically in scala.

i would like to add a function to the existing class at runtime and able to compile it and create a jar for it.

What is "existing class"? Do you have access to its sources? Then you can modify the sources, compile them etc.

Does the class exist as a .class file? You can modify its byte code with Byte-buddy, ASM, Javassist, cglib etc., instrument the byte code with aspects etc.

Is it dynamic class (like FunctionWrapper above)? How did you create it? (For FunctionWrapper you have access to its Symbol so you can use it in further sources.)

Is the class already loaded? Then you'll have to play with class loaders (unload, modify, load modified).

Can a Java class add a method to itself at runtime?

In Java, given an object, is it possible to override one of the methods?

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Thanks for the info Dmytro.. 1. Yes, FunctionWrapper class isnt generated. 2. ok, will take a look on Byte-buddy – user3569397 Aug 21 '20 at 13:46
  • @user3569397 In what sense `FunctionWrapper` isn't generated? You have its symbol, you can use it in further sources. How do you check that it's not generated? What are you trying to do with `FunctionWrapper` that doesn't work for you? – Dmytro Mitin Aug 21 '20 at 14:15
  • @user3569397 If you create toolbox with `ToolBox(mirror).mkToolBox(options = "-d /path/to/dir")` then in `dir` you'll find `.class` file for `FunctionWrapper`. Without `-d` option it was created in a virtual directory in memory. – Dmytro Mitin Aug 21 '20 at 15:00
  • Hi Dmytro, apologies if am not explaining it correctly. i tried checking in source packages. May i please request you where can i check the class created? – user3569397 Aug 21 '20 at 15:03
  • @user3569397 It's not in `src/main/scala...`, it's in `dir` that you specify with `-d` key in `options` parameter of `mkToolBox`, otherwise it's in memory (by default). https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/reflect/ToolBoxFactory.scala#L46-L50 – Dmytro Mitin Aug 21 '20 at 15:08
  • @user3569397 The directory must exist in a disc. – Dmytro Mitin Aug 21 '20 at 15:09
  • Thanks Dmytro, will take a look and will try few things with Byte-buddy as well. i might need help in classloaders . – user3569397 Aug 21 '20 at 15:15
  • @user3569397 Inside `dir` object `FunctionWrapper` is generated in a package with random name `__wrapper$1$92c082bff0fc4ea08f1579a2633c937c`. If you want to have this name in your code you can do `tb.eval(q"$functionWrapper.getClass.getName")` and this will give `__wrapper$1$f3d3f4d7a84347dd8f979d628f85d93e.FunctionWrapper$`. – Dmytro Mitin Aug 21 '20 at 16:14