From 3cd42881ee2a96a2e7d2fe3758e1b936d204146a Mon Sep 17 00:00:00 2001 From: chick Date: Wed, 13 Oct 2021 13:06:44 -0700 Subject: [PATCH 01/10] Let firrtl based applications run despite loading unknown annotations An application like barstools may contain a main that loads an annotations file containing annotation classes that are not on it's classpath. This change allows unknown annotations to be preserved by wrapping them in a UnrecognizedAnnotation. If annotations are then output to a file, they will be unwrapped during serialization This feature can be enabled via an AllowUnrecognizedAnnotations annotation --- .../scala/firrtl/annotations/Annotation.scala | 3 + .../firrtl/annotations/JsonProtocol.scala | 84 +++++- .../firrtl/stage/FirrtlAnnotations.scala | 10 + src/main/scala/firrtl/stage/FirrtlCli.scala | 3 +- .../UnrecognizedAnnotationSpec.scala | 243 ++++++++++++++++++ 5 files changed, 333 insertions(+), 10 deletions(-) create mode 100644 src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala diff --git a/src/main/scala/firrtl/annotations/Annotation.scala b/src/main/scala/firrtl/annotations/Annotation.scala index f4d6ee5599..c6145c8652 100644 --- a/src/main/scala/firrtl/annotations/Annotation.scala +++ b/src/main/scala/firrtl/annotations/Annotation.scala @@ -4,6 +4,7 @@ package firrtl package annotations import firrtl.options.StageUtils +import org.json4s.JValue import scala.collection.Traversable @@ -165,3 +166,5 @@ object Annotation case class DeletedAnnotation(xFormName: String, anno: Annotation) extends NoTargetAnnotation { override def serialize: String = s"""DELETED by $xFormName\n${anno.serialize}""" } + +case class UnrecognizedAnnotation(underlying: JValue) extends NoTargetAnnotation diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala index 3a25998dd4..e9b72442c4 100644 --- a/src/main/scala/firrtl/annotations/JsonProtocol.scala +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -4,14 +4,18 @@ package firrtl package annotations import firrtl.ir._ +import firrtl.stage.AllowUnrecognizedAnnotations +import logger.LazyLogging import scala.util.{Failure, Success, Try} - import org.json4s._ import org.json4s.native.JsonMethods._ import org.json4s.native.Serialization import org.json4s.native.Serialization.{read, write, writePretty} +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer + trait HasSerializationHints { // For serialization of complicated constructor arguments, let the annotation // writer specify additional type hints for relevant classes that might be @@ -22,7 +26,7 @@ trait HasSerializationHints { /** Wrapper [[Annotation]] for Annotations that cannot be serialized */ case class UnserializeableAnnotation(error: String, content: String) extends NoTargetAnnotation -object JsonProtocol { +object JsonProtocol extends LazyLogging { class TransformClassSerializer extends CustomSerializer[Class[_ <: Transform]](format => ( @@ -206,6 +210,13 @@ object JsonProtocol { { case tpe: GroundType => JString(tpe.serialize) } ) ) + class UnrecognizedAnnotationSerializerSerializer + extends CustomSerializer[JObject](format => + ( + { case JObject(s) => JObject(s) }, + { case UnrecognizedAnnotation(underlying) => underlying } + ) + ) /** Construct Json formatter for annotations */ def jsonFormat(tags: Seq[Class[_]]) = { @@ -217,7 +228,8 @@ object JsonProtocol { new LoadMemoryFileTypeSerializer + new IsModuleSerializer + new IsMemberSerializer + new CompleteTargetSerializer + new TypeSerializer + new ExpressionSerializer + new StatementSerializer + new PortSerializer + new DefModuleSerializer + - new CircuitSerializer + new InfoSerializer + new GroundTypeSerializer + new CircuitSerializer + new InfoSerializer + new GroundTypeSerializer + + new UnrecognizedAnnotationSerializerSerializer } /** Serialize annotations to a String for emission */ @@ -266,9 +278,14 @@ object JsonProtocol { writePretty(safeAnnos) } + /** Deserialize JSON input into a Seq[Annotation] + * + * @param in JsonInput, can be file or string + * @return + */ def deserialize(in: JsonInput): Seq[Annotation] = deserializeTry(in).get - def deserializeTry(in: JsonInput): Try[Seq[Annotation]] = Try({ + def deserializeTry(in: JsonInput): Try[Seq[Annotation]] = Try { val parsed = parse(in) val annos = parsed match { case JArray(objs) => objs @@ -288,15 +305,63 @@ object JsonProtocol { throw new InvalidAnnotationJSONException(s"Expected field 'class' not found! $obj") case JObject(fields) => findTypeHints(fields.map(_._2)) case JArray(arr) => findTypeHints(arr) - case oJValue => Seq() + case _ => Seq() }) .distinct + // I don't much like this var here, but it has made it much simpler + // to maintain backward compatibility with the exception test structure + var classNotFoundBuildingLoaded = false val classes = findTypeHints(annos, true) - val loaded = classes.map(Class.forName(_)) + val loaded = classes.map { x => + val result = + try { + val result = Class.forName(x) + result + } catch { + case _: java.lang.ClassNotFoundException => + classNotFoundBuildingLoaded = true // tells us which Exception to throw in recovery + // Found an annotation we don't recognize, So add UnrecognizedAnnotation to `loaded` + Class.forName("firrtl.annotations.UnrecognizedAnnotation") + } + result + } implicit val formats = jsonFormat(loaded) - read[List[Annotation]](in) - }).recoverWith { + try { + read[List[Annotation]](in) + } catch { + case e: org.json4s.MappingException => + // If we get here, the build `read` failed to process an annotation + // So we will map the annos one a time, wrapping the JSON of the unrecognized annotations + var exceptionList = new ArrayBuffer[Exception]() + val firrtlAnnos = annos.map { + case jsonAnno => + try { + jsonAnno.extract[Annotation] + } catch { + case mappingException: org.json4s.MappingException => + exceptionList += mappingException + UnrecognizedAnnotation(jsonAnno) + } + } + + if (firrtlAnnos.contains(AllowUnrecognizedAnnotations)) { + firrtlAnnos + } else { + logger.error( + "Annotation parsing found unrecognized annotations\n" + + "This error can be ignored with an AllowUnrecognizedAnnotationsAnnotation" + + " or command line flag --allow-unrecognized-annotations\n" + + exceptionList.mkString("\n") + ) + if (classNotFoundBuildingLoaded) { + throw new AnnotationClassNotFoundException(e.getMessage) + } else { + throw e + } // throw the mapping exception + } + } + }.recoverWith { // Translate some generic errors to specific ones case e: java.lang.ClassNotFoundException => Failure(new AnnotationClassNotFoundException(e.getMessage)) @@ -308,7 +373,8 @@ object JsonProtocol { in match { case FileInput(file) => Failure(new InvalidAnnotationFileException(file, e)) - case _ => Failure(e) + case _ => + Failure(e) } } } diff --git a/src/main/scala/firrtl/stage/FirrtlAnnotations.scala b/src/main/scala/firrtl/stage/FirrtlAnnotations.scala index 8e1e200101..5bc5a18cdd 100644 --- a/src/main/scala/firrtl/stage/FirrtlAnnotations.scala +++ b/src/main/scala/firrtl/stage/FirrtlAnnotations.scala @@ -324,6 +324,16 @@ case object PrettyNoExprInlining extends NoTargetAnnotation with FirrtlOption wi ) } +case object AllowUnrecognizedAnnotations extends NoTargetAnnotation with FirrtlOption with HasShellOptions { + val options = Seq( + new ShellOption[String]( + longOption = "allow-unrecognized-annotations", + toAnnotationSeq = _ => Seq(this), + helpText = "Allow annotation files to contain unrecognized annotations" + ) + ) +} + /** Turn off folding a specific primitive operand * @param op the op that should never be folded */ diff --git a/src/main/scala/firrtl/stage/FirrtlCli.scala b/src/main/scala/firrtl/stage/FirrtlCli.scala index 2ad7557448..a9c0862715 100644 --- a/src/main/scala/firrtl/stage/FirrtlCli.scala +++ b/src/main/scala/firrtl/stage/FirrtlCli.scala @@ -27,7 +27,8 @@ trait FirrtlCli { this: Shell => DisableFold, OptimizeForFPGA, CurrentFirrtlStateAnnotation, - CommonSubexpressionElimination + CommonSubexpressionElimination, + AllowUnrecognizedAnnotations ) .map(_.addOptions(parser)) diff --git a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala new file mode 100644 index 0000000000..40e964bc07 --- /dev/null +++ b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: Apache-2.0 + +package firrtlTests.annotationTests + +import firrtl.annotations._ +import firrtl.passes.memlib.ReplSeqMemAnnotation +import firrtl.testutils.FirrtlFlatSpec +import firrtl.transforms.BlackBoxInlineAnno +import logger.Logger +import logger.Logger.OutputCaptor + +class UnrecognizedAnnotationSpec extends FirrtlFlatSpec { + behavior.of("unrecognized annotations can be carried through serialization and deserialization") + + it should "preserve unknown annotations when allowed" in { + val annotations = JsonProtocol.deserialize(UnrecognizedAnnotationTextGenerator.jsonText(includeWithOutAllow = true)) + + annotations.exists(_.isInstanceOf[BlackBoxInlineAnno]) should be(true) + annotations.count(_.isInstanceOf[UnrecognizedAnnotation]) should be(2) + annotations.exists(_.isInstanceOf[ReplSeqMemAnnotation]) should be(false) + + val jsonOutputText = JsonProtocol.serialize(annotations) + + jsonOutputText should include(""""class":"firrtl.transforms.BlackBoxInlineAnno"""") + jsonOutputText should include(""""class":"freechips.rocketchip.util.RegFieldDescMappingAnnotation"""") + jsonOutputText should include(""""class":"freechips.rocketchip.util.SRAMAnnotation"""") + } + + it should "throw an error when unknown annotations are present but AllowUnrecognizedAnnotation is not" in { + + // Default log level is error, which the JSON parsing uses here + Logger.makeScope(Seq()) { + val captor = new OutputCaptor + Logger.setOutput(captor.printStream) + + val parsingError = intercept[AnnotationClassNotFoundException] { + JsonProtocol.deserialize(UnrecognizedAnnotationTextGenerator.jsonText(includeWithOutAllow = false)) + } + parsingError.getMessage should include("RegFieldDescMappingAnnotation") + + val output = captor.getOutputAsString + output should include("Annotation parsing found unrecognized annotations") + output should include( + "This error can be ignored with an AllowUnrecognizedAnnotationsAnnotation or command line flag --allow-unrecognized-annotations" + ) + output should include( + "org.json4s.package$MappingException: Do not know how to deserialize 'freechips.rocketchip.util.RegFieldDescMappingAnnotation'" + ) + output should include( + "org.json4s.package$MappingException: Do not know how to deserialize 'freechips.rocketchip.util.SRAMAnnotation'" + ) + } + } +} + +object UnrecognizedAnnotationTextGenerator { + + def jsonText(includeWithOutAllow: Boolean): String = { + val s = new StringBuilder() + + s ++= """[""" + + if (includeWithOutAllow) { + s ++= """ {""" + s ++= """ "class":"firrtl.stage.AllowUnrecognizedAnnotations$"""" + s ++= """ },""" + } + s ++= """ {""" + s ++= """ "class":"firrtl.transforms.BlackBoxInlineAnno",""" + s ++= """ "target":"TestHarness.plusarg_reader_27",""" + s ++= """ "name":"plusarg_reader.v",""" + s ++= """ "text":"// See LICENSE.SiFive for license details.\n\n//VCS coverage exclude_file\n\n// No default parameter values are intended, nor does IEEE 1800-2012 require them (clause A.2.4 param_assignment),\n// but Incisive demands them. These default values should never be used.\nmodule plusarg_reader #(\n parameter FORMAT=\"borked=%d\",\n parameter WIDTH=1,\n parameter [WIDTH-1:0] DEFAULT=0\n) (\n output [WIDTH-1:0] out\n);\n\n`ifdef SYNTHESIS\nassign out = DEFAULT;\n`else\nreg [WIDTH-1:0] myplus;\nassign out = myplus;\n\ninitial begin\n if (!$value$plusargs(FORMAT, myplus)) myplus = DEFAULT;\nend\n`endif\n\nendmodule\n"""" + s ++= """ },""" + s ++= """ {""" + s ++= """ "class":"freechips.rocketchip.util.RegFieldDescMappingAnnotation",""" + s ++= """ "target":"TestHarness.PeripheryBus",""" + s ++= """ "regMappingSer":{""" + s ++= """ "displayName":"PeripheryBus",""" + s ++= """ "deviceName":"PeripheryBus",""" + s ++= """ "baseAddress":16384,""" + s ++= """ "regFields":[""" + s ++= """ {""" + s ++= """ "byteOffset":"0x0",""" + s ++= """ "bitOffset":0,""" + s ++= """ "bitWidth":8,""" + s ++= """ "name":"unnamedRegField0_0",""" + s ++= """ "resetValue":0,""" + s ++= """ "accessType":"None",""" + s ++= """ "wrType":"None",""" + s ++= """ "rdAction":"None",""" + s ++= """ "desc":"None",""" + s ++= """ "group":"None",""" + s ++= """ "groupDesc":"None",""" + s ++= """ "volatile":false,""" + s ++= """ "hasReset":false,""" + s ++= """ "enumerations":{""" + s ++= """""" + s ++= """ }""" + s ++= """ },""" + s ++= """ {""" + s ++= """ "byteOffset":"0x0",""" + s ++= """ "bitOffset":8,""" + s ++= """ "bitWidth":8,""" + s ++= """ "name":"unnamedRegField0_8",""" + s ++= """ "resetValue":0,""" + s ++= """ "accessType":"None",""" + s ++= """ "wrType":"None",""" + s ++= """ "rdAction":"None",""" + s ++= """ "desc":"None",""" + s ++= """ "group":"None",""" + s ++= """ "groupDesc":"None",""" + s ++= """ "volatile":false,""" + s ++= """ "hasReset":false,""" + s ++= """ "enumerations":{""" + s ++= """""" + s ++= """ }""" + s ++= """ },""" + s ++= """ {""" + s ++= """ "byteOffset":"0x0",""" + s ++= """ "bitOffset":16,""" + s ++= """ "bitWidth":8,""" + s ++= """ "name":"unnamedRegField0_16",""" + s ++= """ "resetValue":0,""" + s ++= """ "accessType":"None",""" + s ++= """ "wrType":"None",""" + s ++= """ "rdAction":"None",""" + s ++= """ "desc":"None",""" + s ++= """ "group":"None",""" + s ++= """ "groupDesc":"None",""" + s ++= """ "volatile":false,""" + s ++= """ "hasReset":false,""" + s ++= """ "enumerations":{""" + s ++= """""" + s ++= """ }""" + s ++= """ },""" + s ++= """ {""" + s ++= """ "byteOffset":"0x0",""" + s ++= """ "bitOffset":24,""" + s ++= """ "bitWidth":8,""" + s ++= """ "name":"unnamedRegField0_24",""" + s ++= """ "resetValue":0,""" + s ++= """ "accessType":"None",""" + s ++= """ "wrType":"None",""" + s ++= """ "rdAction":"None",""" + s ++= """ "desc":"None",""" + s ++= """ "group":"None",""" + s ++= """ "groupDesc":"None",""" + s ++= """ "volatile":false,""" + s ++= """ "hasReset":false,""" + s ++= """ "enumerations":{""" + s ++= """""" + s ++= """ }""" + s ++= """ },""" + s ++= """ {""" + s ++= """ "byteOffset":"0x0",""" + s ++= """ "bitOffset":32,""" + s ++= """ "bitWidth":8,""" + s ++= """ "name":"unnamedRegField0_32",""" + s ++= """ "resetValue":0,""" + s ++= """ "accessType":"None",""" + s ++= """ "wrType":"None",""" + s ++= """ "rdAction":"None",""" + s ++= """ "desc":"None",""" + s ++= """ "group":"None",""" + s ++= """ "groupDesc":"None",""" + s ++= """ "volatile":false,""" + s ++= """ "hasReset":false,""" + s ++= """ "enumerations":{""" + s ++= """""" + s ++= """ }""" + s ++= """ },""" + s ++= """ {""" + s ++= """ "byteOffset":"0x0",""" + s ++= """ "bitOffset":40,""" + s ++= """ "bitWidth":8,""" + s ++= """ "name":"unnamedRegField0_40",""" + s ++= """ "resetValue":0,""" + s ++= """ "accessType":"None",""" + s ++= """ "wrType":"None",""" + s ++= """ "rdAction":"None",""" + s ++= """ "desc":"None",""" + s ++= """ "group":"None",""" + s ++= """ "groupDesc":"None",""" + s ++= """ "volatile":false,""" + s ++= """ "hasReset":false,""" + s ++= """ "enumerations":{""" + s ++= """""" + s ++= """ }""" + s ++= """ },""" + s ++= """ {""" + s ++= """ "byteOffset":"0x0",""" + s ++= """ "bitOffset":48,""" + s ++= """ "bitWidth":8,""" + s ++= """ "name":"unnamedRegField0_48",""" + s ++= """ "resetValue":0,""" + s ++= """ "accessType":"None",""" + s ++= """ "wrType":"None",""" + s ++= """ "rdAction":"None",""" + s ++= """ "desc":"None",""" + s ++= """ "group":"None",""" + s ++= """ "groupDesc":"None",""" + s ++= """ "volatile":false,""" + s ++= """ "hasReset":false,""" + s ++= """ "enumerations":{""" + s ++= """""" + s ++= """ }""" + s ++= """ },""" + s ++= """ {""" + s ++= """ "byteOffset":"0x0",""" + s ++= """ "bitOffset":56,""" + s ++= """ "bitWidth":8,""" + s ++= """ "name":"unnamedRegField0_56",""" + s ++= """ "resetValue":0,""" + s ++= """ "accessType":"None",""" + s ++= """ "wrType":"None",""" + s ++= """ "rdAction":"None",""" + s ++= """ "desc":"None",""" + s ++= """ "group":"None",""" + s ++= """ "groupDesc":"None",""" + s ++= """ "volatile":false,""" + s ++= """ "hasReset":false,""" + s ++= """ "enumerations":{""" + s ++= """""" + s ++= """ }""" + s ++= """ }""" + s ++= """ ]""" + s ++= """ }""" + s ++= """ },""" + s ++= """ {""" + s ++= """ "class":"freechips.rocketchip.util.SRAMAnnotation",""" + s ++= """ "target":"TestHarness.Directory.cc_dir",""" + s ++= """ "address_width":10,""" + s ++= """ "name":"cc_dir",""" + s ++= """ "data_width":136,""" + s ++= """ "depth":1024,""" + s ++= """ "description":"Directory RAM",""" + s ++= """ "write_mask_granularity":17""" + s ++= """ }""" + s ++= """]""" + + s.toString + } +} From f5aa1318d5d6530270ae7d6847b1d2e318acf03c Mon Sep 17 00:00:00 2001 From: chick Date: Mon, 1 Nov 2021 22:20:46 -0700 Subject: [PATCH 02/10] A number of fixes in response to PR comments - Added `UnrecogizedAnnotationsException` for internal communications of errors during annotation deserialization - Made tracking of Unrecognized annotation types more robust, by extracting class with regex - fixed AllowUnrecognizedAnnotations to stop it from eating following annotation - change to use mutable package inline with ArrayBuffer - removed intermediate result values used in debugging JsonProtocol - added handler in `stage.view` annotation handler - Added new tests to show behavior when run from command line --- .../firrtl/annotations/AnnotationUtils.scala | 1 + .../firrtl/annotations/JsonProtocol.scala | 293 +++++++++++++----- .../firrtl/stage/FirrtlAnnotations.scala | 2 +- src/main/scala/firrtl/stage/package.scala | 1 + .../UnrecognizedAnnotationSpec.scala | 100 +++++- 5 files changed, 316 insertions(+), 81 deletions(-) diff --git a/src/main/scala/firrtl/annotations/AnnotationUtils.scala b/src/main/scala/firrtl/annotations/AnnotationUtils.scala index 10894b2651..826bd542d7 100644 --- a/src/main/scala/firrtl/annotations/AnnotationUtils.scala +++ b/src/main/scala/firrtl/annotations/AnnotationUtils.scala @@ -9,6 +9,7 @@ import firrtl.ir._ case class InvalidAnnotationFileException(file: File, cause: FirrtlUserException = null) extends FirrtlUserException(s"$file", cause) +case class UnrecogizedAnnotationsException(msg: String) extends FirrtlUserException(s"Unrecognized annotations $msg") case class InvalidAnnotationJSONException(msg: String) extends FirrtlUserException(msg) case class AnnotationFileNotFoundException(file: File) extends FirrtlUserException( diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala index e9b72442c4..2b9499fc3b 100644 --- a/src/main/scala/firrtl/annotations/JsonProtocol.scala +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -14,7 +14,6 @@ import org.json4s.native.Serialization import org.json4s.native.Serialization.{read, write, writePretty} import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer trait HasSerializationHints { // For serialization of complicated constructor arguments, let the annotation @@ -30,39 +29,64 @@ object JsonProtocol extends LazyLogging { class TransformClassSerializer extends CustomSerializer[Class[_ <: Transform]](format => ( - { case JString(s) => Class.forName(s).asInstanceOf[Class[_ <: Transform]] }, - { case x: Class[_] => JString(x.getName) } + { + 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) } + { + 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) } + { + 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) } + { + 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) } + { + case JString(s) => AnnotationUtils.toNamed(s).asInstanceOf[ComponentName] + }, + { + case named: ComponentName => JString(named.serialize) + } ) ) + class TransformSerializer extends CustomSerializer[Transform](format => ( @@ -79,142 +103,238 @@ object JsonProtocol extends LazyLogging { case t: Throwable => throw t } }, - { case x: Transform => JString(x.getClass.getName) } + { + case x: Transform => JString(x.getClass.getName) + } ) ) + class LoadMemoryFileTypeSerializer extends CustomSerializer[MemoryLoadFileType](format => ( - { case JString(s) => MemoryLoadFileType.deserialize(s) }, - { case named: MemoryLoadFileType => JString(named.serialize) } + { + case JString(s) => MemoryLoadFileType.deserialize(s) + }, + { + case named: MemoryLoadFileType => JString(named.serialize) + } ) ) class TargetSerializer extends CustomSerializer[Target](format => ( - { case JString(s) => Target.deserialize(s) }, - { case named: Target => JString(named.serialize) } + { + case JString(s) => Target.deserialize(s) + }, + { + case named: Target => JString(named.serialize) + } ) ) + class GenericTargetSerializer extends CustomSerializer[GenericTarget](format => ( - { case JString(s) => Target.deserialize(s).asInstanceOf[GenericTarget] }, - { case named: GenericTarget => JString(named.serialize) } + { + case JString(s) => Target.deserialize(s).asInstanceOf[GenericTarget] + }, + { + case named: GenericTarget => JString(named.serialize) + } ) ) + class CircuitTargetSerializer extends CustomSerializer[CircuitTarget](format => ( - { case JString(s) => Target.deserialize(s).asInstanceOf[CircuitTarget] }, - { case named: CircuitTarget => JString(named.serialize) } + { + case JString(s) => Target.deserialize(s).asInstanceOf[CircuitTarget] + }, + { + case named: CircuitTarget => JString(named.serialize) + } ) ) + class ModuleTargetSerializer extends CustomSerializer[ModuleTarget](format => ( - { case JString(s) => Target.deserialize(s).asInstanceOf[ModuleTarget] }, - { case named: ModuleTarget => JString(named.serialize) } + { + case JString(s) => Target.deserialize(s).asInstanceOf[ModuleTarget] + }, + { + case named: ModuleTarget => JString(named.serialize) + } ) ) + class InstanceTargetSerializer extends CustomSerializer[InstanceTarget](format => ( - { case JString(s) => Target.deserialize(s).asInstanceOf[InstanceTarget] }, - { case named: InstanceTarget => JString(named.serialize) } + { + case JString(s) => Target.deserialize(s).asInstanceOf[InstanceTarget] + }, + { + case named: InstanceTarget => JString(named.serialize) + } ) ) + class ReferenceTargetSerializer extends CustomSerializer[ReferenceTarget](format => ( - { case JString(s) => Target.deserialize(s).asInstanceOf[ReferenceTarget] }, - { case named: ReferenceTarget => JString(named.serialize) } + { + case JString(s) => Target.deserialize(s).asInstanceOf[ReferenceTarget] + }, + { + case named: ReferenceTarget => JString(named.serialize) + } ) ) + class IsModuleSerializer extends CustomSerializer[IsModule](format => ( - { case JString(s) => Target.deserialize(s).asInstanceOf[IsModule] }, - { case named: IsModule => JString(named.serialize) } + { + case JString(s) => Target.deserialize(s).asInstanceOf[IsModule] + }, + { + case named: IsModule => JString(named.serialize) + } ) ) + class IsMemberSerializer extends CustomSerializer[IsMember](format => ( - { case JString(s) => Target.deserialize(s).asInstanceOf[IsMember] }, - { case named: IsMember => JString(named.serialize) } + { + case JString(s) => Target.deserialize(s).asInstanceOf[IsMember] + }, + { + case named: IsMember => JString(named.serialize) + } ) ) + class CompleteTargetSerializer extends CustomSerializer[CompleteTarget](format => ( - { case JString(s) => Target.deserialize(s).asInstanceOf[CompleteTarget] }, - { case named: CompleteTarget => JString(named.serialize) } + { + case JString(s) => Target.deserialize(s).asInstanceOf[CompleteTarget] + }, + { + case named: CompleteTarget => JString(named.serialize) + } ) ) + // FIRRTL Serializers class TypeSerializer extends CustomSerializer[Type](format => ( - { case JString(s) => Parser.parseType(s) }, - { case tpe: Type => JString(tpe.serialize) } + { + case JString(s) => Parser.parseType(s) + }, + { + case tpe: Type => JString(tpe.serialize) + } ) ) + class ExpressionSerializer extends CustomSerializer[Expression](format => ( - { case JString(s) => Parser.parseExpression(s) }, - { case expr: Expression => JString(expr.serialize) } + { + case JString(s) => Parser.parseExpression(s) + }, + { + case expr: Expression => JString(expr.serialize) + } ) ) + class StatementSerializer extends CustomSerializer[Statement](format => ( - { case JString(s) => Parser.parseStatement(s) }, - { case statement: Statement => JString(statement.serialize) } + { + case JString(s) => Parser.parseStatement(s) + }, + { + case statement: Statement => JString(statement.serialize) + } ) ) + class PortSerializer extends CustomSerializer[Port](format => ( - { case JString(s) => Parser.parsePort(s) }, - { case port: Port => JString(port.serialize) } + { + case JString(s) => Parser.parsePort(s) + }, + { + case port: Port => JString(port.serialize) + } ) ) + class DefModuleSerializer extends CustomSerializer[DefModule](format => ( - { case JString(s) => Parser.parseDefModule(s) }, - { case mod: DefModule => JString(mod.serialize) } + { + case JString(s) => Parser.parseDefModule(s) + }, + { + case mod: DefModule => JString(mod.serialize) + } ) ) + class CircuitSerializer extends CustomSerializer[Circuit](format => ( - { case JString(s) => Parser.parse(s) }, - { case cir: Circuit => JString(cir.serialize) } + { + case JString(s) => Parser.parse(s) + }, + { + case cir: Circuit => JString(cir.serialize) + } ) ) + class InfoSerializer extends CustomSerializer[Info](format => ( - { case JString(s) => Parser.parseInfo(s) }, - { case info: Info => JString(info.serialize) } + { + case JString(s) => Parser.parseInfo(s) + }, + { + case info: Info => JString(info.serialize) + } ) ) + class GroundTypeSerializer extends CustomSerializer[GroundType](format => ( - { case JString(s) => Parser.parseType(s).asInstanceOf[GroundType] }, - { case tpe: GroundType => JString(tpe.serialize) } + { + case JString(s) => Parser.parseType(s).asInstanceOf[GroundType] + }, + { + case tpe: GroundType => JString(tpe.serialize) + } ) ) + class UnrecognizedAnnotationSerializerSerializer extends CustomSerializer[JObject](format => ( - { case JObject(s) => JObject(s) }, - { case UnrecognizedAnnotation(underlying) => underlying } + { + case JObject(s) => JObject(s) + }, + { + case UnrecognizedAnnotation(underlying) => underlying + } ) ) @@ -294,6 +414,18 @@ object JsonProtocol extends LazyLogging { s"Annotations must be serialized as a JArray, got ${x.getClass.getName} instead!" ) } + + def getAnnotationNameFromMappingException(mappingException: MappingException): String = { + val GetClassPattern = "[^']*'([^']+)'.*".r + try { + val GetClassPattern(name) = mappingException.getMessage + name + } catch { + case _: Exception => + "Unknown" + } + } + // Recursively gather typeHints by pulling the "class" field from JObjects // Json4s should emit this as the first field in all serialized classes // Setting requireClassField mandates that all JObjects must provide a typeHint, @@ -314,17 +446,14 @@ object JsonProtocol extends LazyLogging { var classNotFoundBuildingLoaded = false val classes = findTypeHints(annos, true) val loaded = classes.map { x => - val result = - try { - val result = Class.forName(x) - result - } catch { - case _: java.lang.ClassNotFoundException => - classNotFoundBuildingLoaded = true // tells us which Exception to throw in recovery - // Found an annotation we don't recognize, So add UnrecognizedAnnotation to `loaded` - Class.forName("firrtl.annotations.UnrecognizedAnnotation") - } - result + try { + Class.forName(x) + } catch { + case _: java.lang.ClassNotFoundException => + classNotFoundBuildingLoaded = true // tells us which Exception to throw in recovery + // Found an annotation we don't recognize, So add UnrecognizedAnnotation to `loaded` + Class.forName("firrtl.annotations.UnrecognizedAnnotation") + } } implicit val formats = jsonFormat(loaded) try { @@ -333,16 +462,15 @@ object JsonProtocol extends LazyLogging { case e: org.json4s.MappingException => // If we get here, the build `read` failed to process an annotation // So we will map the annos one a time, wrapping the JSON of the unrecognized annotations - var exceptionList = new ArrayBuffer[Exception]() - val firrtlAnnos = annos.map { - case jsonAnno => - try { - jsonAnno.extract[Annotation] - } catch { - case mappingException: org.json4s.MappingException => - exceptionList += mappingException - UnrecognizedAnnotation(jsonAnno) - } + var exceptionList = new mutable.ArrayBuffer[String]() + val firrtlAnnos = annos.map { jsonAnno => + try { + jsonAnno.extract[Annotation] + } catch { + case mappingException: org.json4s.MappingException => + exceptionList += getAnnotationNameFromMappingException(mappingException) + UnrecognizedAnnotation(jsonAnno) + } } if (firrtlAnnos.contains(AllowUnrecognizedAnnotations)) { @@ -350,12 +478,18 @@ object JsonProtocol extends LazyLogging { } else { logger.error( "Annotation parsing found unrecognized annotations\n" + - "This error can be ignored with an AllowUnrecognizedAnnotationsAnnotation" + + "This error can be suppressed with an AllowUnrecognizedAnnotationsAnnotation" + " or command line flag --allow-unrecognized-annotations\n" + exceptionList.mkString("\n") ) if (classNotFoundBuildingLoaded) { - throw new AnnotationClassNotFoundException(e.getMessage) + val problems = exceptionList.distinct.take(10).mkString(", ") + val dots = if (exceptionList.length > 10) { + ", ..." + } else { + "" + } + throw UnrecogizedAnnotationsException(s"($problems$dots)") } else { throw e } // throw the mapping exception @@ -364,15 +498,22 @@ object JsonProtocol extends LazyLogging { }.recoverWith { // Translate some generic errors to specific ones case e: java.lang.ClassNotFoundException => - Failure(new AnnotationClassNotFoundException(e.getMessage)) + Failure(AnnotationClassNotFoundException(e.getMessage)) // Eat the stack traces of json4s exceptions case e @ (_: org.json4s.ParserUtil.ParseException | _: org.json4s.MappingException) => - Failure(new InvalidAnnotationJSONException(e.getMessage)) + Failure(InvalidAnnotationJSONException(e.getMessage)) }.recoverWith { // If the input is a file, wrap in InvalidAnnotationFileException + case e: UnrecogizedAnnotationsException => + in match { + case FileInput(file) => + Failure(InvalidAnnotationFileException(file, e)) + case _ => + Failure(e) + } case e: FirrtlUserException => in match { case FileInput(file) => - Failure(new InvalidAnnotationFileException(file, e)) + Failure(InvalidAnnotationFileException(file, e)) case _ => Failure(e) } diff --git a/src/main/scala/firrtl/stage/FirrtlAnnotations.scala b/src/main/scala/firrtl/stage/FirrtlAnnotations.scala index 5bc5a18cdd..6dbb04342f 100644 --- a/src/main/scala/firrtl/stage/FirrtlAnnotations.scala +++ b/src/main/scala/firrtl/stage/FirrtlAnnotations.scala @@ -326,7 +326,7 @@ case object PrettyNoExprInlining extends NoTargetAnnotation with FirrtlOption wi case object AllowUnrecognizedAnnotations extends NoTargetAnnotation with FirrtlOption with HasShellOptions { val options = Seq( - new ShellOption[String]( + new ShellOption[Unit]( longOption = "allow-unrecognized-annotations", toAnnotationSeq = _ => Seq(this), helpText = "Allow annotation files to contain unrecognized annotations" diff --git a/src/main/scala/firrtl/stage/package.scala b/src/main/scala/firrtl/stage/package.scala index 92736963ff..b3fe24738e 100644 --- a/src/main/scala/firrtl/stage/package.scala +++ b/src/main/scala/firrtl/stage/package.scala @@ -34,6 +34,7 @@ package object stage { case WarnNoScalaVersionDeprecation => c case PrettyNoExprInlining => c case _: DisableFold => c + case AllowUnrecognizedAnnotations => c case CurrentFirrtlStateAnnotation(a) => c } } diff --git a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala index 40e964bc07..c949d9ad32 100644 --- a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala +++ b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala @@ -4,16 +4,20 @@ package firrtlTests.annotationTests import firrtl.annotations._ import firrtl.passes.memlib.ReplSeqMemAnnotation +import firrtl.stage.FirrtlMain import firrtl.testutils.FirrtlFlatSpec import firrtl.transforms.BlackBoxInlineAnno import logger.Logger import logger.Logger.OutputCaptor +import java.io.{File, PrintWriter} + class UnrecognizedAnnotationSpec extends FirrtlFlatSpec { behavior.of("unrecognized annotations can be carried through serialization and deserialization") it should "preserve unknown annotations when allowed" in { - val annotations = JsonProtocol.deserialize(UnrecognizedAnnotationTextGenerator.jsonText(includeWithOutAllow = true)) + val annotations = + JsonProtocol.deserialize(UnrecognizedAnnotationTextGenerator.jsonText(includeAllowUnrecognizedAnnotations = true)) annotations.exists(_.isInstanceOf[BlackBoxInlineAnno]) should be(true) annotations.count(_.isInstanceOf[UnrecognizedAnnotation]) should be(2) @@ -34,7 +38,9 @@ class UnrecognizedAnnotationSpec extends FirrtlFlatSpec { Logger.setOutput(captor.printStream) val parsingError = intercept[AnnotationClassNotFoundException] { - JsonProtocol.deserialize(UnrecognizedAnnotationTextGenerator.jsonText(includeWithOutAllow = false)) + JsonProtocol.deserialize( + UnrecognizedAnnotationTextGenerator.jsonText(includeAllowUnrecognizedAnnotations = false) + ) } parsingError.getMessage should include("RegFieldDescMappingAnnotation") @@ -51,16 +57,102 @@ class UnrecognizedAnnotationSpec extends FirrtlFlatSpec { ) } } + + case class TestFileNames( + allowUnrecognized: Boolean, + inputAnnotations: String, + outputAnnotations: String, + firrtlSource: String, + firrtlOutput: String) + + def setupFiles(allowUnrecognized: Boolean): TestFileNames = { + val dirName = if (allowUnrecognized) { + s"test_run_dir/unrecognized_annotation_allowed" + } else { + s"test_run_dir/unrecognized_annotation_not_allowed" + } + val dir = new File(dirName) + dir.mkdirs() + + val fileNames = TestFileNames( + allowUnrecognized = allowUnrecognized, + inputAnnotations = s"$dirName/input_annotations.json", + outputAnnotations = s"$dirName/output_annotations.json", + firrtlSource = s"$dirName/trivial.fir", + firrtlOutput = s"$dirName/trivial_out" + ) + + def writeText(fileName: String, text: String): Unit = { + val writer = new PrintWriter(fileName) + writer.write(text) + writer.close() + } + + writeText( + fileNames.inputAnnotations, + UnrecognizedAnnotationTextGenerator.jsonText(includeAllowUnrecognizedAnnotations = allowUnrecognized) + ) + writeText( + fileNames.firrtlSource, + s""" + |circuit Trivial : + | module Trivial : + | input clock : Clock + | input reset : UInt<1> + |""".stripMargin + ) + fileNames + } + + /* construct an array of command line strings, based on file names and */ + def makeCommandLineArgs(fileNames: TestFileNames): Array[String] = { + + (if (fileNames.allowUnrecognized) { + Array("--allow-unrecognized-annotations") + } else { + Array.empty[String] + }) ++ + Array( + "--annotation-file", + fileNames.inputAnnotations, + "-i", + fileNames.firrtlSource, + "-X", + "high", + "-o", + fileNames.firrtlOutput, + "--output-annotation-file", + fileNames.outputAnnotations + ) + } + + it should "fail when command line annotation file contains unrecognized annotations and they are not allowed" in { + val fileNames = setupFiles(allowUnrecognized = false) + val args = makeCommandLineArgs(fileNames) + val e = intercept[InvalidAnnotationFileException] { + FirrtlMain.main(args) + } + + e.getMessage should include(fileNames.inputAnnotations) + e.getCause.getMessage should include("freechips.rocketchip.util.RegFieldDescMappingAnnotation") + e.getCause.getMessage should include("freechips.rocketchip.util.SRAMAnnotation") + } + + it should "not fail when command line annotation file contains unrecognized annotations but AllowUnrecognized " in { + val fileNames = setupFiles(allowUnrecognized = true) + val args = makeCommandLineArgs(fileNames) + FirrtlMain.main(args) + } } object UnrecognizedAnnotationTextGenerator { - def jsonText(includeWithOutAllow: Boolean): String = { + def jsonText(includeAllowUnrecognizedAnnotations: Boolean): String = { val s = new StringBuilder() s ++= """[""" - if (includeWithOutAllow) { + if (includeAllowUnrecognizedAnnotations) { s ++= """ {""" s ++= """ "class":"firrtl.stage.AllowUnrecognizedAnnotations$"""" s ++= """ },""" From 97f25ca4f88db739681d5e981a2934cde17e280e Mon Sep 17 00:00:00 2001 From: chick Date: Mon, 1 Nov 2021 22:34:26 -0700 Subject: [PATCH 03/10] - changed suppressed back to ignored - text has been simplified when unrecognized errors occur --- src/main/scala/firrtl/annotations/JsonProtocol.scala | 2 +- .../annotationTests/UnrecognizedAnnotationSpec.scala | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala index 2b9499fc3b..16c21ddc1a 100644 --- a/src/main/scala/firrtl/annotations/JsonProtocol.scala +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -478,7 +478,7 @@ object JsonProtocol extends LazyLogging { } else { logger.error( "Annotation parsing found unrecognized annotations\n" + - "This error can be suppressed with an AllowUnrecognizedAnnotationsAnnotation" + + "This error can be ignored with an AllowUnrecognizedAnnotationsAnnotation" + " or command line flag --allow-unrecognized-annotations\n" + exceptionList.mkString("\n") ) diff --git a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala index c949d9ad32..4bd0ce08e7 100644 --- a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala +++ b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala @@ -37,7 +37,7 @@ class UnrecognizedAnnotationSpec extends FirrtlFlatSpec { val captor = new OutputCaptor Logger.setOutput(captor.printStream) - val parsingError = intercept[AnnotationClassNotFoundException] { + val parsingError = intercept[UnrecogizedAnnotationsException] { JsonProtocol.deserialize( UnrecognizedAnnotationTextGenerator.jsonText(includeAllowUnrecognizedAnnotations = false) ) @@ -50,10 +50,10 @@ class UnrecognizedAnnotationSpec extends FirrtlFlatSpec { "This error can be ignored with an AllowUnrecognizedAnnotationsAnnotation or command line flag --allow-unrecognized-annotations" ) output should include( - "org.json4s.package$MappingException: Do not know how to deserialize 'freechips.rocketchip.util.RegFieldDescMappingAnnotation'" + "freechips.rocketchip.util.RegFieldDescMappingAnnotation" ) output should include( - "org.json4s.package$MappingException: Do not know how to deserialize 'freechips.rocketchip.util.SRAMAnnotation'" + "freechips.rocketchip.util.SRAMAnnotation" ) } } From 6759ec7d5b499234e5e11f2cd5ba904d1710ad95 Mon Sep 17 00:00:00 2001 From: chick Date: Mon, 1 Nov 2021 22:52:36 -0700 Subject: [PATCH 04/10] - cleaned up annotation test data for UnrecognizedAnnotationSpec --- .../UnrecognizedAnnotationSpec.scala | 346 +++++++++--------- 1 file changed, 165 insertions(+), 181 deletions(-) diff --git a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala index 4bd0ce08e7..88a7335a4f 100644 --- a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala +++ b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala @@ -148,188 +148,172 @@ class UnrecognizedAnnotationSpec extends FirrtlFlatSpec { object UnrecognizedAnnotationTextGenerator { def jsonText(includeAllowUnrecognizedAnnotations: Boolean): String = { - val s = new StringBuilder() - - s ++= """[""" - - if (includeAllowUnrecognizedAnnotations) { - s ++= """ {""" - s ++= """ "class":"firrtl.stage.AllowUnrecognizedAnnotations$"""" - s ++= """ },""" + val serializedAllowUnrecognized = if (includeAllowUnrecognizedAnnotations) { + """ + | { + | "class": "firrtl.stage.AllowUnrecognizedAnnotations$" + | },""".stripMargin + } else { + "" } - s ++= """ {""" - s ++= """ "class":"firrtl.transforms.BlackBoxInlineAnno",""" - s ++= """ "target":"TestHarness.plusarg_reader_27",""" - s ++= """ "name":"plusarg_reader.v",""" - s ++= """ "text":"// See LICENSE.SiFive for license details.\n\n//VCS coverage exclude_file\n\n// No default parameter values are intended, nor does IEEE 1800-2012 require them (clause A.2.4 param_assignment),\n// but Incisive demands them. These default values should never be used.\nmodule plusarg_reader #(\n parameter FORMAT=\"borked=%d\",\n parameter WIDTH=1,\n parameter [WIDTH-1:0] DEFAULT=0\n) (\n output [WIDTH-1:0] out\n);\n\n`ifdef SYNTHESIS\nassign out = DEFAULT;\n`else\nreg [WIDTH-1:0] myplus;\nassign out = myplus;\n\ninitial begin\n if (!$value$plusargs(FORMAT, myplus)) myplus = DEFAULT;\nend\n`endif\n\nendmodule\n"""" - s ++= """ },""" - s ++= """ {""" - s ++= """ "class":"freechips.rocketchip.util.RegFieldDescMappingAnnotation",""" - s ++= """ "target":"TestHarness.PeripheryBus",""" - s ++= """ "regMappingSer":{""" - s ++= """ "displayName":"PeripheryBus",""" - s ++= """ "deviceName":"PeripheryBus",""" - s ++= """ "baseAddress":16384,""" - s ++= """ "regFields":[""" - s ++= """ {""" - s ++= """ "byteOffset":"0x0",""" - s ++= """ "bitOffset":0,""" - s ++= """ "bitWidth":8,""" - s ++= """ "name":"unnamedRegField0_0",""" - s ++= """ "resetValue":0,""" - s ++= """ "accessType":"None",""" - s ++= """ "wrType":"None",""" - s ++= """ "rdAction":"None",""" - s ++= """ "desc":"None",""" - s ++= """ "group":"None",""" - s ++= """ "groupDesc":"None",""" - s ++= """ "volatile":false,""" - s ++= """ "hasReset":false,""" - s ++= """ "enumerations":{""" - s ++= """""" - s ++= """ }""" - s ++= """ },""" - s ++= """ {""" - s ++= """ "byteOffset":"0x0",""" - s ++= """ "bitOffset":8,""" - s ++= """ "bitWidth":8,""" - s ++= """ "name":"unnamedRegField0_8",""" - s ++= """ "resetValue":0,""" - s ++= """ "accessType":"None",""" - s ++= """ "wrType":"None",""" - s ++= """ "rdAction":"None",""" - s ++= """ "desc":"None",""" - s ++= """ "group":"None",""" - s ++= """ "groupDesc":"None",""" - s ++= """ "volatile":false,""" - s ++= """ "hasReset":false,""" - s ++= """ "enumerations":{""" - s ++= """""" - s ++= """ }""" - s ++= """ },""" - s ++= """ {""" - s ++= """ "byteOffset":"0x0",""" - s ++= """ "bitOffset":16,""" - s ++= """ "bitWidth":8,""" - s ++= """ "name":"unnamedRegField0_16",""" - s ++= """ "resetValue":0,""" - s ++= """ "accessType":"None",""" - s ++= """ "wrType":"None",""" - s ++= """ "rdAction":"None",""" - s ++= """ "desc":"None",""" - s ++= """ "group":"None",""" - s ++= """ "groupDesc":"None",""" - s ++= """ "volatile":false,""" - s ++= """ "hasReset":false,""" - s ++= """ "enumerations":{""" - s ++= """""" - s ++= """ }""" - s ++= """ },""" - s ++= """ {""" - s ++= """ "byteOffset":"0x0",""" - s ++= """ "bitOffset":24,""" - s ++= """ "bitWidth":8,""" - s ++= """ "name":"unnamedRegField0_24",""" - s ++= """ "resetValue":0,""" - s ++= """ "accessType":"None",""" - s ++= """ "wrType":"None",""" - s ++= """ "rdAction":"None",""" - s ++= """ "desc":"None",""" - s ++= """ "group":"None",""" - s ++= """ "groupDesc":"None",""" - s ++= """ "volatile":false,""" - s ++= """ "hasReset":false,""" - s ++= """ "enumerations":{""" - s ++= """""" - s ++= """ }""" - s ++= """ },""" - s ++= """ {""" - s ++= """ "byteOffset":"0x0",""" - s ++= """ "bitOffset":32,""" - s ++= """ "bitWidth":8,""" - s ++= """ "name":"unnamedRegField0_32",""" - s ++= """ "resetValue":0,""" - s ++= """ "accessType":"None",""" - s ++= """ "wrType":"None",""" - s ++= """ "rdAction":"None",""" - s ++= """ "desc":"None",""" - s ++= """ "group":"None",""" - s ++= """ "groupDesc":"None",""" - s ++= """ "volatile":false,""" - s ++= """ "hasReset":false,""" - s ++= """ "enumerations":{""" - s ++= """""" - s ++= """ }""" - s ++= """ },""" - s ++= """ {""" - s ++= """ "byteOffset":"0x0",""" - s ++= """ "bitOffset":40,""" - s ++= """ "bitWidth":8,""" - s ++= """ "name":"unnamedRegField0_40",""" - s ++= """ "resetValue":0,""" - s ++= """ "accessType":"None",""" - s ++= """ "wrType":"None",""" - s ++= """ "rdAction":"None",""" - s ++= """ "desc":"None",""" - s ++= """ "group":"None",""" - s ++= """ "groupDesc":"None",""" - s ++= """ "volatile":false,""" - s ++= """ "hasReset":false,""" - s ++= """ "enumerations":{""" - s ++= """""" - s ++= """ }""" - s ++= """ },""" - s ++= """ {""" - s ++= """ "byteOffset":"0x0",""" - s ++= """ "bitOffset":48,""" - s ++= """ "bitWidth":8,""" - s ++= """ "name":"unnamedRegField0_48",""" - s ++= """ "resetValue":0,""" - s ++= """ "accessType":"None",""" - s ++= """ "wrType":"None",""" - s ++= """ "rdAction":"None",""" - s ++= """ "desc":"None",""" - s ++= """ "group":"None",""" - s ++= """ "groupDesc":"None",""" - s ++= """ "volatile":false,""" - s ++= """ "hasReset":false,""" - s ++= """ "enumerations":{""" - s ++= """""" - s ++= """ }""" - s ++= """ },""" - s ++= """ {""" - s ++= """ "byteOffset":"0x0",""" - s ++= """ "bitOffset":56,""" - s ++= """ "bitWidth":8,""" - s ++= """ "name":"unnamedRegField0_56",""" - s ++= """ "resetValue":0,""" - s ++= """ "accessType":"None",""" - s ++= """ "wrType":"None",""" - s ++= """ "rdAction":"None",""" - s ++= """ "desc":"None",""" - s ++= """ "group":"None",""" - s ++= """ "groupDesc":"None",""" - s ++= """ "volatile":false,""" - s ++= """ "hasReset":false,""" - s ++= """ "enumerations":{""" - s ++= """""" - s ++= """ }""" - s ++= """ }""" - s ++= """ ]""" - s ++= """ }""" - s ++= """ },""" - s ++= """ {""" - s ++= """ "class":"freechips.rocketchip.util.SRAMAnnotation",""" - s ++= """ "target":"TestHarness.Directory.cc_dir",""" - s ++= """ "address_width":10,""" - s ++= """ "name":"cc_dir",""" - s ++= """ "data_width":136,""" - s ++= """ "depth":1024,""" - s ++= """ "description":"Directory RAM",""" - s ++= """ "write_mask_granularity":17""" - s ++= """ }""" - s ++= """]""" - s.toString + s"""|[$serializedAllowUnrecognized + | { + | "class": "firrtl.transforms.BlackBoxInlineAnno", + | "target": "TestHarness.plusarg_reader_27", + | "name": "plusarg_reader.v", + | "text": "License text" + | }, + | { + | "class": "freechips.rocketchip.util.RegFieldDescMappingAnnotation", + | "target": "TestHarness.PeripheryBus", + | "regMappingSer": { + | "displayName": "PeripheryBus", + | "deviceName": "PeripheryBus", + | "baseAddress": 16384, + | "regFields": [ + | { + | "byteOffset": "0x0", + | "bitOffset": 0, + | "bitWidth": 8, + | "name": "unnamedRegField0_0", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 8, + | "bitWidth": 8, + | "name": "unnamedRegField0_8", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 16, + | "bitWidth": 8, + | "name": "unnamedRegField0_16", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 24, + | "bitWidth": 8, + | "name": "unnamedRegField0_24", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 32, + | "bitWidth": 8, + | "name": "unnamedRegField0_32", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 40, + | "bitWidth": 8, + | "name": "unnamedRegField0_40", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 48, + | "bitWidth": 8, + | "name": "unnamedRegField0_48", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 56, + | "bitWidth": 8, + | "name": "unnamedRegField0_56", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | } + | ] + | } + | }, + | { + | "class": "freechips.rocketchip.util.SRAMAnnotation", + | "target": "TestHarness.Directory.cc_dir", + | "address_width": 10, + | "name": "cc_dir", + | "data_width": 136, + | "depth": 1024, + | "description": "Directory RAM", + | "write_mask_granularity": 17 + | } + |] + |""".stripMargin } } From 9911e73e2b05c0771b8cc5d33619701ea3a4c5dc Mon Sep 17 00:00:00 2001 From: chick Date: Mon, 1 Nov 2021 22:52:55 -0700 Subject: [PATCH 05/10] - scalafmt --- .../UnrecognizedAnnotationSpec.scala | 314 +++++++++--------- 1 file changed, 157 insertions(+), 157 deletions(-) diff --git a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala index 88a7335a4f..56ad7f3394 100644 --- a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala +++ b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala @@ -158,162 +158,162 @@ object UnrecognizedAnnotationTextGenerator { } s"""|[$serializedAllowUnrecognized - | { - | "class": "firrtl.transforms.BlackBoxInlineAnno", - | "target": "TestHarness.plusarg_reader_27", - | "name": "plusarg_reader.v", - | "text": "License text" - | }, - | { - | "class": "freechips.rocketchip.util.RegFieldDescMappingAnnotation", - | "target": "TestHarness.PeripheryBus", - | "regMappingSer": { - | "displayName": "PeripheryBus", - | "deviceName": "PeripheryBus", - | "baseAddress": 16384, - | "regFields": [ - | { - | "byteOffset": "0x0", - | "bitOffset": 0, - | "bitWidth": 8, - | "name": "unnamedRegField0_0", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 8, - | "bitWidth": 8, - | "name": "unnamedRegField0_8", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 16, - | "bitWidth": 8, - | "name": "unnamedRegField0_16", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 24, - | "bitWidth": 8, - | "name": "unnamedRegField0_24", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 32, - | "bitWidth": 8, - | "name": "unnamedRegField0_32", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 40, - | "bitWidth": 8, - | "name": "unnamedRegField0_40", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 48, - | "bitWidth": 8, - | "name": "unnamedRegField0_48", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 56, - | "bitWidth": 8, - | "name": "unnamedRegField0_56", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | } - | ] - | } - | }, - | { - | "class": "freechips.rocketchip.util.SRAMAnnotation", - | "target": "TestHarness.Directory.cc_dir", - | "address_width": 10, - | "name": "cc_dir", - | "data_width": 136, - | "depth": 1024, - | "description": "Directory RAM", - | "write_mask_granularity": 17 - | } - |] - |""".stripMargin + | { + | "class": "firrtl.transforms.BlackBoxInlineAnno", + | "target": "TestHarness.plusarg_reader_27", + | "name": "plusarg_reader.v", + | "text": "License text" + | }, + | { + | "class": "freechips.rocketchip.util.RegFieldDescMappingAnnotation", + | "target": "TestHarness.PeripheryBus", + | "regMappingSer": { + | "displayName": "PeripheryBus", + | "deviceName": "PeripheryBus", + | "baseAddress": 16384, + | "regFields": [ + | { + | "byteOffset": "0x0", + | "bitOffset": 0, + | "bitWidth": 8, + | "name": "unnamedRegField0_0", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 8, + | "bitWidth": 8, + | "name": "unnamedRegField0_8", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 16, + | "bitWidth": 8, + | "name": "unnamedRegField0_16", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 24, + | "bitWidth": 8, + | "name": "unnamedRegField0_24", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 32, + | "bitWidth": 8, + | "name": "unnamedRegField0_32", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 40, + | "bitWidth": 8, + | "name": "unnamedRegField0_40", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 48, + | "bitWidth": 8, + | "name": "unnamedRegField0_48", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | }, + | { + | "byteOffset": "0x0", + | "bitOffset": 56, + | "bitWidth": 8, + | "name": "unnamedRegField0_56", + | "resetValue": 0, + | "accessType": "None", + | "wrType": "None", + | "rdAction": "None", + | "desc": "None", + | "group": "None", + | "groupDesc": "None", + | "volatile": false, + | "hasReset": false, + | "enumerations": {} + | } + | ] + | } + | }, + | { + | "class": "freechips.rocketchip.util.SRAMAnnotation", + | "target": "TestHarness.Directory.cc_dir", + | "address_width": 10, + | "name": "cc_dir", + | "data_width": 136, + | "depth": 1024, + | "description": "Directory RAM", + | "write_mask_granularity": 17 + | } + |] + |""".stripMargin } } From 8bccb9ac964c891c8da9488f41e2c239d509a116 Mon Sep 17 00:00:00 2001 From: chick Date: Tue, 2 Nov 2021 14:20:36 -0700 Subject: [PATCH 06/10] - moved regex to object level - finally got the flag passing down into the deserialization right in - JsonProtocol - GetIncludes - Rearranged allowing unrecognized tests - Adding checking for annotation having made it through to the output annotations --- .../firrtl/annotations/JsonProtocol.scala | 15 ++-- .../firrtl/options/phases/GetIncludes.scala | 12 ++- .../UnrecognizedAnnotationSpec.scala | 87 ++++++++++++------- 3 files changed, 74 insertions(+), 40 deletions(-) diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala index 16c21ddc1a..3098f1f7d7 100644 --- a/src/main/scala/firrtl/annotations/JsonProtocol.scala +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -26,6 +26,8 @@ trait HasSerializationHints { case class UnserializeableAnnotation(error: String, content: String) extends NoTargetAnnotation object JsonProtocol extends LazyLogging { + val GetClassPattern = "[^']*'([^']+)'.*".r + class TransformClassSerializer extends CustomSerializer[Class[_ <: Transform]](format => ( @@ -401,11 +403,14 @@ object JsonProtocol extends LazyLogging { /** Deserialize JSON input into a Seq[Annotation] * * @param in JsonInput, can be file or string + * @param allowUnrecognizedAnnotations is set to true if command line contains flag to allow this behavior * @return */ - def deserialize(in: JsonInput): Seq[Annotation] = deserializeTry(in).get + def deserialize(in: JsonInput, allowUnrecognizedAnnotations: Boolean = false): Seq[Annotation] = { + deserializeTry(in, allowUnrecognizedAnnotations).get + } - def deserializeTry(in: JsonInput): Try[Seq[Annotation]] = Try { + def deserializeTry(in: JsonInput, allowUnrecognizedAnnotations: Boolean = false): Try[Seq[Annotation]] = Try { val parsed = parse(in) val annos = parsed match { case JArray(objs) => objs @@ -415,14 +420,14 @@ object JsonProtocol extends LazyLogging { ) } + /* Tries to extract class name from the mapping exception */ def getAnnotationNameFromMappingException(mappingException: MappingException): String = { - val GetClassPattern = "[^']*'([^']+)'.*".r try { val GetClassPattern(name) = mappingException.getMessage name } catch { case _: Exception => - "Unknown" + mappingException.getMessage } } @@ -473,7 +478,7 @@ object JsonProtocol extends LazyLogging { } } - if (firrtlAnnos.contains(AllowUnrecognizedAnnotations)) { + if (firrtlAnnos.contains(AllowUnrecognizedAnnotations) || allowUnrecognizedAnnotations) { firrtlAnnos } else { logger.error( diff --git a/src/main/scala/firrtl/options/phases/GetIncludes.scala b/src/main/scala/firrtl/options/phases/GetIncludes.scala index 15692e7989..d50b2c6fb3 100644 --- a/src/main/scala/firrtl/options/phases/GetIncludes.scala +++ b/src/main/scala/firrtl/options/phases/GetIncludes.scala @@ -6,9 +6,9 @@ import firrtl.AnnotationSeq import firrtl.annotations.{AnnotationFileNotFoundException, JsonProtocol} import firrtl.options.{InputAnnotationFileAnnotation, Phase, StageUtils} import firrtl.FileUtils +import firrtl.stage.AllowUnrecognizedAnnotations import java.io.File - import scala.collection.mutable import scala.util.{Failure, Try} @@ -25,10 +25,13 @@ class GetIncludes extends Phase { * @param filename a JSON or YAML file of [[annotations.Annotation]] * @throws annotations.AnnotationFileNotFoundException if the file does not exist */ - private def readAnnotationsFromFile(filename: String): AnnotationSeq = { + private def readAnnotationsFromFile( + filename: String, + allowUnrecognizedAnnotations: Boolean = false + ): AnnotationSeq = { val file = new File(filename).getCanonicalFile if (!file.exists) { throw new AnnotationFileNotFoundException(file) } - JsonProtocol.deserialize(file) + JsonProtocol.deserialize(file, allowUnrecognizedAnnotations) } /** Recursively read all [[Annotation]]s from any [[InputAnnotationFileAnnotation]]s while making sure that each file is @@ -38,6 +41,7 @@ class GetIncludes extends Phase { * @return the original annotation sequence with any discovered annotations added */ private def getIncludes(includeGuard: mutable.Set[String] = mutable.Set())(annos: AnnotationSeq): AnnotationSeq = { + val allowUnrecognizedAnnotations = annos.contains(AllowUnrecognizedAnnotations) annos.flatMap { case a @ InputAnnotationFileAnnotation(value) => if (includeGuard.contains(value)) { @@ -45,7 +49,7 @@ class GetIncludes extends Phase { None } else { includeGuard += value - getIncludes(includeGuard)(readAnnotationsFromFile(value)) + getIncludes(includeGuard)(readAnnotationsFromFile(value, allowUnrecognizedAnnotations)) } case x => Seq(x) } diff --git a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala index 56ad7f3394..5f6be9f3b9 100644 --- a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala +++ b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala @@ -2,6 +2,7 @@ package firrtlTests.annotationTests +import firrtl.FileUtils import firrtl.annotations._ import firrtl.passes.memlib.ReplSeqMemAnnotation import firrtl.stage.FirrtlMain @@ -58,26 +59,68 @@ class UnrecognizedAnnotationSpec extends FirrtlFlatSpec { } } + // Following test will operate on an annotation JSON file with two unrecognized annotations in it + // + + it should "fail by default" in { + val fileNames = setupFiles(addAllowUnrecognizedFlag = false, addAllowUnrecognizedAnno = false) + val args = makeCommandLineArgs(fileNames) + val e = intercept[InvalidAnnotationFileException] { + FirrtlMain.main(args) + } + + e.getMessage should include(fileNames.inputAnnotations) + e.getCause.getMessage should include("freechips.rocketchip.util.RegFieldDescMappingAnnotation") + e.getCause.getMessage should include("freechips.rocketchip.util.SRAMAnnotation") + } + + it should "succeed when the AllowUnrecognized flag is passed on command line" in { + val fileNames = setupFiles(addAllowUnrecognizedFlag = false, addAllowUnrecognizedAnno = true) + shouldSucceed(fileNames) + } + + it should "succeed when the AllowUnrecognizedAnnotation is in the annotation file" in { + val fileNames = setupFiles(addAllowUnrecognizedFlag = true, addAllowUnrecognizedAnno = false) + shouldSucceed(fileNames) + } + + it should "succeed when both forms of the override are specified" in { + val fileNames = setupFiles(addAllowUnrecognizedFlag = true, addAllowUnrecognizedAnno = true) + shouldSucceed(fileNames) + } + + def shouldSucceed(fileNames: TestFileNames): Unit = { + val args = makeCommandLineArgs(fileNames) + FirrtlMain.main(args) + + val outputAnnotationText = FileUtils.getText(fileNames.outputAnnotationsFull) + outputAnnotationText should include("freechips.rocketchip.util.RegFieldDescMappingAnnotation") + outputAnnotationText should include("freechips.rocketchip.util.SRAMAnnotation") + } + case class TestFileNames( - allowUnrecognized: Boolean, - inputAnnotations: String, - outputAnnotations: String, - firrtlSource: String, - firrtlOutput: String) + allowUnrecognized: Boolean, + inputAnnotations: String, + outputAnnotations: String, + outputAnnotationsFull: String, + firrtlSource: String, + firrtlOutput: String) - def setupFiles(allowUnrecognized: Boolean): TestFileNames = { - val dirName = if (allowUnrecognized) { - s"test_run_dir/unrecognized_annotation_allowed" - } else { - s"test_run_dir/unrecognized_annotation_not_allowed" + def setupFiles(addAllowUnrecognizedFlag: Boolean, addAllowUnrecognizedAnno: Boolean): TestFileNames = { + val dirName = (addAllowUnrecognizedFlag, addAllowUnrecognizedAnno) match { + case (false, false) => s"test_run_dir/unrecognized_annotation_fail" + case (true, false) => s"test_run_dir/unrecognized_annotation_flag" + case (false, true) => s"test_run_dir/unrecognized_annotation_anno" + case (true, true) => s"test_run_dir/unrecognized_annotation_flag_and_anno" } val dir = new File(dirName) dir.mkdirs() val fileNames = TestFileNames( - allowUnrecognized = allowUnrecognized, + allowUnrecognized = addAllowUnrecognizedFlag, inputAnnotations = s"$dirName/input_annotations.json", - outputAnnotations = s"$dirName/output_annotations.json", + outputAnnotations = s"$dirName/output_annotations", + outputAnnotationsFull = s"$dirName/output_annotations.anno.json", firrtlSource = s"$dirName/trivial.fir", firrtlOutput = s"$dirName/trivial_out" ) @@ -90,7 +133,7 @@ class UnrecognizedAnnotationSpec extends FirrtlFlatSpec { writeText( fileNames.inputAnnotations, - UnrecognizedAnnotationTextGenerator.jsonText(includeAllowUnrecognizedAnnotations = allowUnrecognized) + UnrecognizedAnnotationTextGenerator.jsonText(includeAllowUnrecognizedAnnotations = addAllowUnrecognizedAnno) ) writeText( fileNames.firrtlSource, @@ -125,24 +168,6 @@ class UnrecognizedAnnotationSpec extends FirrtlFlatSpec { fileNames.outputAnnotations ) } - - it should "fail when command line annotation file contains unrecognized annotations and they are not allowed" in { - val fileNames = setupFiles(allowUnrecognized = false) - val args = makeCommandLineArgs(fileNames) - val e = intercept[InvalidAnnotationFileException] { - FirrtlMain.main(args) - } - - e.getMessage should include(fileNames.inputAnnotations) - e.getCause.getMessage should include("freechips.rocketchip.util.RegFieldDescMappingAnnotation") - e.getCause.getMessage should include("freechips.rocketchip.util.SRAMAnnotation") - } - - it should "not fail when command line annotation file contains unrecognized annotations but AllowUnrecognized " in { - val fileNames = setupFiles(allowUnrecognized = true) - val args = makeCommandLineArgs(fileNames) - FirrtlMain.main(args) - } } object UnrecognizedAnnotationTextGenerator { From 4e7ee801473d3e084f1b9376fe845d066664eb6a Mon Sep 17 00:00:00 2001 From: chick Date: Tue, 2 Nov 2021 14:53:48 -0700 Subject: [PATCH 07/10] - scalafmt --- src/test/scala/firrtlTests/AnnotationTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/firrtlTests/AnnotationTests.scala b/src/test/scala/firrtlTests/AnnotationTests.scala index 6ee6385650..97a77cc42f 100644 --- a/src/test/scala/firrtlTests/AnnotationTests.scala +++ b/src/test/scala/firrtlTests/AnnotationTests.scala @@ -564,7 +564,7 @@ class JsonAnnotationTests extends AnnotationTests { val manager = setupManager(Some(anno)) the[Exception] thrownBy Driver.execute(manager) should matchPattern { - case InvalidAnnotationFileException(_, _: AnnotationClassNotFoundException) => + case InvalidAnnotationFileException(_, _: UnrecogizedAnnotationsException) => } } From 8dda90715e2b9ecec0692a3402976802885586af Mon Sep 17 00:00:00 2001 From: chick Date: Thu, 4 Nov 2021 11:49:05 -0700 Subject: [PATCH 08/10] - fixed missing distinct in building list of unrecognized annotations --- src/main/scala/firrtl/annotations/JsonProtocol.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala index 3098f1f7d7..a7fea45b6f 100644 --- a/src/main/scala/firrtl/annotations/JsonProtocol.scala +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -488,8 +488,9 @@ object JsonProtocol extends LazyLogging { exceptionList.mkString("\n") ) if (classNotFoundBuildingLoaded) { - val problems = exceptionList.distinct.take(10).mkString(", ") - val dots = if (exceptionList.length > 10) { + val distinctProblems = exceptionList.distinct + val problems = distinctProblems.take(10).mkString(", ") + val dots = if (distinctProblems.length > 10) { ", ..." } else { "" From 8007d543a2e44b29d2b0620f5cae6bb69e2e9163 Mon Sep 17 00:00:00 2001 From: chick Date: Thu, 4 Nov 2021 17:16:10 -0700 Subject: [PATCH 09/10] - JsonProtocol - Fixed up white space explosion - UnrecognizedAnnotationSerializerSerializer => UnrecognizedAnnotationSerializer - canonicalized getAnnotationNameFromMappingException - var => val - UnrecognizedAnnotationSpec - simplified sample annotation data, much smaller now --- .../firrtl/annotations/JsonProtocol.scala | 237 +++++------------- .../scala/firrtlTests/AnnotationTests.scala | 33 +++ .../UnrecognizedAnnotationSpec.scala | 143 ----------- 3 files changed, 90 insertions(+), 323 deletions(-) diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala index a7fea45b6f..a275fde1d5 100644 --- a/src/main/scala/firrtl/annotations/JsonProtocol.scala +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -8,6 +8,7 @@ import firrtl.stage.AllowUnrecognizedAnnotations import logger.LazyLogging import scala.util.{Failure, Success, Try} + import org.json4s._ import org.json4s.native.JsonMethods._ import org.json4s.native.Serialization @@ -31,64 +32,39 @@ object JsonProtocol extends LazyLogging { class TransformClassSerializer extends CustomSerializer[Class[_ <: Transform]](format => ( - { - case JString(s) => Class.forName(s).asInstanceOf[Class[_ <: Transform]] - }, - { - case x: Class[_] => JString(x.getName) - } + { 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) - } + { 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) - } + { 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) - } + { 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) - } + { case JString(s) => AnnotationUtils.toNamed(s).asInstanceOf[ComponentName] }, + { case named: ComponentName => JString(named.serialize) } ) ) - class TransformSerializer extends CustomSerializer[Transform](format => ( @@ -105,238 +81,143 @@ object JsonProtocol extends LazyLogging { case t: Throwable => throw t } }, - { - case x: Transform => JString(x.getClass.getName) - } + { case x: Transform => JString(x.getClass.getName) } ) ) - class LoadMemoryFileTypeSerializer extends CustomSerializer[MemoryLoadFileType](format => ( - { - case JString(s) => MemoryLoadFileType.deserialize(s) - }, - { - case named: MemoryLoadFileType => JString(named.serialize) - } + { case JString(s) => MemoryLoadFileType.deserialize(s) }, + { case named: MemoryLoadFileType => JString(named.serialize) } ) ) class TargetSerializer extends CustomSerializer[Target](format => ( - { - case JString(s) => Target.deserialize(s) - }, - { - case named: Target => JString(named.serialize) - } + { case JString(s) => Target.deserialize(s) }, + { case named: Target => JString(named.serialize) } ) ) - class GenericTargetSerializer extends CustomSerializer[GenericTarget](format => ( - { - case JString(s) => Target.deserialize(s).asInstanceOf[GenericTarget] - }, - { - case named: GenericTarget => JString(named.serialize) - } + { case JString(s) => Target.deserialize(s).asInstanceOf[GenericTarget] }, + { case named: GenericTarget => JString(named.serialize) } ) ) - class CircuitTargetSerializer extends CustomSerializer[CircuitTarget](format => ( - { - case JString(s) => Target.deserialize(s).asInstanceOf[CircuitTarget] - }, - { - case named: CircuitTarget => JString(named.serialize) - } + { case JString(s) => Target.deserialize(s).asInstanceOf[CircuitTarget] }, + { case named: CircuitTarget => JString(named.serialize) } ) ) - class ModuleTargetSerializer extends CustomSerializer[ModuleTarget](format => ( - { - case JString(s) => Target.deserialize(s).asInstanceOf[ModuleTarget] - }, - { - case named: ModuleTarget => JString(named.serialize) - } + { case JString(s) => Target.deserialize(s).asInstanceOf[ModuleTarget] }, + { case named: ModuleTarget => JString(named.serialize) } ) ) - class InstanceTargetSerializer extends CustomSerializer[InstanceTarget](format => ( - { - case JString(s) => Target.deserialize(s).asInstanceOf[InstanceTarget] - }, - { - case named: InstanceTarget => JString(named.serialize) - } + { case JString(s) => Target.deserialize(s).asInstanceOf[InstanceTarget] }, + { case named: InstanceTarget => JString(named.serialize) } ) ) - class ReferenceTargetSerializer extends CustomSerializer[ReferenceTarget](format => ( - { - case JString(s) => Target.deserialize(s).asInstanceOf[ReferenceTarget] - }, - { - case named: ReferenceTarget => JString(named.serialize) - } + { case JString(s) => Target.deserialize(s).asInstanceOf[ReferenceTarget] }, + { case named: ReferenceTarget => JString(named.serialize) } ) ) - class IsModuleSerializer extends CustomSerializer[IsModule](format => ( - { - case JString(s) => Target.deserialize(s).asInstanceOf[IsModule] - }, - { - case named: IsModule => JString(named.serialize) - } + { case JString(s) => Target.deserialize(s).asInstanceOf[IsModule] }, + { case named: IsModule => JString(named.serialize) } ) ) - class IsMemberSerializer extends CustomSerializer[IsMember](format => ( - { - case JString(s) => Target.deserialize(s).asInstanceOf[IsMember] - }, - { - case named: IsMember => JString(named.serialize) - } + { case JString(s) => Target.deserialize(s).asInstanceOf[IsMember] }, + { case named: IsMember => JString(named.serialize) } ) ) - class CompleteTargetSerializer extends CustomSerializer[CompleteTarget](format => ( - { - case JString(s) => Target.deserialize(s).asInstanceOf[CompleteTarget] - }, - { - case named: CompleteTarget => JString(named.serialize) - } + { case JString(s) => Target.deserialize(s).asInstanceOf[CompleteTarget] }, + { case named: CompleteTarget => JString(named.serialize) } ) ) - // FIRRTL Serializers class TypeSerializer extends CustomSerializer[Type](format => ( - { - case JString(s) => Parser.parseType(s) - }, - { - case tpe: Type => JString(tpe.serialize) - } + { case JString(s) => Parser.parseType(s) }, + { case tpe: Type => JString(tpe.serialize) } ) ) - class ExpressionSerializer extends CustomSerializer[Expression](format => ( - { - case JString(s) => Parser.parseExpression(s) - }, - { - case expr: Expression => JString(expr.serialize) - } + { case JString(s) => Parser.parseExpression(s) }, + { case expr: Expression => JString(expr.serialize) } ) ) - class StatementSerializer extends CustomSerializer[Statement](format => ( - { - case JString(s) => Parser.parseStatement(s) - }, - { - case statement: Statement => JString(statement.serialize) - } + { case JString(s) => Parser.parseStatement(s) }, + { case statement: Statement => JString(statement.serialize) } ) ) - class PortSerializer extends CustomSerializer[Port](format => ( - { - case JString(s) => Parser.parsePort(s) - }, - { - case port: Port => JString(port.serialize) - } + { case JString(s) => Parser.parsePort(s) }, + { case port: Port => JString(port.serialize) } ) ) - class DefModuleSerializer extends CustomSerializer[DefModule](format => ( - { - case JString(s) => Parser.parseDefModule(s) - }, - { - case mod: DefModule => JString(mod.serialize) - } + { case JString(s) => Parser.parseDefModule(s) }, + { case mod: DefModule => JString(mod.serialize) } ) ) - class CircuitSerializer extends CustomSerializer[Circuit](format => ( - { - case JString(s) => Parser.parse(s) - }, - { - case cir: Circuit => JString(cir.serialize) - } + { case JString(s) => Parser.parse(s) }, + { case cir: Circuit => JString(cir.serialize) } ) ) - class InfoSerializer extends CustomSerializer[Info](format => ( - { - case JString(s) => Parser.parseInfo(s) - }, - { - case info: Info => JString(info.serialize) - } + { case JString(s) => Parser.parseInfo(s) }, + { case info: Info => JString(info.serialize) } ) ) - class GroundTypeSerializer extends CustomSerializer[GroundType](format => ( - { - case JString(s) => Parser.parseType(s).asInstanceOf[GroundType] - }, - { - case tpe: GroundType => JString(tpe.serialize) - } + { case JString(s) => Parser.parseType(s).asInstanceOf[GroundType] }, + { case tpe: GroundType => JString(tpe.serialize) } ) ) - class UnrecognizedAnnotationSerializerSerializer + class UnrecognizedAnnotationSerializer extends CustomSerializer[JObject](format => ( - { - case JObject(s) => JObject(s) - }, - { - case UnrecognizedAnnotation(underlying) => underlying - } + { case JObject(s) => JObject(s) }, + { case UnrecognizedAnnotation(underlying) => underlying } ) ) @@ -351,7 +232,7 @@ object JsonProtocol extends LazyLogging { new CompleteTargetSerializer + new TypeSerializer + new ExpressionSerializer + new StatementSerializer + new PortSerializer + new DefModuleSerializer + new CircuitSerializer + new InfoSerializer + new GroundTypeSerializer + - new UnrecognizedAnnotationSerializerSerializer + new UnrecognizedAnnotationSerializer } /** Serialize annotations to a String for emission */ @@ -422,12 +303,9 @@ object JsonProtocol extends LazyLogging { /* Tries to extract class name from the mapping exception */ def getAnnotationNameFromMappingException(mappingException: MappingException): String = { - try { - val GetClassPattern(name) = mappingException.getMessage - name - } catch { - case _: Exception => - mappingException.getMessage + mappingException.getMessage match { + case GetClassPattern(name) => name + case other => other } } @@ -467,7 +345,7 @@ object JsonProtocol extends LazyLogging { case e: org.json4s.MappingException => // If we get here, the build `read` failed to process an annotation // So we will map the annos one a time, wrapping the JSON of the unrecognized annotations - var exceptionList = new mutable.ArrayBuffer[String]() + val exceptionList = new mutable.ArrayBuffer[String]() val firrtlAnnos = annos.map { jsonAnno => try { jsonAnno.extract[Annotation] @@ -520,8 +398,7 @@ object JsonProtocol extends LazyLogging { in match { case FileInput(file) => Failure(InvalidAnnotationFileException(file, e)) - case _ => - Failure(e) + case _ => Failure(e) } } } diff --git a/src/test/scala/firrtlTests/AnnotationTests.scala b/src/test/scala/firrtlTests/AnnotationTests.scala index 97a77cc42f..a8ff0aa0a1 100644 --- a/src/test/scala/firrtlTests/AnnotationTests.scala +++ b/src/test/scala/firrtlTests/AnnotationTests.scala @@ -614,4 +614,37 @@ class JsonAnnotationTests extends AnnotationTests { val cr = DoNothingTransform.runTransform(CircuitState(parse(input), ChirrtlForm, annos)) cr.annotations.toSeq shouldEqual annos } + + "fully qualified class name that is undeserializable" should "give an invalid json exception" in { + val anno = """ + |[ + | { + | "class":"firrtlTests.MyUnserAnno", + | "box":"7" + | } + |] """.stripMargin + + val manager = setupManager(Some(anno)) + the[Exception] thrownBy Driver.execute(manager) should matchPattern { + case InvalidAnnotationFileException(_, _: InvalidAnnotationJSONException) => + } + } + + "unqualified class name" should "give an unrecognized annotation exception" in { + val anno = """ + |[ + | { + | "class":"MyUnserAnno" + | "box":"7" + | } + |] """.stripMargin + val manager = setupManager(Some(anno)) + the[Exception] thrownBy Driver.execute(manager) should matchPattern { + case InvalidAnnotationFileException(_, _: UnrecogizedAnnotationsException) => + } + } } + +/* These are used by the last two tests. It is outside the main test to keep the qualified name simpler*/ +class UnserBox(val x: Int) +case class MyUnserAnno(box: UnserBox) extends NoTargetAnnotation diff --git a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala index 5f6be9f3b9..d2df77037f 100644 --- a/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala +++ b/src/test/scala/firrtlTests/annotationTests/UnrecognizedAnnotationSpec.scala @@ -191,152 +191,9 @@ object UnrecognizedAnnotationTextGenerator { | }, | { | "class": "freechips.rocketchip.util.RegFieldDescMappingAnnotation", - | "target": "TestHarness.PeripheryBus", - | "regMappingSer": { - | "displayName": "PeripheryBus", - | "deviceName": "PeripheryBus", - | "baseAddress": 16384, - | "regFields": [ - | { - | "byteOffset": "0x0", - | "bitOffset": 0, - | "bitWidth": 8, - | "name": "unnamedRegField0_0", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 8, - | "bitWidth": 8, - | "name": "unnamedRegField0_8", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 16, - | "bitWidth": 8, - | "name": "unnamedRegField0_16", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 24, - | "bitWidth": 8, - | "name": "unnamedRegField0_24", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 32, - | "bitWidth": 8, - | "name": "unnamedRegField0_32", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 40, - | "bitWidth": 8, - | "name": "unnamedRegField0_40", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 48, - | "bitWidth": 8, - | "name": "unnamedRegField0_48", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | }, - | { - | "byteOffset": "0x0", - | "bitOffset": 56, - | "bitWidth": 8, - | "name": "unnamedRegField0_56", - | "resetValue": 0, - | "accessType": "None", - | "wrType": "None", - | "rdAction": "None", - | "desc": "None", - | "group": "None", - | "groupDesc": "None", - | "volatile": false, - | "hasReset": false, - | "enumerations": {} - | } - | ] - | } | }, | { | "class": "freechips.rocketchip.util.SRAMAnnotation", - | "target": "TestHarness.Directory.cc_dir", - | "address_width": 10, - | "name": "cc_dir", - | "data_width": 136, - | "depth": 1024, - | "description": "Directory RAM", - | "write_mask_granularity": 17 | } |] |""".stripMargin From f8b0b7ea4da0c3770f1de576abdb3cd73b2ba1e3 Mon Sep 17 00:00:00 2001 From: chick Date: Fri, 12 Nov 2021 10:43:37 -0800 Subject: [PATCH 10/10] - JsonProtocol - Made GetClassPattern private - Switched to flatMap to compute `val loaded = ...` --- .../scala/firrtl/annotations/JsonProtocol.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala index a275fde1d5..6908a3a1ed 100644 --- a/src/main/scala/firrtl/annotations/JsonProtocol.scala +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -27,7 +27,7 @@ trait HasSerializationHints { case class UnserializeableAnnotation(error: String, content: String) extends NoTargetAnnotation object JsonProtocol extends LazyLogging { - val GetClassPattern = "[^']*'([^']+)'.*".r + private val GetClassPattern = "[^']*'([^']+)'.*".r class TransformClassSerializer extends CustomSerializer[Class[_ <: Transform]](format => @@ -328,15 +328,14 @@ object JsonProtocol extends LazyLogging { // to maintain backward compatibility with the exception test structure var classNotFoundBuildingLoaded = false val classes = findTypeHints(annos, true) - val loaded = classes.map { x => - try { - Class.forName(x) + val loaded = classes.flatMap { x => + (try { + Some(Class.forName(x)) } catch { case _: java.lang.ClassNotFoundException => - classNotFoundBuildingLoaded = true // tells us which Exception to throw in recovery - // Found an annotation we don't recognize, So add UnrecognizedAnnotation to `loaded` - Class.forName("firrtl.annotations.UnrecognizedAnnotation") - } + classNotFoundBuildingLoaded = true + None + }): Option[Class[_]] } implicit val formats = jsonFormat(loaded) try {