|  | 
|  | 1 | +package scala.runtime | 
|  | 2 | + | 
|  | 3 | +import java.lang.invoke._ | 
|  | 4 | + | 
|  | 5 | +/** | 
|  | 6 | + * This class is only intended to be called by synthetic `$deserializeLambda$` method that the Scala 2.12 | 
|  | 7 | + * compiler will add to classes hosting lambdas. | 
|  | 8 | + * | 
|  | 9 | + * It is not intended to be consumed directly. | 
|  | 10 | + */ | 
|  | 11 | +object LambdaDeserializer { | 
|  | 12 | +  /** | 
|  | 13 | +   * Deserialize a lambda by calling `LambdaMetafactory.altMetafactory` to spin up a lambda class | 
|  | 14 | +   * and instantiating this class with the captured arguments. | 
|  | 15 | +   * | 
|  | 16 | +   * A cache may be provided to ensure that subsequent deserialization of the same lambda expression | 
|  | 17 | +   * is cheap, it amounts to a reflective call to the constructor of the previously created class. | 
|  | 18 | +   * However, deserialization of the same lambda expression is not guaranteed to use the same class, | 
|  | 19 | +   * concurrent deserialization of the same lambda expression may spin up more than one class. | 
|  | 20 | +   * | 
|  | 21 | +   * Assumptions: | 
|  | 22 | +   *  - No additional marker interfaces are required beyond `{java.io,scala.}Serializable`. These are | 
|  | 23 | +   *    not stored in `SerializedLambda`, so we can't reconstitute them. | 
|  | 24 | +   *  - No additional bridge methods are passed to `altMetafactory`. Again, these are not stored. | 
|  | 25 | +   * | 
|  | 26 | +   * @param lookup      The factory for method handles. Must have access to the implementation method, the | 
|  | 27 | +   *                    functional interface class, and `java.io.Serializable` or `scala.Serializable` as | 
|  | 28 | +   *                    required. | 
|  | 29 | +   * @param cache       A cache used to avoid spinning up a class for each deserialization of a given lambda. May be `null` | 
|  | 30 | +   * @param serialized  The lambda to deserialize. Note that this is typically created by the `readResolve` | 
|  | 31 | +   *                    member of the anonymous class created by `LambdaMetaFactory`. | 
|  | 32 | +   * @return            An instance of the functional interface | 
|  | 33 | +   */ | 
|  | 34 | +  def deserializeLambda(lookup: MethodHandles.Lookup, cache: java.util.Map[String, MethodHandle], serialized: SerializedLambda): AnyRef = { | 
|  | 35 | +    def slashDot(name: String) = name.replaceAll("/", ".") | 
|  | 36 | +    val loader = lookup.lookupClass().getClassLoader | 
|  | 37 | +    val implClass = loader.loadClass(slashDot(serialized.getImplClass)) | 
|  | 38 | + | 
|  | 39 | +    def makeCallSite: CallSite = { | 
|  | 40 | +      import serialized._ | 
|  | 41 | +      def parseDescriptor(s: String) = | 
|  | 42 | +        MethodType.fromMethodDescriptorString(s, loader) | 
|  | 43 | + | 
|  | 44 | +      val funcInterfaceSignature = parseDescriptor(getFunctionalInterfaceMethodSignature) | 
|  | 45 | +      val instantiated = parseDescriptor(getInstantiatedMethodType) | 
|  | 46 | +      val functionalInterfaceClass = loader.loadClass(slashDot(getFunctionalInterfaceClass)) | 
|  | 47 | + | 
|  | 48 | +      val implMethodSig = parseDescriptor(getImplMethodSignature) | 
|  | 49 | +      // Construct the invoked type from the impl method type. This is the type of a factory | 
|  | 50 | +      // that will be generated by the meta-factory. It is a method type, with param types | 
|  | 51 | +      // coming form the types of the captures, and return type being the functional interface. | 
|  | 52 | +      val invokedType: MethodType = { | 
|  | 53 | +        // 1. Add receiver for non-static impl methods | 
|  | 54 | +        val withReceiver = getImplMethodKind match { | 
|  | 55 | +          case MethodHandleInfo.REF_invokeStatic | MethodHandleInfo.REF_newInvokeSpecial => | 
|  | 56 | +            implMethodSig | 
|  | 57 | +          case _ => | 
|  | 58 | +            implMethodSig.insertParameterTypes(0, implClass) | 
|  | 59 | +        } | 
|  | 60 | +        // 2. Remove lambda parameters, leaving only captures. Note: the receiver may be a lambda parameter, | 
|  | 61 | +        //    such as in `Function<Object, String> s = Object::toString` | 
|  | 62 | +        val lambdaArity = funcInterfaceSignature.parameterCount() | 
|  | 63 | +        val from = withReceiver.parameterCount() - lambdaArity | 
|  | 64 | +        val to = withReceiver.parameterCount() | 
|  | 65 | + | 
|  | 66 | +        // 3. Drop the lambda return type and replace with the functional interface. | 
|  | 67 | +        withReceiver.dropParameterTypes(from, to).changeReturnType(functionalInterfaceClass) | 
|  | 68 | +      } | 
|  | 69 | + | 
|  | 70 | +      // Lookup the implementation method | 
|  | 71 | +      val implMethod: MethodHandle = try { | 
|  | 72 | +        findMember(lookup, getImplMethodKind, implClass, getImplMethodName, implMethodSig) | 
|  | 73 | +      } catch { | 
|  | 74 | +        case e: ReflectiveOperationException => throw new IllegalArgumentException("Illegal lambda deserialization", e) | 
|  | 75 | +      } | 
|  | 76 | + | 
|  | 77 | +      val flags: Int = LambdaMetafactory.FLAG_SERIALIZABLE | LambdaMetafactory.FLAG_MARKERS | 
|  | 78 | +      val isScalaFunction = functionalInterfaceClass.getName.startsWith("scala.Function") | 
|  | 79 | +      val markerInterface: Class[_] = loader.loadClass(if (isScalaFunction) ScalaSerializable else JavaIOSerializable) | 
|  | 80 | + | 
|  | 81 | +      LambdaMetafactory.altMetafactory( | 
|  | 82 | +        lookup, getFunctionalInterfaceMethodName, invokedType, | 
|  | 83 | + | 
|  | 84 | +        /* samMethodType          = */ funcInterfaceSignature, | 
|  | 85 | +        /* implMethod             = */ implMethod, | 
|  | 86 | +        /* instantiatedMethodType = */ instantiated, | 
|  | 87 | +        /* flags                  = */ flags.asInstanceOf[AnyRef], | 
|  | 88 | +        /* markerInterfaceCount   = */ 1.asInstanceOf[AnyRef], | 
|  | 89 | +        /* markerInterfaces[0]    = */ markerInterface, | 
|  | 90 | +        /* bridgeCount            = */ 0.asInstanceOf[AnyRef] | 
|  | 91 | +      ) | 
|  | 92 | +    } | 
|  | 93 | + | 
|  | 94 | +    val key = serialized.getImplMethodName + " : " + serialized.getImplMethodSignature | 
|  | 95 | +    val factory: MethodHandle = if (cache == null) { | 
|  | 96 | +      makeCallSite.getTarget | 
|  | 97 | +    } else cache.get(key) match { | 
|  | 98 | +      case null => | 
|  | 99 | +        val callSite = makeCallSite | 
|  | 100 | +        val temp = callSite.getTarget | 
|  | 101 | +        cache.put(key, temp) | 
|  | 102 | +        temp | 
|  | 103 | +      case target => target | 
|  | 104 | +    } | 
|  | 105 | + | 
|  | 106 | +    val captures = Array.tabulate(serialized.getCapturedArgCount)(n => serialized.getCapturedArg(n)) | 
|  | 107 | +    factory.invokeWithArguments(captures: _*) | 
|  | 108 | +  } | 
|  | 109 | + | 
|  | 110 | +  private val ScalaSerializable = "scala.Serializable" | 
|  | 111 | + | 
|  | 112 | +  private val JavaIOSerializable = { | 
|  | 113 | +    // We could actually omit this marker interface as LambdaMetaFactory will add it if | 
|  | 114 | +    // the FLAG_SERIALIZABLE is set and of the provided markers extend it. But the code | 
|  | 115 | +    // is cleaner if we uniformly add a single marker, so I'm leaving it in place. | 
|  | 116 | +    "java.io.Serializable" | 
|  | 117 | +  } | 
|  | 118 | + | 
|  | 119 | +  private def findMember(lookup: MethodHandles.Lookup, kind: Int, owner: Class[_], | 
|  | 120 | +                         name: String, signature: MethodType): MethodHandle = { | 
|  | 121 | +    kind match { | 
|  | 122 | +      case MethodHandleInfo.REF_invokeStatic => | 
|  | 123 | +        lookup.findStatic(owner, name, signature) | 
|  | 124 | +      case MethodHandleInfo.REF_newInvokeSpecial => | 
|  | 125 | +        lookup.findConstructor(owner, signature) | 
|  | 126 | +      case MethodHandleInfo.REF_invokeVirtual | MethodHandleInfo.REF_invokeInterface => | 
|  | 127 | +        lookup.findVirtual(owner, name, signature) | 
|  | 128 | +      case MethodHandleInfo.REF_invokeSpecial => | 
|  | 129 | +        lookup.findSpecial(owner, name, signature, owner) | 
|  | 130 | +    } | 
|  | 131 | +  } | 
|  | 132 | +} | 
0 commit comments