Invoke private method in arbitrary scala object - scala

Let's say I have a Scala object:
object SomeObject {
private def someMethod(msg: String): Unit = println(msg)
}
I can invoke someMethod with the following code:
import scala.reflect.runtime.{universe => ru}
import scala.reflect.ClassTag
def invokeObjectPrivateMethod[R](methodName: String, args: AnyRef*): R = {
val rm = ru.runtimeMirror(getClass.getClassLoader)
val instanceMirror = rm.reflect(SomeObject)
val methodSymbol = ru.typeOf[SomeObject.type].decl(ru.TermName(methodName)).asMethod
val method = instanceMirror.reflectMethod(methodSymbol)
method(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod("someMethod", "it works")
But above I've hardcoded SomeObject into the function. What I'd really like is to pass an arbitrary object name/classtag/whatever so I can invoke a private function generically in ANY object.
These attempts have NOT worked:
// With explicit ClassTag parameter
def invokeObjectPrivateMethod2[R](classTag: ClassTag[_], methodName: String, args: AnyRef*): R = {
val rm = ru.runtimeMirror(getClass.getClassLoader)
val instanceMirror = rm.reflect(classTag)
val methodSymbol = ru.typeOf[classTag.type].decl(ru.TermName(methodName)).asMethod
val method = instanceMirror.reflectMethod(methodSymbol)
method(args: _*).asInstanceOf[R]
}
// This fails at runtime with: `scala.ScalaReflectionException: <none> is not a method`
invokeObjectPrivateMethod2[Unit](ClassTag(SomeObject.getClass), "someMethod", "it doesn't work")
// With implicit ClassTag/TypeTag
def invokeObjectPrivateMethod3[T: ClassTag, S: ru.TypeTag, R](methodName: String, args: AnyRef*)(implicit ct: ClassTag[T]): R = {
val rm = ru.runtimeMirror(getClass.getClassLoader)
val instanceMirror = rm.reflect(ct)
val methodSymbol = ru.typeOf[S].decl(ru.TermName(methodName)).asMethod
val method = instanceMirror.reflectMethod(methodSymbol)
method(args: _*).asInstanceOf[R]
}
// This fails at compile time: `not found: type SomeObject`
invokeObjectPrivateMethod3[SomeObject, SomeObject, Unit]("someMethod", "it also doesn't work")
I'm somewhat out of my depth, so what I'm trying with the 3rd option might not even make sense.
Thanks!

Try the following if you have String of object name
def invokeObjectPrivateMethod[R](objectName: String, methodName: String, args: AnyRef*): R = {
val rm = scala.reflect.runtime.currentMirror
val moduleSymbol = rm.staticModule(objectName)
val classSymbol = moduleSymbol.moduleClass.asClass
val moduleMirror = rm.reflectModule(moduleSymbol)
val objectInstance = moduleMirror.instance
val objectType = classSymbol.toType
val methodSymbol = objectType.decl(ru.TermName(methodName)).asMethod
val instanceMirror = rm.reflect(objectInstance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod("com.example.App.SomeObject", "someMethod", "it works") //it works
or if you have object ClassTag
def invokeObjectPrivateMethod[R](classTag: ClassTag[_], methodName: String, args: AnyRef*): R = {
val rm = scala.reflect.runtime.currentMirror
val clazz = classTag.runtimeClass
val classSymbol = rm.classSymbol(clazz)
val moduleSymbol = classSymbol.owner.info.decl(classSymbol.name.toTermName).asModule //see (*)
val moduleMirror = rm.reflectModule(moduleSymbol)
val objectInstance = moduleMirror.instance
val objectType = classSymbol.toType
val methodSymbol = objectType.decl(ru.TermName(methodName)).asMethod
val instanceMirror = rm.reflect(objectInstance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod(classTag[SomeObject.type], "someMethod", "it works") //it works
or if you have object TypeTag
def invokeObjectPrivateMethod[R](typeTag: TypeTag[_], methodName: String, args: AnyRef*): R = {
val rm = scala.reflect.runtime.currentMirror
val objectType = typeTag.tpe
val clazz = rm.runtimeClass(objectType) // see (**)
val classSymbol = rm.classSymbol(clazz)
val moduleSymbol = classSymbol.owner.info.decl(classSymbol.name.toTermName).asModule // see (*)
val moduleMirror = rm.reflectModule(moduleSymbol)
val objectInstance = moduleMirror.instance
val methodSymbol = objectType.decl(ru.TermName(methodName)).asMethod
val instanceMirror = rm.reflect(objectInstance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod(typeTag[SomeObject.type], "someMethod", "it works") //it works
or if you have object Class
def invokeObjectPrivateMethod[R](clazz: Class[_], methodName: String, args: AnyRef*): R = {
val rm = scala.reflect.runtime.currentMirror
val classSymbol = rm.classSymbol(clazz)
val moduleSymbol = classSymbol.owner.info.decl(classSymbol.name.toTermName).asModule //see (*)
val moduleMirror = rm.reflectModule(moduleSymbol)
val objectInstance = moduleMirror.instance
val objectType = classSymbol.toType
val methodSymbol = objectType.decl(ru.TermName(methodName)).asMethod
val instanceMirror = rm.reflect(objectInstance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod(SomeObject.getClass, "someMethod", "it works") //it works
or if you have object Type
def invokeObjectPrivateMethod[R](typ: Type, methodName: String, args: AnyRef*): R = {
val rm = scala.reflect.runtime.currentMirror
val classSymbol = typ.typeSymbol.asClass
val moduleSymbol = classSymbol.owner.info.decl(classSymbol.name.toTermName).asModule //see (*)
val moduleMirror = rm.reflectModule(moduleSymbol)
val objectInstance = moduleMirror.instance
val methodSymbol = typ.decl(ru.TermName(methodName)).asMethod
val instanceMirror = rm.reflect(objectInstance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod(typeOf[SomeObject.type], "someMethod", "it works") //it works
(*) Get the module symbol, given I have the module class, scala macro
(**) How to get ClassTag form TypeTag, or both at same time?
I assume that the method didn't have overloaded versions, otherwise you should work with typ.decl(...).alternatives.find(...).get.asMethod or typ.decl(...).alternatives.head.asMethod.
Using Java reflection
def invokeObjectPrivateMethod[R](packageName: String, objectName: String, methodName: String, args: AnyRef*): R = {
val javaClassName = s"$packageName.${objectName.replace('.', '$')}$$"
val clazz = Class.forName(javaClassName)
val method = clazz.getDeclaredMethods.find(_.getName == methodName).get
method.setAccessible(true)
val field = clazz.getField("MODULE$")
val instance = field.get(null) // null because field is static
method.invoke(instance, args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod("com.example", "App.SomeObject", "someMethod", "it works") //it works
Using Java method handles
def invokeObjectPrivateMethod[R](packageName: String, objectName: String, methodName: String, args: AnyRef*): R = {
val javaClassName = s"$packageName.${objectName.replace('.', '$')}$$"
val clazz = Class.forName(javaClassName)
val lookup = MethodHandles.lookup
val field = clazz.getField("MODULE$")
val fieldMethodHandle = lookup.unreflectGetter(field)
val instance = fieldMethodHandle.invokeWithArguments()
val method = clazz.getDeclaredMethods.find(_.getName == methodName).get
method.setAccessible(true)
val methodHandle = lookup.unreflect(method)
methodHandle.invokeWithArguments(instance +: args : _*).asInstanceOf[R]
}
invokeObjectPrivateMethod("com.example", "App.SomeObject", "someMethod", "it works") //it works

Related

Scala: generics and implicit

I have to develop class StackMachine[T]. If T = Boolean, then there should be logical operations. If T = Int,Double,Long and etc. there should be ariphmetic operations. Firstly i developed class Stack[T].
class Stack[T](val stack: List[T]) {
val length: Int = stack.length
def isEmpty: Boolean = {length == 0}
def push(x: T): Stack[T] = {
new Stack[T](x :: stack)
}
def peak: T = {
if (this.isEmpty)
throw new ArrayIndexOutOfBoundsException
else stack.head
}
def pop(): Stack[T] = {
if (this.isEmpty)
throw new ArrayStoreException()
val x :: xs = stack
new Stack[T](xs)
}
The thin is that i dont know how to develop StackMachine[T] the presence of operations in which depends on the type.
I tried this:
case class StackMachine[T](val stack:Stack[T]){
def const(x: T): StackMachine[T] = {new StackMachine[T](new Stack[T](this.stack.push(x).stack))}
def dup: StackMachine[T] = {new StackMachine[T](new Stack[T](this.stack.push(this.stack.peak).stack))}
def swap: StackMachine[T] = {
val startStack = this.stack
val startPeak = startStack.peak
val secondStack = startStack.pop()
val secondPeak = secondStack.peak
val finalStack = secondStack.pop().push(startPeak)
StackMachine[T](stack)
}
def and(): StackMachine[Boolean] = {
val startStack = this.stack.asInstanceOf[Stack[Boolean]]
val startPeak = startStack.peak
val secondStack = startStack.pop()
val secondPeak = secondStack.peak
StackMachine[Boolean](new Stack[Boolean](secondStack.push(startPeak && secondPeak).stack))
}
def or: StackMachine[Boolean] = {
val startStack = this.stack.asInstanceOf[Stack[Boolean]]
val startPeak = startStack.peak
val secondStack = startStack.pop()
val secondPeak = secondStack.pop().peak
StackMachine[Boolean](new Stack[Boolean](secondStack.push(startPeak || secondPeak).stack))
}
def xor: StackMachine[Boolean] = {
val startStack = this.stack.asInstanceOf[Stack[Boolean]]
val startPeak = startStack.peak
val secondStack = startStack.pop()
val secondPeak = secondStack.pop().peak
StackMachine[Boolean](new Stack[Boolean](secondStack.push(startPeak ^ secondPeak).stack))
}
def sum(input : T)(implicit N: Numeric[T]) = {
val startStack = this.stack
val startPeak = startStack.peak
val secondStack = startStack.pop()
StackMachine[T](new Stack[T](secondStack.push(N.plus(startPeak,input)).stack))
}
def dif(input : T)(implicit N: Numeric[T]) = {
val startStack = this.stack
val startPeak = startStack.peak
val secondStack = startStack.pop()
StackMachine[T](new Stack[T](secondStack.push(N.minus(startPeak,input)).stack))
}
def mul(input : T)(implicit N: Numeric[T]) = {
val startStack = this.stack
val startPeak = startStack.peak
val secondStack = startStack.pop()
StackMachine[T](new Stack[T](secondStack.push(N.toDouble(startPeak).*(N.toDouble(input)).asInstanceOf[T]).stack))
}
def div(input : T)(implicit N: Numeric[T]) = {
val startStack = this.stack
val startPeak = startStack.peak
val secondStack = startStack.pop()
StackMachine[T](new Stack[T](secondStack.push(N.toDouble(startPeak)./(N.toDouble(input)).asInstanceOf[T]).stack))
}
def min(input : T)(implicit N: Numeric[T]) = {
val startStack = this.stack
val startPeak = startStack.peak
val secondStack = startStack.pop()
StackMachine[T](new Stack[T](secondStack.push(N.min(startPeak,input)).stack))
}
def max(input : T)(implicit N: Numeric[T]) = {
val startStack = this.stack
val startPeak = startStack.peak
val secondStack = startStack.pop()
StackMachine[T](new Stack[T](secondStack.push(N.max(startPeak,input)).stack))
}
}
But this is wrong, because operations shouldnt have input parameters because all variables have to be taken from Stack. More than that, this way i cant create diff and mul functions.
I thoght to make StackMachine[T] abstract and use imlplicit object, but failed because in that case, my functions cant return StackMachine. May be i just dont understand implicit well enough or there is another way of doing this?
Then yeah it seems the project is intended to be solved using a typeclass.
For example, see this small one for Boolean-like and:
sealed trait BehavesAsBoolean[T] {
def and(t1: T, t2: T): T
}
object BehavesAsBoolean {
implicit final val BooleanBehavesAsBoolean: BehavesAsBoolean[Boolean] =
new BehavesAsBoolean[Boolean] {
override def and(b1: Boolean, b2: Boolean): Boolean =
b1 && b2
}
}
final class StackMachine[T](stack: Stack[T]) {
def and(implicit ev: BehavesAsBoolean[T]): Option[StackMachine[T]] =
for {
// I changed the implementation of pop to return an Option[(T, Stack[T])]
(b1, s2) <- stack.pop
(b2, s3) <- s2.pop
} yield {
new StackMachine(s3.push(ev.and(b1, b2)))
}
}
Of course, you may still prefer to throw exceptions rather than using Option
Anyways, I hope this helps you to finish the code.
You can see the code running here.

How to create Shapeless HMap from from var arg list

I'm creating shapeless HMap using the code like below from scala-exercises.
import shapeless.HMap
class BiMapIS[K, V]
implicit val intToString = new BiMapIS[Int, String]
implicit val stringToInt = new BiMapIS[String, Int]
val hm = HMap[BiMapIS](23 -> "foo", "bar" -> 13)
I would like to create HMap from variable arguments as below (I'm having long list of arguments so just checking whether I can simplify the code littlebit) -
import shapeless.{HMap, HNil}
import java.util.{List => JList}
val entities: JList[(_, _)] = ???
class BiMapIS[K, V]
implicit val intToString = new BiMapIS[Int, String]
implicit val stringToInt = new BiMapIS[String, Int]
import collection.JavaConverters._
val entitiesSeq = entities.asScala.toList
val hm = HMap[BiMapIS](entitiesSeq:_*)
Is there any way I can create HMap from variable args?
I'm using shapless 2.33 with scala 2.12 https://mvnrepository.com/artifact/com.chuusai/shapeless_2.12/2.3.3
Try
val entitiesSeq = entities.asScala.toMap[Any, Any]
val hm = new HMap[BiMapIS](entitiesSeq)

writing a custom get method for a scala map

I have a map which is something like
val m = Map("foo" -> "bar", "faz" -> "baz")
I need to write a custom get method, so that the key can be the key in the map with a number in the end.
So for example:
m.get("foo1") should return "bar"
I am looking for a good scala pattern to solve this problem.
Also I am generating the above map from a for loop using yield, so I can't do something like this
val m = CustomMap("foo" -> "bar")
Any solutions will be appreciated.
Thanks
First of all, you can generate a map from a for comprehension, and then convert it to CustomMap. You just need to define a
def apply(map: Map[String, String]) = CustomMap(map.toSeq :_*) in CustomMap - then you can do val m = CustomMap( for { ... } yield ... )
Secondly, if it doesn't have to be named get (it probably shouldn't be anyway), you can do this sort of thing with an implicit:
object PimpMyMap {
val pref = ".*?(\\d+)".r
implicit class Pimped[V](val map: Map[String,V]) extends AnyVal {
def getPrefix(key: String): Option[V] = map.get(key).orElse { key match {
case pref(k) => map.get(k)
case _ => None
}
}
Now you can write things like:
import PimpMyMap._
val map = Map("foo" -> 1)
val one = map.getPrefix("foo123") // Some(1)
val anotherOne = map.getPrefix("foo") // also Some(1);
You can do this with an implicit class and implicit conversion:
import scala.language.implicitConversions
object MapHelpers {
implicit def optionStringToString(maybeS: Option[String]): String = maybeS.getOrElse("")
implicit class MapWithIntKey(val m: Map[String, String]) extends Map[String, String] {
override def get(key: String): Option[String] = {
val intRegex = """(\d+)""".r
val keyWithoutInt = intRegex
.findFirstMatchIn(key)
.map(int => {
val idx = key.indexOf(int.toString)
key.slice(0, idx)
})
.getOrElse(key)
m.get(keyWithoutInt)
}
def +[V1 >: String](
kv: (String, V1)): scala.collection.immutable.Map[String, V1] = m + kv
def -(key: String): scala.collection.immutable.Map[String, String] = m - key
def iterator: Iterator[(String, String)] = m.iterator
}
}
object App {
import MapHelpers._
def testMapImplicit(): Unit = {
val myMap: MapWithIntKey = Map("foo" -> "bar", "faz" -> "baz")
val result: String = myMap.get("foo1")
println("result", result) // bar
}
}
Working Scastie
If you have a sure way to get the real key from the fake key, you can do this with Map.withDefault:
class CustomMap[K, +V] private (underlying: Map[K, Option[V]]) {
def get(k: K): Option[V] = underlying(k)
}
object CustomMap {
def apply[K, V](original: Map[K, V], keyReducer: K => K) = new CustomMap(originalMap.
mapValues(Some(_)).
withDefault(k => originalMap.get(keyReducer(k))
)
}
In your case, you can use this with
val stringKeyReducer: String => String = k.reverse.dropWhile(_.isDigit).reverse
to drop the digits at the end of your strings, so
CustomMap(Map("foo" -> "bar"), stringKeyReducer).get("foo1") = Some("bar")
Here is solution which combines both the answers.
import scala.language.implicitConversions
object MapHelpers {
implicit def optionStringToString(maybeS: Option[String]): String = maybeS.getOrElse("")
implicit class MapWithIntKey(val m: Map[String, String]) extends Map[String, String] {
override def get(key: String): Option[String] = {
val prefix = "(.*?)\\d+".r
m.get(key).orElse{
key match {
case prefix(p) => m.get(p)
case _ => None
}
}
}
def +[V1 >: String](kv: (String, V1)): scala.collection.immutable.Map[String, V1] = m + kv
def -(key: String): scala.collection.immutable.Map[String, String] = m - key
def iterator: Iterator[(String, String)] = m.iterator
}
}
object App {
import MapHelpers._
def testMapImplicit(): Unit = {
val myMap: MapWithIntKey = Map("foo" -> "bar", "faz" -> "baz")
println("result - number match ", myMap.get("foo1"))
println("result - exact match ", myMap.get("foo"))
}
}
App.testMapImplicit()
Working Scastie

Ordering of implicit method and mapping with shapeless

The ordering of implicit seems to matter when using shapeless.
Look at the example code below where it will not work.
import shapeless._
case class Userz(i: Int, j: String, k: Option[Boolean])
object r {
def func(): Userz = {
val a = Userz(100, "UserA", Some(false))
val b = Userz(400, "UserB", None)
val genA = Generic[Userz].to(a)
val genB = Generic[Userz].to(b)
val genC = genA zip genB
val genD = genC.map(Mergerz)
val User = Generic[Userz].from(genD)
return User
}
}
object Mergerz extends Poly1 {
implicit def caseInt = at[(Int, Int)] {a => a._1 + a._2}
implicit def caseString = at[(String, String)] {a => a._1 + a._2}
implicit def caseBoolean = at[(Option[Boolean], Option[Boolean])] {a => Some(a._1.getOrElse(a._2.getOrElse(false)))}
}
r.func()
And the code below which will work
import shapeless._
case class Userz(i: Int, j: String, k: Option[Boolean])
object Mergerz extends Poly1 {
implicit def caseInt = at[(Int, Int)] {a => a._1 + a._2}
implicit def caseString = at[(String, String)] {a => a._1 + a._2}
implicit def caseBoolean = at[(Option[Boolean], Option[Boolean])] {a => Some(a._1.getOrElse(a._2.getOrElse(false)))}
}
object r {
def func(): Userz = {
val a = Userz(100, "UserA", Some(false))
val b = Userz(400, "UserB", None)
val genA = Generic[Userz].to(a)
val genB = Generic[Userz].to(b)
val genC = genA zip genB
val genD = genC.map(Mergerz)
val User = Generic[Userz].from(genD)
return User
}
}
r.func()
My question is, why does the order matters? I already tried importing the implicits which doesn't work.
Because type inference within a file basically works top-to-bottom. When it's typechecking func, it hasn't gotten to caseInt etc and doesn't know they are suitable. Annotating their types should work as well, and is generally recommended for implicits, but it may be problematic when using a library like Shapeless: see Shapeless not finding implicits in test, but can in REPL for an example.

Is it possible to 'transform' a HMap into another HMap

If I have a Shapeless HMap[MappingA] (with implicits properly defined for the type MappingA[K, V]), can I type-safely transform/map it to a HMap[MappingB].
class MappingA[K, V]
implicit val intToString = new MappingA[Int, String]
implicit val stringToInt = new MappingA[String, Int]
class MappingB[K, V]
implicit val longToString = new MappingA[Long, String]
implicit val stringToLong = new MappingA[String, Long]
val hm1 = HMap[MappingA](1 -> "one", "two" -> 2)
// How to...
val hm2: HMap[MappingB] = ??? // transform/map hm1
// expected for hm2 in this basic example
// HMap[MappingB](1L -> "one", "two" -> 2L)