diff options
| author | Jack Koenig | 2018-02-27 18:07:11 -0800 |
|---|---|---|
| committer | GitHub | 2018-02-27 18:07:11 -0800 |
| commit | c7eb1570dfb1c7701ea32d1209982a053f3cec1d (patch) | |
| tree | 3f509b202d82841c5dad5588d1f953a25d389b44 /src/main/scala/firrtl/annotations/JsonProtocol.scala | |
| parent | b90fc784a1819c1d7905910130a7da022214bc22 (diff) | |
Refactor Annotations (#721)
- Old Annotation renamed to deprecated LegacyAnnotation
- Annotation is now a trait that can be extended
- New JsonProtocol for Annotation [de]serialization
- Replace AnnotationMap with AnnotationSeq
- Deprecate Transform.getMyAnnotations
- Update Transforms
- Turn on deprecation warnings
- Remove deprecated Driver.compile
- Make AnnotationTests abstract with Legacy and Json subclasses
- Add functionality to convert LegacyAnnotations of built-in annos
This will give a noisy warning and is more of a best effort than a
robust solution.
Fixes #475 Closes #609
Diffstat (limited to 'src/main/scala/firrtl/annotations/JsonProtocol.scala')
| -rw-r--r-- | src/main/scala/firrtl/annotations/JsonProtocol.scala | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala new file mode 100644 index 00000000..5005ccb0 --- /dev/null +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -0,0 +1,84 @@ + +package firrtl +package annotations + +import scala.util.Try + +import org.json4s._ +import org.json4s.native.JsonMethods._ +import org.json4s.native.Serialization +import org.json4s.native.Serialization.{read, write, writePretty} + +import firrtl.ir._ +import firrtl.Utils.error + +object JsonProtocol { + + // Helper for error messages + private def prettifyJsonInput(in: JsonInput): String = { + def defaultToString(base: String, obj: Any): String = s"$base@${obj.hashCode.toHexString}" + in match { + case FileInput(file) => file.toString + case StringInput(o) => defaultToString("String", o) + case ReaderInput(o) => defaultToString("Reader", o) + case StreamInput(o) => defaultToString("Stream", o) + } + } + + class TransformClassSerializer extends CustomSerializer[Class[_ <: Transform]](format => ( + { case JString(s) => Class.forName(s).asInstanceOf[Class[_ <: Transform]] }, + { case x: Class[_] => JString(x.getName) } + )) + // TODO Reduce boilerplate? + class NamedSerializer extends CustomSerializer[Named](format => ( + { case JString(s) => AnnotationUtils.toNamed(s) }, + { case named: Named => JString(named.serialize) } + )) + class CircuitNameSerializer extends CustomSerializer[CircuitName](format => ( + { case JString(s) => AnnotationUtils.toNamed(s).asInstanceOf[CircuitName] }, + { case named: CircuitName => JString(named.serialize) } + )) + class ModuleNameSerializer extends CustomSerializer[ModuleName](format => ( + { case JString(s) => AnnotationUtils.toNamed(s).asInstanceOf[ModuleName] }, + { case named: ModuleName => JString(named.serialize) } + )) + class ComponentNameSerializer extends CustomSerializer[ComponentName](format => ( + { case JString(s) => AnnotationUtils.toNamed(s).asInstanceOf[ComponentName] }, + { case named: ComponentName => JString(named.serialize) } + )) + + /** Construct Json formatter for annotations */ + def jsonFormat(tags: Seq[Class[_ <: Annotation]]) = { + Serialization.formats(FullTypeHints(tags.toList)).withTypeHintFieldName("class") + + new TransformClassSerializer + new NamedSerializer + new CircuitNameSerializer + + new ModuleNameSerializer + new ComponentNameSerializer + } + + /** Serialize annotations to a String for emission */ + def serialize(annos: Seq[Annotation]): String = serializeTry(annos).get + + def serializeTry(annos: Seq[Annotation]): Try[String] = { + val tags = annos.map(_.getClass).distinct + implicit val formats = jsonFormat(tags) + Try(writePretty(annos)) + } + + def deserialize(in: JsonInput): Seq[Annotation] = deserializeTry(in).get + + def deserializeTry(in: JsonInput): Try[Seq[Annotation]] = Try({ + def throwError() = throw new InvalidAnnotationFileException(prettifyJsonInput(in)) + val parsed = parse(in) + val annos = parsed match { + case JArray(objs) => objs + case _ => throwError() + } + // Gather classes so we can deserialize arbitrary Annotations + val classes = annos.map({ + case JObject(("class", JString(c)) :: tail) => c + case _ => throwError() + }).distinct + val loaded = classes.map(Class.forName(_).asInstanceOf[Class[_ <: Annotation]]) + implicit val formats = jsonFormat(loaded) + read[List[Annotation]](in) + }) +} |
