aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/scala/firrtl/Compiler.scala12
-rw-r--r--src/main/scala/firrtl/annotations/JsonProtocol.scala29
-rw-r--r--src/test/scala/firrtlTests/annotationTests/JsonProtocolSpec.scala12
3 files changed, 38 insertions, 15 deletions
diff --git a/src/main/scala/firrtl/Compiler.scala b/src/main/scala/firrtl/Compiler.scala
index 2998af3c..7354b8ee 100644
--- a/src/main/scala/firrtl/Compiler.scala
+++ b/src/main/scala/firrtl/Compiler.scala
@@ -214,17 +214,7 @@ private[firrtl] object Transform {
val remappedAnnotations = propagateAnnotations(name, logger, before.annotations, after.annotations, after.renames)
logger.trace(s"Annotations:")
- logger.trace {
- JsonProtocol
- .serializeTry(remappedAnnotations)
- .recoverWith {
- case NonFatal(e) =>
- val msg = s"Exception thrown during Annotation serialization:\n " +
- e.toString.replaceAll("\n", "\n ")
- Try(msg)
- }
- .get
- }
+ logger.trace(JsonProtocol.serializeRecover(remappedAnnotations))
logger.trace(s"Circuit:\n${after.circuit.serialize}")
diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala
index c284c788..3a25998d 100644
--- a/src/main/scala/firrtl/annotations/JsonProtocol.scala
+++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala
@@ -5,7 +5,7 @@ package annotations
import firrtl.ir._
-import scala.util.{Failure, Try}
+import scala.util.{Failure, Success, Try}
import org.json4s._
import org.json4s.native.JsonMethods._
@@ -19,6 +19,9 @@ trait HasSerializationHints {
def typeHints: Seq[Class[_]]
}
+/** Wrapper [[Annotation]] for Annotations that cannot be serialized */
+case class UnserializeableAnnotation(error: String, content: String) extends NoTargetAnnotation
+
object JsonProtocol {
class TransformClassSerializer
extends CustomSerializer[Class[_ <: Transform]](format =>
@@ -227,14 +230,17 @@ object JsonProtocol {
): Seq[(Annotation, Throwable)] =
annos.map(a => a -> Try(write(a))).collect { case (a, Failure(e)) => (a, e) }
- def serializeTry(annos: Seq[Annotation]): Try[String] = {
- val tags = annos
+ private def getTags(annos: Seq[Annotation]): Seq[Class[_]] =
+ annos
.flatMap({
case anno: HasSerializationHints => anno.getClass +: anno.typeHints
case anno => Seq(anno.getClass)
})
.distinct
+ def serializeTry(annos: Seq[Annotation]): Try[String] = {
+ val tags = getTags(annos)
+
implicit val formats = jsonFormat(tags)
Try(writePretty(annos)).recoverWith {
case e: org.json4s.MappingException =>
@@ -243,6 +249,23 @@ object JsonProtocol {
}
}
+ /** Serialize annotations to JSON while wrapping unserializeable ones with [[UnserializeableAnnotation]]
+ *
+ * @note this is slower than standard serialization
+ */
+ def serializeRecover(annos: Seq[Annotation]): String = {
+ val tags = classOf[UnserializeableAnnotation] +: getTags(annos)
+ implicit val formats = jsonFormat(tags)
+
+ val safeAnnos = annos.map { anno =>
+ Try(write(anno)) match {
+ case Success(_) => anno
+ case Failure(e) => UnserializeableAnnotation(e.getMessage, anno.toString)
+ }
+ }
+ writePretty(safeAnnos)
+ }
+
def deserialize(in: JsonInput): Seq[Annotation] = deserializeTry(in).get
def deserializeTry(in: JsonInput): Try[Seq[Annotation]] = Try({
diff --git a/src/test/scala/firrtlTests/annotationTests/JsonProtocolSpec.scala b/src/test/scala/firrtlTests/annotationTests/JsonProtocolSpec.scala
index 6dd018eb..b2d029f5 100644
--- a/src/test/scala/firrtlTests/annotationTests/JsonProtocolSpec.scala
+++ b/src/test/scala/firrtlTests/annotationTests/JsonProtocolSpec.scala
@@ -3,9 +3,10 @@
package firrtlTests.annotationTests
import firrtl._
-import firrtl.annotations.{JsonProtocol, NoTargetAnnotation, UnserializableAnnotationException}
+import firrtl.annotations._
import firrtl.ir._
import firrtl.options.Dependency
+import firrtl.transforms.DontTouchAnnotation
import scala.util.Failure
import _root_.logger.{LogLevel, LogLevelAnnotation, Logger}
import org.scalatest.flatspec.AnyFlatSpec
@@ -77,4 +78,13 @@ class JsonProtocolSpec extends AnyFlatSpec with Matchers {
e.getMessage should include("Classes defined in method bodies are not supported")
}
}
+ "JsonProtocol.serializeRecover" should "emit even annotations that cannot be serialized" in {
+ case class MyAnno(x: Int) extends NoTargetAnnotation
+ val target = CircuitTarget("Top").module("Foo").ref("x")
+ val annos = MyAnno(3) :: DontTouchAnnotation(target) :: Nil
+ val res = JsonProtocol.serializeRecover(annos)
+ res should include(""""class":"firrtl.annotations.UnserializeableAnnotation",""")
+ res should include(""""error":"Classes defined in method bodies are not supported.",""")
+ res should include(""""content":"MyAnno(3)"""")
+ }
}