diff --git a/README.md b/README.md index 83290c6f..870a4116 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ verify the build and binary from the command line. - Local flags with `@flag` directive - DataView for copy free data passing - DateRef for copy free data passing with ownership + - Allows for not null interfaces references in records - Generating string names for C++ enums - Bug fixes @@ -301,9 +302,9 @@ Notable differences when comparing to the Java/ObjC support: easily add extension methods (by add functions to prototype) without having to derive from a base class. -The command to run Wasm/TypeScript unit tests is `bazel run -//test-suite:server-ts`. You will need the `tsc` compiler and the `browserify` -tool to run these tests. +Use `bazel run //test-suite:server-ts` to run the Wasm/TypeScript unit tests. +You will need `npm` and run `npm install` in the `test-suite` folder. +You need as well the `tsc` compiler and the `browserify` tool to run these tests. ## Async interface support diff --git a/src/source/Main.scala b/src/source/Main.scala index eb40c8c4..232d2599 100644 --- a/src/source/Main.scala +++ b/src/source/Main.scala @@ -101,6 +101,7 @@ object Main { var inFileListPath: Option[File] = None var outFileListPath: Option[File] = None var skipGeneration: Boolean = false + var allowInterfacesInRecords: Boolean = false var yamlOutFolder: Option[File] = None var yamlOutFile: Option[String] = None var yamlPrefix: String = "" @@ -265,6 +266,8 @@ object Main { .text("Optional file in which to write the list of output files produced.") opt[Boolean]("skip-generation").valueName("").foreach(x => skipGeneration = x) .text("Way of specifying if file generation should be skipped (default: false)") + opt[Boolean]("allow-interfaces-in-records").valueName("").foreach(x => allowInterfacesInRecords = x) + .text("Allows for records to contain interfaces.") note("\nIdentifier styles (ex: \"FooBar\", \"fooBar\", \"foo_bar\", \"FOO_BAR\", \"m_fooBar\")\nUse an exclamation mark to apply stricty, even on ALL_CAPS identifiers (ex: \"FooBar!\")\n") identStyle("ident-java-enum", c => { javaIdentStyle = javaIdentStyle.copy(enum = c) }) @@ -341,7 +344,7 @@ object Main { // Resolve names in IDL file, check types. System.out.println("Resolving...") - resolver.resolve(meta.defaults, idl) match { + resolver.resolve(meta.defaults, idl, allowInterfacesInRecords) match { case Some(err) => System.err.println(err) System.exit(1); return @@ -435,6 +438,7 @@ object Main { jsIdentStyle, tsOutFolder, tsModule, + allowInterfacesInRecords, outFileListWriter, skipGeneration, yamlOutFolder, diff --git a/src/source/generator.scala b/src/source/generator.scala index 907041d4..b5e16d43 100644 --- a/src/source/generator.scala +++ b/src/source/generator.scala @@ -100,6 +100,7 @@ package object generatorTools { jsIdentStyle: JsIdentStyle, tsOutFolder: Option[File], tsModule: String, + allowInterfacesInRecords: Boolean, outFileListWriter: Option[Writer], skipGeneration: Boolean, yamlOutFolder: Option[File], diff --git a/src/source/resolver.scala b/src/source/resolver.scala index bd63d25b..de678b0a 100644 --- a/src/source/resolver.scala +++ b/src/source/resolver.scala @@ -34,7 +34,7 @@ package object resolver { type Scope = immutable.Map[String,Meta] -def resolve(metas: Scope, idl: Seq[TypeDecl]): Option[Error] = { +def resolve(metas: Scope, idl: Seq[TypeDecl], allowInterfacesInRecords: Boolean): Option[Error] = { try { var topScope = metas @@ -74,7 +74,7 @@ def resolve(metas: Scope, idl: Seq[TypeDecl]): Option[Error] = { scope = scope.updated(typeParam.ident.name, MParam(typeParam.ident.name)) } - resolve(scope, typeDecl.body) + resolve(scope, typeDecl.body, allowInterfacesInRecords) } for (typeDecl <- idl) { @@ -88,10 +88,10 @@ def resolve(metas: Scope, idl: Seq[TypeDecl]): Option[Error] = { None } -private def resolve(scope: Scope, typeDef: TypeDef) { +private def resolve(scope: Scope, typeDef: TypeDef, allowInterfacesInRecords: Boolean) { typeDef match { case e: Enum => resolveEnum(scope, e) - case r: Record => resolveRecord(scope, r) + case r: Record => resolveRecord(scope, r, allowInterfacesInRecords) case i: Interface => resolveInterface(scope, i) case p: ProtobufMessage=> } @@ -214,7 +214,7 @@ private def constTypeCheck(ty: MExpr, value: Any, resolvedConsts: Seq[Const]) { } } -private def resolveRecord(scope: Scope, r: Record) { +private def resolveRecord(scope: Scope, r: Record, allowInterfacesInRecords: Boolean) { val dupeChecker = new DupeChecker("record field") for (f <- r.fields) { dupeChecker.check(f.ident) @@ -243,7 +243,9 @@ private def resolveRecord(scope: Scope, r: Record) { } case df: MDef => df.defType match { case DInterface => - throw new Error(f.ident.loc, "Interface reference cannot live in a record").toException + if (!allowInterfacesInRecords) { + throw new Error(f.ident.loc, "Interface reference cannot live in a record").toException + } case DRecord => val record = df.body.asInstanceOf[Record] if (!r.derivingTypes.subsetOf(record.derivingTypes)) @@ -252,7 +254,9 @@ private def resolveRecord(scope: Scope, r: Record) { } case e: MExtern => e.defType match { case DInterface => - throw new Error(f.ident.loc, "Interface reference cannot live in a record").toException + if (!allowInterfacesInRecords) { + throw new Error(f.ident.loc, "Interface reference cannot live in a record").toException + } case DRecord => val record = e.body.asInstanceOf[Record] if (!r.derivingTypes.subsetOf(record.derivingTypes))