Skip to content

Commit 721ced2

Browse files
cloud-fandavies
authored andcommitted
[SPARK-13067] [SQL] workaround for a weird scala reflection problem
A simple workaround to avoid getting parameter types when convert a logical plan to json. Author: Wenchen Fan <[email protected]> Closes #10970 from cloud-fan/reflection.
1 parent 66449b8 commit 721ced2

File tree

2 files changed

+23
-6
lines changed

2 files changed

+23
-6
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,20 @@ object ScalaReflection extends ScalaReflection {
601601
getConstructorParameters(t)
602602
}
603603

604+
/**
605+
* Returns the parameter names for the primary constructor of this class.
606+
*
607+
* Logically we should call `getConstructorParameters` and throw away the parameter types to get
608+
* parameter names, however there are some weird scala reflection problems and this method is a
609+
* workaround to avoid getting parameter types.
610+
*/
611+
def getConstructorParameterNames(cls: Class[_]): Seq[String] = {
612+
val m = runtimeMirror(cls.getClassLoader)
613+
val classSymbol = m.staticClass(cls.getName)
614+
val t = classSymbol.selfType
615+
constructParams(t).map(_.name.toString)
616+
}
617+
604618
def getClassFromType(tpe: Type): Class[_] = mirror.runtimeClass(tpe.erasure.typeSymbol.asClass)
605619
}
606620

@@ -745,6 +759,12 @@ trait ScalaReflection {
745759
def getConstructorParameters(tpe: Type): Seq[(String, Type)] = {
746760
val formalTypeArgs = tpe.typeSymbol.asClass.typeParams
747761
val TypeRef(_, _, actualTypeArgs) = tpe
762+
constructParams(tpe).map { p =>
763+
p.name.toString -> p.typeSignature.substituteTypes(formalTypeArgs, actualTypeArgs)
764+
}
765+
}
766+
767+
protected def constructParams(tpe: Type): Seq[Symbol] = {
748768
val constructorSymbol = tpe.member(nme.CONSTRUCTOR)
749769
val params = if (constructorSymbol.isMethod) {
750770
constructorSymbol.asMethod.paramss
@@ -758,9 +778,6 @@ trait ScalaReflection {
758778
primaryConstructorSymbol.get.asMethod.paramss
759779
}
760780
}
761-
762-
params.flatten.map { p =>
763-
p.name.toString -> p.typeSignature.substituteTypes(formalTypeArgs, actualTypeArgs)
764-
}
781+
params.flatten
765782
}
766783
}

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/trees/TreeNode.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ abstract class TreeNode[BaseType <: TreeNode[BaseType]] extends Product {
512512
}
513513

514514
protected def jsonFields: List[JField] = {
515-
val fieldNames = getConstructorParameters(getClass).map(_._1)
515+
val fieldNames = getConstructorParameterNames(getClass)
516516
val fieldValues = productIterator.toSeq ++ otherCopyArgs
517517
assert(fieldNames.length == fieldValues.length, s"${getClass.getSimpleName} fields: " +
518518
fieldNames.mkString(", ") + s", values: " + fieldValues.map(_.toString).mkString(", "))
@@ -560,7 +560,7 @@ abstract class TreeNode[BaseType <: TreeNode[BaseType]] extends Product {
560560
case obj if obj.getClass.getName.endsWith("$") => "object" -> obj.getClass.getName
561561
// returns null if the product type doesn't have a primary constructor, e.g. HiveFunctionWrapper
562562
case p: Product => try {
563-
val fieldNames = getConstructorParameters(p.getClass).map(_._1)
563+
val fieldNames = getConstructorParameterNames(p.getClass)
564564
val fieldValues = p.productIterator.toSeq
565565
assert(fieldNames.length == fieldValues.length)
566566
("product-class" -> JString(p.getClass.getName)) :: fieldNames.zip(fieldValues).map {

0 commit comments

Comments
 (0)