All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.pickling.generator.sourcegen.scala Maven / Gradle / Ivy

The newest version!
package scala.pickling
package generator

import scala.reflect.api.Universe

/** This is the portion of code which can actually generate a pickler/unpickler object instance
  * from the IR AST.
  *
  *
  */
private[pickling]  trait SourceGenerator extends Macro with FastTypeTagMacros {
  import c.universe._

  def pickleNull(builder: c.Tree): c.Tree =
    q"""_root_.scala.pickling.Defaults.nullPickler.pickle(null, $builder)"""



  def allowNonExistentField(impl: Tree): c.Tree = {
    q"""try $impl catch {
            case _: _root_.scala.pickling.PicklingException =>
            // TODO - Don't just ignore bad things, figure out how to actually read the class file for reals.
            // We could basically by blocked by scala here, though.
          }"""
  }
  def generatePickleImplFromAst(picklerAst: PicklerAst): c.Tree = {

    def genGetField(x: GetField): c.Tree = {
      def pickleLogic(isStaticallyElided: Boolean, fieldValue: Tree, tpe: Type): Tree = {
        val fieldPickler = c.fresh(newTermName("fieldPickler"))
        val elideHint =
          if (isStaticallyElided) q"b.hintElidedType($fieldPickler.tag)" else q""
        // NOTE; This will look up an IMPLICIT pickler for the value, based on the type.
        //       This is how we chain our macros to find all the valid types.
        q"""
           val $fieldPickler = _root_.scala.Predef.implicitly[_root_.scala.pickling.Pickler[$tpe]]
           $elideHint
           $fieldPickler.pickle($fieldValue, b)"""
      }

      def putField(getterLogic: Tree, isStaticallyElided: Boolean, tpe: Type): c.Tree =
        q"builder.putField(${x.name}, b => ${pickleLogic(isStaticallyElided, getterLogic, tpe)})"

      x.getter match {
        case x: IrField =>
          val tpe = x.tpe[c.universe.type](c.universe)
          val staticallyElided = tpe.isEffectivelyFinal || tpe.isEffectivelyPrimitive
          if(x.isScala || !x.isPublic) {
            // We always have to use reflection for scala fields right now.  Additionally, for Scala fields, we
            // actually have no idea if they exist at runtime, so we allow failure for now, which is EVIL, but we have no alternative.
            val result = reflectivelyGet(newTermName("picklee"), x)(fm => fm)
            val rTerm = c.fresh(newTermName("result"))
            val logic = q"""val $rTerm = { ..$result }
                            ${putField(q"$rTerm.asInstanceOf[$tpe]", staticallyElided, tpe)}"""
            if(x.isScala) allowNonExistentField(logic) else logic
          } else putField(q"picklee.${newTermName(x.fieldName)}", staticallyElided, tpe)
        case _: IrConstructor =>
          // This is a logic erorr
          sys.error(s"Pickling logic error.  Found constructor when trying to pickle field ${x.name}.")
        case y: IrMethod =>
          val tpe = y.returnType(c.universe)
          val staticallyElided = {
            // TODO - This is too naive of a determination.  In practice, we almost want an "always elide" and "never elide" mode.
            tpe.isEffectivelyFinal || tpe.isEffectivelyPrimitive
          }
          if (y.isPublic) putField(q"picklee.${newTermName(y.methodName)}", staticallyElided, tpe)
          else {
            val result = reflectivelyGet(newTermName("picklee"), y)(fm => putField(q"${fm}.asInstanceOf[${y.returnType(u).asInstanceOf[c.Type]}]", staticallyElided, tpe))
            q"""..$result"""
          }
      }
    }


    def genSubclassDispatch(x: SubclassDispatch): c.Tree = {
      val tpe = x.parent.tpe[c.universe.type](c.universe)
      val clazzName = newTermName("clazz")
        val compileTimeDispatch: List[CaseDef] = (x.subClasses map { subtpe =>
        val tpe = subtpe.tpe[c.universe.type](c.universe)
        CaseDef(Bind(clazzName, Ident(nme.WILDCARD)), q"clazz == classOf[$tpe]", createPickler(tpe, q"builder"))
      })(collection.breakOut)


      val failDispatch = {
        val dispatcheeNames = x.subClasses.map(_.className).mkString(", ")
        val otherTermName = newTermName("other")
        val throwUnknownTag =
          if(x.subClasses.isEmpty) {
            q"""throw _root_.scala.pickling.PicklingException("Class " + clazz + " not recognized by pickler")"""
        } else q"""throw _root_.scala.pickling.PicklingException("Class " + clazz + " not recognized by pickler, looking for one of: " + $dispatcheeNames)"""
        CaseDef(Bind(otherTermName, Ident(nme.WILDCARD)), throwUnknownTag)
      }
      val runtimeDispatch = CaseDef(Ident(nme.WILDCARD), EmptyTree, createRuntimePickler(q"builder"))
      val unknownDispatch =
        if(x.lookupRuntime) List(runtimeDispatch)
        else List(failDispatch)



      val picklerLookup = q"""
        val clazz = if (picklee != null) picklee.getClass else null
        ${Match(q"clazz", compileTimeDispatch ++ unknownDispatch)}
      """
      val subclasses =
        q"""
          val pickler: _root_.scala.pickling.Pickler[_] = $picklerLookup
          pickler.asInstanceOf[_root_.scala.pickling.Pickler[$tpe]].pickle(picklee, builder)
        """
      // If we have parent behavior, we need to do a quick instanceOf check first.
      x.parentBehavior match {
        case None => subclasses
        case Some(b) =>
          val parentTpe = x.parent.tpe[c.universe.type](c.universe)
          val impl = generatePickleImplFromAst(b)
          q"""if(classOf[$parentTpe] == picklee.getClass) $impl else $subclasses"""
      }
    }
    def genPickleEntry(op: PickleEntry): c.Tree = {
      val nested =
        op.ops.toList map genPickleOp
      val oid = c.fresh(newTermName("oid"))
      // TODO - hint known size
      val shareHint: List[c.Tree] =
        if(shareNothing) List(q"()")
        else List(q"val $oid = _root_.scala.pickling.internal.`package`.lookupPicklee(picklee)", q"builder.hintOid($oid)")
      /*val shareLogic: c.Tree =
        if(shareNothing) q"..$nested"
        else q"if($oid == -1) { ..$nested }"*/
      q"""
         ..$shareHint
         builder.beginEntry(picklee, tag)
         ..$nested
         builder.endEntry()
       """
    }
    def genPickleOp(op: PicklerAst): c.Tree =
      op match {
        case PickleBehavior(ops) => q"""..${ops.map(genPickleOp)}"""
        case x: GetField => genGetField(x)
        case x: PickleEntry => genPickleEntry(x)
        case x: SubclassDispatch => genSubclassDispatch(x)
        case x: PickleExternalizable => genExternalizablePickle(newTermName("picklee"), newTermName("builder"), x)
      }
    genPickleOp(picklerAst)
  }

  // Code which will lookup a runtime pickler.
  def createRuntimePickler(builder: c.Tree): c.Tree = q"""
    val classLoader = this.getClass.getClassLoader
    _root_.scala.pickling.internal.GRL.lock()
    val tag = try {
      _root_.scala.pickling.FastTypeTag.mkRaw(clazz, _root_.scala.reflect.runtime.universe.runtimeMirror(classLoader))
    } finally _root_.scala.pickling.internal.GRL.unlock()
    _root_.scala.pickling.internal.`package`.currentRuntime.picklers.genPickler(classLoader, clazz, tag)
  """

  def createPickler(tpe: c.Type, builder: c.Tree): c.Tree = q"""
    val pickler = _root_.scala.Predef.implicitly[_root_.scala.pickling.Pickler[$tpe]]
    pickler
  """

  def checkNullPickle(pickleLogic: c.Tree): c.Tree =
    q"""
        if(null == picklee) {
          _root_.scala.pickling.Defaults.nullPickler.pickle(null, builder)
        } else $pickleLogic"""

  def genPicklerLogic[T: c.WeakTypeTag](picklerAst: PicklerAst): c.Tree = {
    // TODO - The pickler logic should actually check to make sure the thing passed is actually a FOO, and
    //        If it is not, we delegate to a runtime pickler.
    //        ALTHOUGH, we could, instead, delegate that as function of the AST that we can encode, so
    //        The presence of runtime code is controlled by generation algorithms, rather than the code generator.
    //        We could still disable the generation here.
    //        This is somethign the current algorithm does which we do not do.
    checkNullPickle(generatePickleImplFromAst(picklerAst))
  }

  def genUnpicklerLogic[T: c.WeakTypeTag](unpicklerAst: UnpicklerAst): c.Tree = {
    val unpickleLogic = generateUnpickleImplFromAst(unpicklerAst)
    // Handle null + Ref types first
    unpickleNull(unpickleRef(unpickleLogic))
  }

  /** Wraps unpickle logic with the logic on how to handle `null` values.   Assumes the term `tagKey` is available. */
  def unpickleNull(unpickleLogic: c.Tree): c.Tree = {
    q""" if (tagKey == _root_.scala.pickling.FastTypeTag.Null.key) null else $unpickleLogic"""
  }
  /** Wraps unpickle logic with the logic on how to handle `Ref` values.   Assumes the terms `tagKey` and `reader` are available. */
  def unpickleRef(unpickleLogic: c.Tree): c.Tree = {
    q"""if (tagKey == _root_.scala.pickling.FastTypeTag.Ref.key) {
         _root_.scala.Predef.implicitly[_root_.scala.pickling.Unpickler[_root_.scala.pickling.refs.Ref]].unpickle(tagKey, reader)
       } else $unpickleLogic"""
  }

  def readField(name: String, tpe: Type): c.Tree = {
    val readerName = c.fresh(newTermName("reader"))
    val unpicklerName    = c.fresh(newTermName("unpickler$unpickle$"))
    // TODO - is this the right place to do this?
    val staticHint       = if (tpe.isEffectivelyFinal) q"$readerName.hintElidedType($unpicklerName.tag)" else q"";

    val resultName = c.fresh(newTermName("result"))
    // TODO - may be able to drop locally.
      q"""
         _root_.scala.Predef.locally {
           val $readerName = reader.readField($name)
           var $unpicklerName: _root_.scala.pickling.Unpickler[$tpe] = null
           $unpicklerName = _root_.scala.Predef.implicitly[_root_.scala.pickling.Unpickler[$tpe]]
           $staticHint
           val typeString = $readerName.beginEntry()
           val $resultName = $unpicklerName.unpickle(typeString, $readerName)
           $readerName.endEntry()
           $resultName.asInstanceOf[$tpe]
         }
       """
  }


  def genConstructorUnpickle(cons: CallConstructor): c.Tree = {
    // Note, this is a bit ugly.
    var idx = 0
    val names = cons.fieldNames
    val tpess = cons.constructor.parameterTypes[c.universe.type](c.universe)
    val argss =
      tpess map { tpes =>
         tpes map { tpe =>
           val name = names(idx)
           idx += 1
           readField(name, tpe)
         }
      }
    val tpe = cons.constructor.returnType[c.universe.type](c.universe)
    // TODO - Handle reflective case.
    if(cons.requiresReflection) sys.error(s"Unable to reflectively call constructors, currently.")
    else {
      if(cons.constructor.parameterNames.isEmpty) q"""new ${tpe}"""
      else {
        q"new $tpe(...$argss)"
      }
    }
  }
  def genCallModuleFactory(cons: CallModuleFactory): c.Tree = {
    // Note, this is a bit ugly.
    var idx = 0
    val names = cons.fields
    val tpess = cons.factoryMethod.parameterTypes[c.universe.type](c.universe)
    val argss =
      tpess map { tpes =>
        tpes map { tpe =>
          val name = names(idx)
          idx += 1
          readField(name, tpe)
        }
      }
    val tpe = cons.factoryMethod.returnType[c.universe.type](c.universe)
    // TODO - Handle reflective case.
    val result = if(cons.requiresReflection) sys.error(s"Unable to reflectively call factory methods, currently.")
    else {
      if(cons.factoryMethod.parameterNames.isEmpty) q"${tpe}.${newTermName(cons.factoryMethod.methodName)}()"
      else {
        q"${tpe}.${newTermName(cons.factoryMethod.methodName)}(...$argss)"
      }
    }
    result
  }

  /** Creates a `set` operation that reads a value from the pickle and writes it into the object.
    *
    * Note: This assumes there exists a `result` name in scope which is the currently instantiated unpickle object.
    */
  def genSetField(s: SetField): c.Tree = {
    s.setter match {
      case x: IrMethod =>
        x.parameterTypes[c.universe.type ](c.universe) match {
          case List(List(tpe)) =>
            val read = readField(s.name, tpe)
            if(x.isPublic) {
              q"""
                 result.${newTermName(x.methodName)}($read)
               """
            } else reflectivelySet(newTermName("result"), x, read)
          case x => sys.error(s"Cannot handle a setting method that does not take exactly one parameter, found parameters: $x")
        }

      case x: IrField =>
        val tpe = x.tpe[c.universe.type](c.universe)
        val read = readField(s.name, tpe)
        val staticallyElided = tpe.isEffectivelyFinal || tpe.isEffectivelyPrimitive
        if(x.isScala || !x.isPublic || x.isFinal) {
          reflectivelySet(newTermName("result"), x, read)
        } else q"""result.${newTermName(x.fieldName)} = $read"""
    }

  }

  val ShortType = typeOf[Short]
  val CharType = typeOf[Char]
  val IntType = typeOf[Int]
  val LongType = typeOf[Long]
  val FloatType = typeOf[Float]
  val DoubleType = typeOf[Double]
  val BooleanType = typeOf[Boolean]
  /** This will lift any primitive value into  the java-boxed version (usefulf or reflective code.) */
  def liftPrimitives(value: c.Tree, tpe: Type): c.Tree = {
    tpe match {
      case ShortType => q"new _root_.java.lang.Short($value)"
      case CharType => q"new _root_.java.lang.Character($value)"
      case IntType => q"new _root_.java.lang.Integer($value)"
      case LongType => q"new _root_.java.lang.Long($value)"
      case FloatType => q"new _root_.java.lang.Float($value)"
      case DoubleType => q"new _root_.java.lang.Double($value)"
      case BooleanType => q"new _root_.java.lang.Boolean($value)"
      case _ => value
    }
  }


  def createUnpickler(tpe: Type): Tree =
    q"_root_.scala.Predef.implicitly[_root_.scala.pickling.Unpickler[$tpe]]"
  def genSubclassUnpickler(x: SubclassUnpicklerDelegation): c.Tree = {
    val tpe = x.parent.tpe[c.universe.type](c.universe)
    val defaultCase =
      if(x.lookupRuntime) CaseDef(Ident(nme.WILDCARD), EmptyTree, q"_root_.scala.pickling.internal.`package`.currentRuntime.picklers.genUnpickler(_root_.scala.pickling.internal.`package`.currentMirror, tagKey)")
      else CaseDef(Ident(nme.WILDCARD), EmptyTree, q"""throw new _root_.scala.pickling.PicklingException("Cannot unpickle, Unexpected tag: " + tagKey + " not recognized.")""")
    val subClassCases =
       x.subClasses.toList map { sc =>
         val stpe = sc.tpe[c.universe.type](c.universe)
         val skey = stpe.key
         CaseDef(Literal(Constant(skey)), EmptyTree, createUnpickler(stpe))
       }
    val subClass =
      q"""val unpickler: _root_.scala.pickling.Unpickler[_] = ${Match(q"tagKey", subClassCases ++ List(defaultCase))}
        unpickler.asInstanceOf[_root_.scala.pickling.Unpickler[$tpe]].unpickle(tagKey, reader)
        """
    x.parentBehavior match {
      case None => subClass
      case Some(p) =>
        val ptree = generateUnpickleImplFromAst(p)
        q"""if(tagKey == ${tpe.key}) $ptree else $subClass"""
    }
  }
  def genUnpickleSingleton(s: UnpickleSingleton): c.Tree = {
    val tpe = s.tpe.tpe[c.universe.type](c.universe)
    val m = tpe.typeSymbol.asClass.module
    q"$m"
  }

  def genAllocateInstance(x: AllocateInstance): c.Tree = {
    val tpe = x.tpe.tpe[c.universe.type](c.universe)
    q"""_root_.scala.concurrent.util.Unsafe.instance.allocateInstance(classOf[$tpe]).asInstanceOf[$tpe]"""
  }

  def generateUnpickleImplFromAst(unpicklerAst: UnpicklerAst): c.Tree = {
    unpicklerAst match {
      case c: CallConstructor => genConstructorUnpickle(c)
      case c: CallModuleFactory => genCallModuleFactory(c)
      case x: SetField => genSetField(x)
      case x: SubclassUnpicklerDelegation => genSubclassUnpickler(x)
      case x: UnpickleSingleton => genUnpickleSingleton(x)
      case x: AllocateInstance => genAllocateInstance(x)
        // TODO - This is kind of hacky, should be a temproary workaround for a better solution.
      case x: UnpickleExternalizable => genExternalizablUnPickle(newTermName("reader"), x)
      case x: UnpickleBehavior =>
        val behavior = x.operations.map(generateUnpickleImplFromAst).toList
        // TODO - This is kind of hacky.  We're trying to make sure during unpickling we always register/unregister appropriately...
        //  This is actually guarranteed to fail if "structural sharing" is done in a way such that the shared "tree"
        // of objects is needed to call the constructor. In this isntance, we are unable to register the new object
        // before constructing it, leading to out of order OIDs and a failed deserialize.
        // This is why we're deprecating sharing.
        x.operations match {
          case List() => q"null"
          case List(head: SubclassUnpicklerDelegation) => generateUnpickleImplFromAst(head)
            // TODO - Can we assume that ever additional operation is something which manipualtes the result?
            // Once again, flakyness here is why we're deprecating sharing.
          case hd :: tail =>
            val hdTree = generateUnpickleImplFromAst(hd)
            val tlTree = tail.map(generateUnpickleImplFromAst)
            // Note: We are *always* generating the unpickle sharing code, in the event that
            //       someone pickles with sharing, but we try to unpickle without it.
            q"""_root_.scala.Predef.locally {
                  val oid = _root_.scala.pickling.internal.`package`.preregisterUnpicklee()
                  val result = $hdTree;
                  _root_.scala.pickling.internal.`package`.registerUnpicklee(result, oid)
                  ..$tlTree;
                  result
                }"""
        }
    }
  }


  /** generates the tree which will construct + return a new instance of a Pickler class, capable of
    * pickling an instance of type T, using the behavior outlined by the PicklerAst.
    */
  def generatePicklerClass[T: c.WeakTypeTag](picklerAst: PicklerAst): c.Tree = {
    val tpe = computeType[T]
    val picklerName = c.fresh((syntheticBaseName(tpe) + "Pickler"): TermName)
    val createTagTree = super[FastTypeTagMacros].impl[T]
    q"""
      _root_.scala.Predef.locally {
        implicit object $picklerName extends _root_.scala.pickling.Pickler[$tpe] with _root_.scala.pickling.Generated {
          def pickle(picklee: $tpe, builder: _root_.scala.pickling.PBuilder): _root_.scala.Unit = ${genPicklerLogic[T](picklerAst)}
          def tag: _root_.scala.pickling.FastTypeTag[$tpe] = $createTagTree
        }
        $picklerName
      }
    """
  }

  def generateUnpicklerClass[T: c.WeakTypeTag](unpicklerAst: UnpicklerAst): c.Tree = {
    val tpe = computeType[T]
    val unpicklerName = c.fresh((syntheticBaseName(tpe) + "Unpickler"): TermName)
    val createTagTree = super[FastTypeTagMacros].impl[T]
    val unpickleLogic = genUnpicklerLogic[T](unpicklerAst)
    q"""
       _root_.scala.Predef.locally {
          implicit object $unpicklerName extends  _root_.scala.pickling.Unpickler[$tpe] with _root_.scala.pickling.Generated {
            def unpickle(tagKey: _root_.java.lang.String, reader: _root_.scala.pickling.PReader): _root_.scala.Any = $unpickleLogic
            def tag: _root_.scala.pickling.FastTypeTag[$tpe] = $createTagTree
          }
          $unpicklerName : _root_.scala.pickling.Unpickler[$tpe] with _root_.scala.pickling.Generated
       }
     """
  }

  def generatePicklerUnpicklerClass[T: c.WeakTypeTag](impl: PickleUnpickleImplementation): c.Tree = {
    val tpe = computeType[T]
    val name = c.fresh((syntheticBaseName(tpe) + "PicklerUnpickler"): TermName)
    val createTagTree = super[FastTypeTagMacros].impl[T]
    val unpickleLogic = genUnpicklerLogic[T](impl.unpickle)
    val pickleLogic = genPicklerLogic[T](impl.pickle)
    q"""
       _root_.scala.Predef.locally {
          implicit object $name extends _root_.scala.pickling.AbstractPicklerUnpickler[$tpe] with _root_.scala.pickling.Generated {
            //import _root_.scala.language.existentials
            override def pickle(picklee: $tpe, builder: _root_.scala.pickling.PBuilder): _root_.scala.Unit = $pickleLogic
            override def unpickle(tagKey: _root_.java.lang.String, reader: _root_.scala.pickling.PReader): _root_.scala.Any = $unpickleLogic
            override def tag: _root_.scala.pickling.FastTypeTag[$tpe] = $createTagTree
          }
          $name : _root_.scala.pickling.AbstractPicklerUnpickler[$tpe] with _root_.scala.pickling.Generated
       }
     """
  }


  /** Given a tree which unpickles a value, we regsiter that value with its OID hint so that
    * we can effectively deserialized later references.
    */
  def registerUnPickledRef(instantiationLogic: c.Tree): c.Tree = {
    // TODO - We may not want to ALWAYS do this, some kind of enabling flag...
    val instance = c.fresh(newTermName("instance"))
    q"""
              val oid = _root_.scala.pickling.internal.`package`.currentRuntime.refRegistry.unpickle.preregisterUnpicklee()
              val $instance = $instantiationLogic
              _root_.scala.pickling.internal.`package`.currentRuntime.refRegistry.unpickle.registerUnpicklee($instance, oid)
              $instance
            """
  }




  def computeType[T: c.WeakTypeTag]: Type = {
    val originalTpe = weakTypeOf[T]
    // Note: this makes it so modules work, things like foo.type.
    //       For some reason we get an issue with not having == defined on Class[_] otherwise.
    // TODO - fix this for certain primitive types, like Null, etc.
    if(originalTpe.termSymbol.isModule) originalTpe.widen
    else originalTpe
  }

  // -- Externalizable Hackery --
  def genExternalizablePickle(target: TermName, builder: TermName, pe: PickleExternalizable): c.Tree = {
    val out = c.fresh(newTermName("out"))
    val objectOutTpe = typeOf[scala.pickling.util.GenObjectOutput]
    val fieldName = "$ext"
    q"""val $out = new _root_.scala.pickling.util.GenObjectOutput
        $target.writeExternal($out)
        $builder.putField($fieldName, b =>
          _root_.scala.pickling.functions.pickleInto($out, b)
        )
     """
  }
  def genExternalizablUnPickle(reader: TermName, pe: UnpickleExternalizable): c.Tree = {
    val tpe = pe.tpe.tpe[c.universe.type](c.universe)
    val readerName = c.fresh(newTermName("readerName"))
    val target = c.fresh(newTermName("out"))
    val objectOutTpe = typeOf[scala.pickling.util.GenObjectOutput]
    val fieldName = "$ext"
    q"""
       val $target = _root_.scala.concurrent.util.Unsafe.instance.allocateInstance(classOf[$tpe]).asInstanceOf[$tpe]
       val $readerName = reader.readField($fieldName)
       val out = {
         val up = _root_.scala.Predef.implicitly[_root_.scala.pickling.Unpickler[$objectOutTpe]]
         up.unpickleEntry($readerName).asInstanceOf[$objectOutTpe]
       }
       val in = out.toInput
       $target.readExternal(in)
       $target
        """
  }


  // ---- Reflective Helper Methods ----

  def reflectivelyGet(target: TermName, value: IrMember)(body: c.Tree => c.Tree): List[c.Tree] = {
    // Note: We have chosen not to use scala reflection due to errors that occur in it.
    // TODO - Should we trap errors and return better error messages?
    // TODO - We should attempt to SAVE the reflective methods/fields somewhere so we aren't
    //        looking them up all the time.
    val valueTree =
      value match {
        case field: IrField =>
          val fieldTerm = c.fresh(newTermName("field"))
          val get = q"""
                val $fieldTerm = _root_.scala.pickling.internal.Reflect.getField($target.getClass, ${field.javaReflectionName})
                $fieldTerm.setAccessible(true)
                $fieldTerm.get($target)
                """
          get
        // Private scala methods may not encode normally for case classes.  This is a hack which goes after the field.
        // TODO - We should update this to look for the accessor method which scala generally exposes for the deconstructor.
        case mthd: IrMethod if mthd.isScala && mthd.isPrivate =>
          val methodTerm = c.fresh(newTermName("mthd"))
          // TODO - We may need to do a specialied lookup for magic named methods.
          q"""val $methodTerm = _root_.scala.pickling.internal.Reflect.getMethod($target.getClass, ${mthd.javaReflectionName}, Array())
              $methodTerm.setAccessible(true)
              $methodTerm.invoke($target)
              """
        case mthd: IrMethod =>
          val methodTerm = c.fresh(newTermName("mthd"))
          q"""val $methodTerm = _root_.scala.pickling.internal.Reflect.getMethod($target.getClass, ${mthd.javaReflectionName}, Array())
              $methodTerm.setAccessible(true)
              $methodTerm.invoke($target)"""
      }
    List(body(valueTree))
  }
  def reflectivelySet(target: TermName, setter: IrMember, value: c.Tree): c.Tree = {
    // Note: We've chosen not to use scala reflection due to it being buggy.
    // TODO - Should we trap errors and return better error messages?
    // TODO - We should attempt to SAVE the reflective methods/fields somewhere so we aren't
    //        looking them up all the time.
    setter match {
      case field: IrField =>
        val fieldTerm = c.fresh(newTermName("field"))
        val result = q"""
                 val $fieldTerm = _root_.scala.pickling.internal.Reflect.getField($target.getClass, ${field.javaReflectionName})
                 $fieldTerm.setAccessible(true)
                 $fieldTerm.set($target, ${liftPrimitives(value, field.tpe[c.universe.type](c.universe))})"""
        // Workaround for issues with not being able to accurate read scala symbols.
        if(field.isScala) allowNonExistentField(result) else result
      case mthd: IrMethod =>
        val methodTerm = c.fresh(newTermName("mthd"))
        // TODO - We should ensure types align.
        val List(List(tpe)) = mthd.parameterTypes[c.universe.type](c.universe)
        q"""
                val $methodTerm = _root_.scala.pickling.internal.Reflect.getMethod($target.getClass, ${mthd.javaReflectionName}, Array(classOf[$tpe]))
                $methodTerm.setAccessible(true)
                $methodTerm.invoke($target, ${liftPrimitives(value, tpe)})
              """
    }

  }

  // -- End Reflective Helper Methods --

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy