/* Copyright 2010 Aaron J. Radke Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package cc.drx import scala.language.existentials object JailBreak{ //--wrap objects with jailbreak configuration def apply[A](obj:A):JailBreak[A] = JailBreak(obj,obj.getClass) def apply[A](obj:A,className:String):JailBreak[A] = JailBreak(obj,forName(className)) //--construct new objection def companion(className:String):JailBreak[_] = apply(className + "$") def apply(className:String):JailBreak[_] = apply(className,None) def apply(className:String, constructorName:String):JailBreak[_] = apply(className,Some(constructorName)) def apply(className:String, constructor:Option[String]=None):JailBreak[_] = { val klass = forName(className) val obj = if(className endsWith "$") klass.getField("MODULE$").get(()) else constructor match{ case None => klass.getConstructor().newInstance() //java8: klass.newInstance case Some(name) => klass.getMethod(name).invoke(null) } JailBreak(obj,klass) } //TODO base JailBreak around a Class instead of an object private def forName(className:String) = java.lang.Class.forName(className) private def findClass(className:String):Option[java.lang.Class[_]] = //a type hole exists explictly becuase we don't reflectively know what it actually is Try{forName(className)}.toOption def field[A](name:String):Try[A] = Try{ val args = name.split('.') val className = args.init.mkString(".") val fieldName = args.last val c = forName(className) c.getField(fieldName).get(c).asInstanceOf[A] } } /**Simple scala type inferencing wrapper around the java reflection api to break into protected fields * * */ case class JailBreak[A](obj:A,klass:Class[_]){ private def field(fieldName:String):java.lang.reflect.Field = { val reflectedField = klass.getDeclaredField(fieldName) reflectedField.setAccessible(true) //we need to be able to set this private method reflectedField } private def method(name:String,numArgs:Int):java.lang.reflect.Method = { val mopt = methods.filter(_.getName == name).find(_.getParameterTypes.size == numArgs) // println(s"found method $mopt") if(mopt.isEmpty) throw(new java.lang.RuntimeException( s"method name '$name' with $numArgs arguments does not exist in $this" )) val m = mopt.get m.setAccessible(true) //we need to be able to set this private method m } def fields = klass.getDeclaredFields lazy val methods = klass.getDeclaredMethods def update[B](fieldName:String,value:B):Unit = field(fieldName).set(obj,value) def apply[B](fieldName:String):B = field(fieldName).get(obj).asInstanceOf[B] // def /[B](fieldName:Symbol):B = field(fieldName.name).get(obj).asInstanceOf[B] //supper simple access, assumes field exists and the first name found matches the arguments... warning this may causes some things to break badly if these assumptiosn are not met, but using jailbreak itself is a dangerous thing so if you decide to be dangerous lets make it easy def invoke[B](methodName:String):B = method(methodName, 0).invoke(obj).asInstanceOf[B] def invoke[B](methodName:String,arg0:AnyRef):B = method(methodName,1).invoke(obj,arg0).asInstanceOf[B] //scala.AnyRef => java.lang.Object def invoke[B](methodName:String,arg0:AnyRef,arg1:AnyRef):B = method(methodName, 2).invoke(obj,arg0,arg1).asInstanceOf[B] }