aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Schuiki2022-03-25 19:53:43 +0100
committerGitHub2022-03-25 18:53:43 +0000
commita2d48a5896335a567ddb21ed87fe38cd65d4764d (patch)
tree28c5fc6dd2795200fbb1d8bed5613d990d76ee93
parent8d2262c0566c5cc7fc66dec7d3e6d1f8102879b5 (diff)
Fix anno deserialization when class field is not first (#2501)
Update `findTypeHints` to allow for the "class" field in JSON objects to appear anywhere in the object. This used to rely on the field being the very first in the object, which is easily violated when reading JSON data generated externally, since an object's order of fields is unspecified and can be arbitrarily scrambled. Fixes #2497.
-rw-r--r--src/main/scala/firrtl/annotations/JsonProtocol.scala13
-rw-r--r--src/test/scala/firrtl/JsonProtocolSpec.scala12
2 files changed, 19 insertions, 6 deletions
diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala
index 6908a3a1..24ebc781 100644
--- a/src/main/scala/firrtl/annotations/JsonProtocol.scala
+++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala
@@ -315,12 +315,13 @@ object JsonProtocol extends LazyLogging {
// this used on the first invocation to check all annotations do so
def findTypeHints(classInst: Seq[JValue], requireClassField: Boolean = false): Seq[String] = classInst
.flatMap({
- case JObject(("class", JString(name)) :: fields) => name +: findTypeHints(fields.map(_._2))
- case obj: JObject if requireClassField =>
- throw new InvalidAnnotationJSONException(s"Expected field 'class' not found! $obj")
- case JObject(fields) => findTypeHints(fields.map(_._2))
- case JArray(arr) => findTypeHints(arr)
- case _ => Seq()
+ case JObject(fields) =>
+ val hint = fields.collectFirst { case ("class", JString(name)) => name }
+ if (requireClassField && hint.isEmpty)
+ throw new InvalidAnnotationJSONException(s"Expected field 'class' not found! $fields")
+ hint ++: findTypeHints(fields.map(_._2))
+ case JArray(arr) => findTypeHints(arr)
+ case _ => Seq()
})
.distinct
diff --git a/src/test/scala/firrtl/JsonProtocolSpec.scala b/src/test/scala/firrtl/JsonProtocolSpec.scala
index 0da43204..3e07542b 100644
--- a/src/test/scala/firrtl/JsonProtocolSpec.scala
+++ b/src/test/scala/firrtl/JsonProtocolSpec.scala
@@ -31,6 +31,8 @@ object JsonProtocolTestClasses {
with HasSerializationHints {
def typeHints = Seq(param.getClass)
}
+
+ case class SimpleAnnotation(alpha: String) extends NoTargetAnnotation
}
import JsonProtocolTestClasses._
@@ -68,4 +70,14 @@ class JsonProtocolSpec extends AnyFlatSpec {
val deserAnno = serializeAndDeserialize(anno)
assert(anno == deserAnno)
}
+
+ "JSON object order" should "not affect deserialization" in {
+ val anno = SimpleAnnotation("hello")
+ val serializedAnno = """[{
+ "alpha": "hello",
+ "class": "firrtlTests.JsonProtocolTestClasses$SimpleAnnotation"
+ }]"""
+ val deserAnno = JsonProtocol.deserialize(serializedAnno).head
+ assert(anno == deserAnno)
+ }
}