From a466a66bf6a6494dc857430742bc9cc099bcf720 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Sun, 28 Jun 2015 16:55:59 -0700 Subject: [PATCH 01/52] Closer to compilation! --- src/source/CxGenerator.scala | 285 +++++++++++++++++++++++++++++++++++ src/source/CxMarshal.scala | 105 +++++++++++++ src/source/Main.scala | 48 ++++++ src/source/Marshal.scala | 1 + src/source/ast.scala | 2 + src/source/generator.scala | 15 +- 6 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 src/source/CxGenerator.scala create mode 100644 src/source/CxMarshal.scala diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala new file mode 100644 index 000000000..3d3c901d7 --- /dev/null +++ b/src/source/CxGenerator.scala @@ -0,0 +1,285 @@ +/** + * Copyright 2014 Dropbox, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package djinni + +import djinni.ast.Record.DerivingType +import djinni.ast._ +import djinni.generatorTools._ +import djinni.meta._ +import djinni.writer.IndentWriter + +import scala.collection.mutable + +class CxGenerator(spec: Spec) extends Generator(spec) { + + val marshal = new CxMarshal(spec) + + val writeCxFile = writeCppFileGeneric(spec.cxOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle, spec.cxIncludePrefix) _ + def writeHppFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = + writeHppFileGeneric(spec.cxHeaderOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle)(name, origin, includes, fwds, f, f2) + + class CxRefs(name: String) { + var hpp = mutable.TreeSet[String]() + var hppFwds = mutable.TreeSet[String]() + var cx = mutable.TreeSet[String]() + + def find(ty: TypeRef) { find(ty.resolved) } + def find(tm: MExpr) { + tm.args.foreach(find) + find(tm.base) + } + def find(m: Meta) = for(r <- marshal.references(m, name)) r match { + case ImportRef(arg) => hpp.add("#include " + arg) + case DeclRef(decl, Some(spec.cxNamespace)) => hppFwds.add(decl) + case DeclRef(_, _) => + } + } + + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { + val refs = new CxRefs(ident.name) + val self = marshal.typename(ident, e) + + writeHppFile(ident, origin, refs.hpp, refs.hppFwds, w => { + w.w(s"enum class $self : int").bracedSemi { + for (o <- e.options) { + writeDoc(w, o.doc) + w.wl(idCx.enum(o.ident.name) + ",") + } + } + }) + } + + def generateHppConstants(w: IndentWriter, consts: Seq[Const]) = { + for (c <- consts) { + w.wl + writeDoc(w, c.doc) + w.wl(s"static ${marshal.fieldType(c.ty)} const ${idCx.const(c.ident)};") + } + } + + def generateCxConstants(w: IndentWriter, consts: Seq[Const], selfName: String) = { + def writeCxConst(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match { + case l: Long => w.w(l.toString) + case d: Double if marshal.fieldType(ty) == "float" => w.w(d.toString + "f") + case d: Double => w.w(d.toString) + case b: Boolean => w.w(if (b) "true" else "false") + case s: String => w.w(s) + case e: EnumValue => w.w(marshal.typename(ty) + "::" + idCx.enum(e.ty.name + "_" + e.name)) + case v: ConstRef => w.w(selfName + "::" + idCx.const(v)) + case z: Map[_, _] => { // Value is record + val recordMdef = ty.resolved.base.asInstanceOf[MDef] + val record = recordMdef.body.asInstanceOf[Record] + val vMap = z.asInstanceOf[Map[String, Any]] + w.wl(marshal.typename(ty) + "(") + w.increase() + // Use exact sequence + val skipFirst = SkipFirst() + for (f <- record.fields) { + skipFirst {w.wl(",")} + writeCxConst(w, f.ty, vMap.apply(f.ident.name)) + w.w(" /* " + idCx.field(f.ident) + " */ ") + } + w.w(")") + w.decrease() + } + } + + val skipFirst = SkipFirst() + for (c <- consts) { + skipFirst{ w.wl } + w.w(s"${marshal.fieldType(c.ty)} const $selfName::${idCx.const(c.ident)} = ") + writeCxConst(w, c.ty, c.value) + w.wl(";") + } + } + + override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) { + val refs = new CxRefs(ident.name) + r.fields.foreach(f => refs.find(f.ty)) + r.consts.foreach(c => refs.find(c.ty)) + refs.hpp.add("#include ") // Add for std::move + + val self = marshal.typename(ident, r) + val (cxName, cxFinal) = if (r.ext.cx) (ident.name + "_base", "") else (ident.name, " final") + val actualSelf = marshal.typename(cxName, r) + + // Requiring the extended class + if (r.ext.cx) { + refs.hpp.add(s"class $self; // Requiring extended class") + refs.cx.add("#include "+q("../" + spec.cxFileIdentStyle(ident) + "." + spec.cxHeaderExt)) + } + + // C++ Header + def writeCxPrototype(w: IndentWriter) { + writeDoc(w, doc) + writeCxTypeParams(w, params) + w.w("struct " + actualSelf + cxFinal).bracedSemi { + generateHppConstants(w, r.consts) + // Field definitions. + for (f <- r.fields) { + writeDoc(w, f.doc) + w.wl(marshal.fieldType(f.ty) + " " + idCx.field(f.ident) + ";") + } + + if (r.derivingTypes.contains(DerivingType.Eq)) { + w.wl + w.wl(s"friend bool operator==(const $actualSelf& lhs, const $actualSelf& rhs);") + w.wl(s"friend bool operator!=(const $actualSelf& lhs, const $actualSelf& rhs);") + } + if (r.derivingTypes.contains(DerivingType.Ord)) { + w.wl + w.wl(s"friend bool operator<(const $actualSelf& lhs, const $actualSelf& rhs);") + w.wl(s"friend bool operator>(const $actualSelf& lhs, const $actualSelf& rhs);") + } + if (r.derivingTypes.contains(DerivingType.Eq) && r.derivingTypes.contains(DerivingType.Ord)) { + w.wl + w.wl(s"friend bool operator<=(const $actualSelf& lhs, const $actualSelf& rhs);") + w.wl(s"friend bool operator>=(const $actualSelf& lhs, const $actualSelf& rhs);") + } + + // Constructor. + if(r.fields.nonEmpty) { + w.wl + writeAlignedCall(w, actualSelf + "(", r.fields, ")", f => marshal.fieldType(f.ty) + " " + idCx.local(f.ident)) + w.wl + val init = (f: Field) => idCx.field(f.ident) + "(std::move(" + idCx.local(f.ident) + "))" + w.wl(": " + init(r.fields.head)) + r.fields.tail.map(f => ", " + init(f)).foreach(w.wl) + w.wl("{}") + } + + if (r.ext.cx) { + w.wl + w.wl(s"virtual ~$actualSelf() = default;") + w.wl + // Defining the dtor disables implicit copy/move operation generation, so re-enable them + // Make them protected to avoid slicing + w.wlOutdent("protected:") + w.wl(s"$actualSelf(const $actualSelf&) = default;") + w.wl(s"$actualSelf($actualSelf&&) = default;") + w.wl(s"$actualSelf& operator=(const $actualSelf&) = default;") + w.wl(s"$actualSelf& operator=($actualSelf&&) = default;") + } + } + } + + writeHppFile(cxName, origin, refs.hpp, refs.hppFwds, writeCxPrototype) + + if (r.consts.nonEmpty || r.derivingTypes.nonEmpty) { + writeCxFile(cxName, origin, refs.cx, w => { + generateCxConstants(w, r.consts, actualSelf) + + if (r.derivingTypes.contains(DerivingType.Eq)) { + w.wl + w.w(s"bool operator==(const $actualSelf& lhs, const $actualSelf& rhs)").braced { + if(!r.fields.isEmpty) { + writeAlignedCall(w, "return ", r.fields, " &&", "", f => s"lhs.${idCx.field(f.ident)} == rhs.${idCx.field(f.ident)}") + w.wl(";") + } else { + w.wl("return true;") + } + } + w.wl + w.w(s"bool operator!=(const $actualSelf& lhs, const $actualSelf& rhs)").braced { + w.wl("return !(lhs == rhs);") + } + } + if (r.derivingTypes.contains(DerivingType.Ord)) { + w.wl + w.w(s"bool operator<(const $actualSelf& lhs, const $actualSelf& rhs)").braced { + for(f <- r.fields) { + w.w(s"if (lhs.${idCx.field(f.ident)} < rhs.${idCx.field(f.ident)})").braced { + w.wl("return true;") + } + w.w(s"if (rhs.${idCx.field(f.ident)} < lhs.${idCx.field(f.ident)})").braced { + w.wl("return false;") + } + } + w.wl("return false;") + } + w.wl + w.w(s"bool operator>(const $actualSelf& lhs, const $actualSelf& rhs)").braced { + w.wl("return rhs < lhs;") + } + } + if (r.derivingTypes.contains(DerivingType.Eq) && r.derivingTypes.contains(DerivingType.Ord)) { + w.wl + w.w(s"bool operator<=(const $actualSelf& lhs, const $actualSelf& rhs)").braced { + w.wl("return !(rhs < lhs);") + } + w.wl + w.w(s"bool operator>=(const $actualSelf& lhs, const $actualSelf& rhs)").braced { + w.wl("return !(lhs < rhs);") + } + } + }) + } + + } + + override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) { + val refs = new CxRefs(ident.name) + i.methods.map(m => { + m.params.map(p => refs.find(p.ty)) + m.ret.foreach(refs.find) + }) + i.consts.map(c => { + refs.find(c.ty) + }) + + val self = marshal.typename(ident, i) + + writeHppFile(ident, origin, refs.hpp, refs.hppFwds, w => { + writeDoc(w, doc) + writeCxTypeParams(w, typeParams) + w.w(s"class $self").bracedSemi { + w.wlOutdent("public:") + // Destructor + w.wl(s"virtual ~$self() {}") + // Constants + generateHppConstants(w, i.consts) + // Methods + for (m <- i.methods) { + w.wl + writeDoc(w, m.doc) + val ret = marshal.returnType(m.ret) + val params = m.params.map(p => marshal.paramType(p.ty) + " " + idCx.local(p.ident)) + if (m.static) { + w.wl(s"static $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")};") + } else { + val constFlag = if (m.const) " const" else "" + w.wl(s"virtual $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag = 0;") + } + } + } + }) + + // Cx only generated in need of Constants + if (i.consts.nonEmpty) { + writeCxFile(ident, origin, refs.cx, w => { + generateCxConstants(w, i.consts, self) + }) + } + + } + + def writeCxTypeParams(w: IndentWriter, params: Seq[TypeParam]) { + if (params.isEmpty) return + w.wl("template " + params.map(p => "typename " + idCx.typeParam(p.ident)).mkString("<", ", ", ">")) + } + +} diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala new file mode 100644 index 000000000..79770c329 --- /dev/null +++ b/src/source/CxMarshal.scala @@ -0,0 +1,105 @@ +package djinni + +import djinni.ast._ +import djinni.generatorTools._ +import djinni.meta._ + +class CxMarshal(spec: Spec) extends Marshal(spec) { + + override def typename(tm: MExpr): String = toCxType(tm, None) + def typename(name: String, ty: TypeDef): String = ty match { + case e: Enum => idCx.enumType(name) + case i: Interface => idCx.ty(name) + case r: Record => idCx.ty(name) + } + + override def fqTypename(tm: MExpr): String = toCxType(tm, Some(spec.cxNamespace)) + def fqTypename(name: String, ty: TypeDef): String = ty match { + case e: Enum => withNs(Some(spec.cxNamespace), idCx.enumType(name)) + case i: Interface => withNs(Some(spec.cxNamespace), idCx.ty(name)) + case r: Record => withNs(Some(spec.cxNamespace), idCx.ty(name)) + } + + override def paramType(tm: MExpr): String = toCxParamType(tm) + override def fqParamType(tm: MExpr): String = toCxParamType(tm, Some(spec.cxNamespace)) + + override def returnType(ret: Option[TypeRef]): String = ret.fold("void")(toCxType(_, None)) + override def fqReturnType(ret: Option[TypeRef]): String = ret.fold("void")(toCxType(_, Some(spec.cxNamespace))) + + override def fieldType(tm: MExpr): String = typename(tm) + override def fqFieldType(tm: MExpr): String = fqTypename(tm) + + override def toCx(tm: MExpr, expr: String): String = throw new AssertionError("cx to cx conversion") + override def fromCx(tm: MExpr, expr: String): String = throw new AssertionError("cx to cx conversion") + + def references(m: Meta, exclude: String): Seq[SymbolReference] = m match { + case p: MPrimitive => p.idlName match { + case "i8" | "i16" | "i32" | "i64" => List(ImportRef("")) + case _ => List() + } + case MString => List(ImportRef("")) + case MDate => List(ImportRef("")) + case MBinary => List(ImportRef(""), ImportRef("")) + case MList => List(ImportRef("")) + case MSet => List(ImportRef("")) + case MMap => List(ImportRef("")) + case d: MDef => d.defType match { + case DEnum | DRecord => + if (d.name != exclude) { + List(ImportRef(q(spec.cxIncludePrefix + spec.cxFileIdentStyle(d.name) + "." + spec.cxHeaderExt))) + } else { + List() + } + case DInterface => + if (d.name != exclude) { + List(ImportRef(""), DeclRef(s"class ${typename(d.name, d.body)};", Some(spec.cxNamespace))) + } else { + List(ImportRef("")) + } + } + case p: MParam => List() + } + + private def toCxType(ty: TypeRef, namespace: Option[String] = None): String = toCxType(ty.resolved, namespace) + private def toCxType(tm: MExpr, namespace: Option[String]): String = { + def base(m: Meta): String = m match { + case p: MPrimitive => p.cName + case MString => "std::string" + case MDate => "std::chrono::system_clock::time_point" + case MBinary => "std::vector" + case MList => "std::vector" + case MSet => "std::unordered_set" + case MMap => "std::unordered_map" + case d: MDef => + d.defType match { + case DEnum => withNs(namespace, idCx.enumType(d.name)) + case DRecord => withNs(namespace, idCx.ty(d.name)) + case DInterface => s"std::shared_ptr<${withNs(namespace, idCx.ty(d.name))}>" + } + case p: MParam => idCx.typeParam(p.name) + } + def expr(tm: MExpr): String = { + val args = if (tm.args.isEmpty) "" else tm.args.map(expr).mkString("<", ", ", ">") + base(tm.base) + args + } + expr(tm) + } + + // this can be used in c++ generation to know whether a const& should be applied to the parameter or not + private def toCxParamType(tm: MExpr, namespace: Option[String] = None): String = { + val cxType = toCxType(tm, namespace) + val refType = "const " + cxType + " &" + val valueType = cxType + + def toType(expr: MExpr): String = expr.base match { + case p: MPrimitive => valueType + case d: MDef => d.defType match { + case DEnum => valueType + case _ => refType + } + case MOptional => toType(expr.args.head) + case _ => refType + } + toType(tm) + } +} diff --git a/src/source/Main.scala b/src/source/Main.scala index 34d623b0d..9b83f42ae 100644 --- a/src/source/Main.scala +++ b/src/source/Main.scala @@ -74,6 +74,15 @@ object Main { var yamlOutFolder: Option[File] = None var yamlOutFile: Option[String] = None var yamlPrefix: String = "" + var cxOutFolder: Option[File] = None + var cxNamespace: String = "djinni_generated" + var cxIdentStyle = IdentStyle.cxDefault + var cxTypeEnumIdentStyle: IdentConverter = null + var cxFileIdentStyle: IdentConverter = IdentStyle.underLower + var cxHeaderOutFolderOptional: Option[File] = None + var cxIncludePrefix: String = "" + var cxExt: String = "cx" + var cxHeaderExt: String = "hpp" val argParser = new scopt.OptionParser[Unit]("djinni") { @@ -165,6 +174,7 @@ object Main { .text("The namespace name to use for generated Objective-C++ classes.") opt[String]("objc-base-lib-include-prefix").valueName("...").foreach(x => objcBaseLibIncludePrefix = x) .text("The Objective-C++ base library's include path, relative to the Objective-C++ classes.") +<<<<<<< HEAD note("") opt[File]("yaml-out").valueName("").foreach(x => yamlOutFolder = Some(x)) .text("The output folder for YAML files (Generator disabled if unspecified).") @@ -179,6 +189,21 @@ 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[File]("cx-out").valueName("").foreach(x => cxOutFolder = Some(x)) + .text("The output folder for Cx files (Generator disabled if unspecified).") + opt[File]("cx-header-out").valueName("").foreach(x => cxHeaderOutFolderOptional = Some(x)) + .text("The output folder for Cx header files (default: the same as --cx-out).") + opt[String]("cx-include-prefix").valueName("").foreach(cxIncludePrefix = _) + .text("The prefix for #includes of header files from Cx files.") + opt[String]("cx-namespace").valueName("...").foreach(x => cxNamespace = x) + .text("The namespace name to use for generated Cx classes.") + opt[String]("cx-ext").valueName("").foreach(cxExt = _) + .text("The filename extension for Cx files (default: \"cpp\").") + opt[String]("hpp-ext").valueName("").foreach(cxHeaderExt = _) + .text("The filename extension for Cx header files (default: \"hpp\").") + +>>>>>>> 3e64732... Closer to compilation! note("\nIdentifier styles (ex: \"FooBar\", \"fooBar\", \"foo_bar\", \"FOO_BAR\", \"m_fooBar\")\n") identStyle("ident-java-enum", c => { javaIdentStyle = javaIdentStyle.copy(enum = c) }) @@ -200,6 +225,14 @@ object Main { identStyle("ident-objc-type-param", c => { objcIdentStyle = objcIdentStyle.copy(typeParam = c) }) identStyle("ident-objc-local", c => { objcIdentStyle = objcIdentStyle.copy(local = c) }) identStyle("ident-objc-file", c => { objcFileIdentStyleOptional = Some(c) }) + identStyle("ident-cx-enum", c => { cxIdentStyle = cxIdentStyle.copy(enum = c) }) + identStyle("ident-cx-field", c => { cxIdentStyle = cxIdentStyle.copy(field = c) }) + identStyle("ident-cx-method", c => { cxIdentStyle = cxIdentStyle.copy(method = c) }) + identStyle("ident-cx-type", c => { cxIdentStyle = cxIdentStyle.copy(ty = c) }) + identStyle("ident-cx-enum-type", c => { cppTypeEnumIdentStyle = c }) + identStyle("ident-cx-type-param", c => { cxIdentStyle = cxIdentStyle.copy(typeParam = c) }) + identStyle("ident-cx-local", c => { cxIdentStyle = cxIdentStyle.copy(local = c) }) + identStyle("ident-cx-file", c => { cxFileIdentStyle = c }) } @@ -214,6 +247,7 @@ object Main { val jniFileIdentStyle = jniFileIdentStyleOptional.getOrElse(cppFileIdentStyle) var objcFileIdentStyle = objcFileIdentStyleOptional.getOrElse(objcIdentStyle.ty) val objcppIncludeObjcPrefix = objcppIncludeObjcPrefixOptional.getOrElse(objcppIncludePrefix) + val cxHeaderOutFolder = if (cxHeaderOutFolderOptional.isDefined) cxHeaderOutFolderOptional else cxOutFolder // Add ObjC prefix to identstyle objcIdentStyle = objcIdentStyle.copy(ty = IdentStyle.prefix(objcTypePrefix,objcIdentStyle.ty)) @@ -223,6 +257,10 @@ object Main { cppIdentStyle = cppIdentStyle.copy(enumType = cppTypeEnumIdentStyle) } + if (cxTypeEnumIdentStyle != null) { + cxIdentStyle = cxIdentStyle.copy(enumType = cxTypeEnumIdentStyle) + } + // Parse IDL file. System.out.println("Parsing...") val inFileListWriter = if (inFileListPath.isDefined) { @@ -304,11 +342,21 @@ object Main { objcppIncludeObjcPrefix, objcppNamespace, objcBaseLibIncludePrefix, +<<<<<<< HEAD outFileListWriter, skipGeneration, yamlOutFolder, yamlOutFile, yamlPrefix) +======= + cxOutFolder, + cxNamespace, + cxFileIdentStyle, + cxHeaderOutFolderOptional, + cxIncludePrefix, + cxExt, + cxHeaderExt) +>>>>>>> 3e64732... Closer to compilation! try { diff --git a/src/source/Marshal.scala b/src/source/Marshal.scala index f4fff656e..463684b4a 100644 --- a/src/source/Marshal.scala +++ b/src/source/Marshal.scala @@ -40,6 +40,7 @@ abstract class Marshal(spec: Spec) { protected val idCpp = spec.cppIdentStyle protected val idJava = spec.javaIdentStyle protected val idObjc = spec.objcIdentStyle + protected val idCx = spec.cxIdentStyle protected def withNs(namespace: Option[String], t: String) = namespace match { case None => t diff --git a/src/source/ast.scala b/src/source/ast.scala index 6d2ca62c8..0371bcb90 100644 --- a/src/source/ast.scala +++ b/src/source/ast.scala @@ -52,6 +52,8 @@ case class Ext(java: Boolean, cpp: Boolean, objc: Boolean) { } } +case class Ext(java: Boolean, cpp: Boolean, objc: Boolean, cx: Boolean) + case class TypeRef(expr: TypeExpr) { var resolved: MExpr = null } diff --git a/src/source/generator.scala b/src/source/generator.scala index 2f9c65cf4..e55f32f87 100644 --- a/src/source/generator.scala +++ b/src/source/generator.scala @@ -73,7 +73,14 @@ package object generatorTools { skipGeneration: Boolean, yamlOutFolder: Option[File], yamlOutFile: Option[String], - yamlPrefix: String) + yamlPrefix: String, + cxOutFolder: Option[File], + cxHeaderExt: String, + cxHeaderOutFolder: Option[File], + cxIncludePrefix: String, + cxNamespace: String, + cxIdentStyle: CxIdentStyle, + cxFileIdentStyle: IdentConverter) def preComma(s: String) = { if (s.isEmpty) s else ", " + s @@ -95,6 +102,10 @@ package object generatorTools { method: IdentConverter, field: IdentConverter, local: IdentConverter, enum: IdentConverter, const: IdentConverter) + case class CxIdentStyle(ty: IdentConverter, enumType: IdentConverter, typeParam: IdentConverter, + method: IdentConverter, field: IdentConverter, local: IdentConverter, + enum: IdentConverter, const: IdentConverter) + object IdentStyle { val camelUpper = (s: String) => s.split('_').map(firstUpper).mkString val camelLower = (s: String) => { @@ -109,6 +120,7 @@ package object generatorTools { val javaDefault = JavaIdentStyle(camelUpper, camelUpper, camelLower, camelLower, camelLower, underCaps, underCaps) val cppDefault = CppIdentStyle(camelUpper, camelUpper, camelUpper, underLower, underLower, underLower, underCaps, underCaps) val objcDefault = ObjcIdentStyle(camelUpper, camelUpper, camelLower, camelLower, camelLower, camelUpper, camelUpper) + val cxDefault = CxIdentStyle(camelUpper, camelUpper, camelUpper, underLower, underLower, underLower, underCaps, underCaps) val styles = Map( "FooBar" -> camelUpper, @@ -253,6 +265,7 @@ abstract class Generator(spec: Spec) val idCpp = spec.cppIdentStyle val idJava = spec.javaIdentStyle val idObjc = spec.objcIdentStyle + val idCx = spec.cxIdentStyle def wrapNamespace(w: IndentWriter, ns: String, f: IndentWriter => Unit) { ns match { From 8d00af65809c042ef360409c887b9d0696a60bec Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Sat, 4 Jul 2015 11:31:50 -0700 Subject: [PATCH 02/52] Fixing compiler errors and warnings. Still blind monkey-coding. --- src/source/CxMarshal.scala | 2 ++ src/source/Main.scala | 16 +++++++--------- src/source/generator.scala | 1 + src/source/parser.scala | 11 ++++++++--- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index 79770c329..bef6e6b77 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -40,6 +40,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { case MString => List(ImportRef("")) case MDate => List(ImportRef("")) case MBinary => List(ImportRef(""), ImportRef("")) + case MOptional => List() case MList => List(ImportRef("")) case MSet => List(ImportRef("")) case MMap => List(ImportRef("")) @@ -67,6 +68,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { case MString => "std::string" case MDate => "std::chrono::system_clock::time_point" case MBinary => "std::vector" + case MOptional => "" case MList => "std::vector" case MSet => "std::unordered_set" case MMap => "std::unordered_map" diff --git a/src/source/Main.scala b/src/source/Main.scala index 9b83f42ae..71a874ee2 100644 --- a/src/source/Main.scala +++ b/src/source/Main.scala @@ -342,21 +342,19 @@ object Main { objcppIncludeObjcPrefix, objcppNamespace, objcBaseLibIncludePrefix, -<<<<<<< HEAD outFileListWriter, skipGeneration, yamlOutFolder, yamlOutFile, - yamlPrefix) -======= + yamlPrefix, cxOutFolder, - cxNamespace, - cxFileIdentStyle, - cxHeaderOutFolderOptional, - cxIncludePrefix, cxExt, - cxHeaderExt) ->>>>>>> 3e64732... Closer to compilation! + cxHeaderExt, + cxHeaderOutFolder, + cxIncludePrefix, + cxNamespace, + cxIdentStyle, + cxFileIdentStyle) try { diff --git a/src/source/generator.scala b/src/source/generator.scala index e55f32f87..ebdd9fe6e 100644 --- a/src/source/generator.scala +++ b/src/source/generator.scala @@ -75,6 +75,7 @@ package object generatorTools { yamlOutFile: Option[String], yamlPrefix: String, cxOutFolder: Option[File], + cxExt: String, cxHeaderExt: String, cxHeaderOutFolder: Option[File], cxIncludePrefix: String, diff --git a/src/source/parser.scala b/src/source/parser.scala index 4a561184f..82083c800 100644 --- a/src/source/parser.scala +++ b/src/source/parser.scala @@ -58,13 +58,14 @@ private object IdlParser extends RegexParsers { } def ext(default: Ext) = (rep1("+" ~> ident) >> checkExts) | success(default) - def extRecord = ext(Ext(false, false, false)) - def extInterface = ext(Ext(true, true, true)) + def extRecord = ext(Ext(false, false, false, false)) + def extInterface = ext(Ext(true, true, true, true)) def checkExts(parts: List[Ident]): Parser[Ext] = { var foundCpp = false var foundJava = false var foundObjc = false + var foundCx = false for (part <- parts) part.name match { @@ -80,9 +81,13 @@ private object IdlParser extends RegexParsers { if (foundObjc) return err("Found multiple \"o\" modifiers.") foundObjc = true } + case "x" => { + if (foundCx) return err("Found multiple \"x\" modifiers.") + foundCx = true + } case _ => return err("Invalid modifier \"" + part.name + "\"") } - success(Ext(foundJava, foundCpp, foundObjc)) + success(Ext(foundJava, foundCpp, foundObjc, foundCx)) } def typeDef: Parser[TypeDef] = record | enum | interface From d6b0236a082899b166d824c2c242aa948701dd80 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Sat, 4 Jul 2015 20:59:05 -0700 Subject: [PATCH 03/52] It compiles. Does it emit "C++/Cx" code? Probably. Will it be any good? Certainly not. Lots of work remains! --- src/source/CxMarshal.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index bef6e6b77..affbcc7ea 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -29,9 +29,6 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { override def fieldType(tm: MExpr): String = typename(tm) override def fqFieldType(tm: MExpr): String = fqTypename(tm) - override def toCx(tm: MExpr, expr: String): String = throw new AssertionError("cx to cx conversion") - override def fromCx(tm: MExpr, expr: String): String = throw new AssertionError("cx to cx conversion") - def references(m: Meta, exclude: String): Seq[SymbolReference] = m match { case p: MPrimitive => p.idlName match { case "i8" | "i16" | "i32" | "i64" => List(ImportRef("")) From 662580df938418484a252675424d83e605b353ae Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Wed, 8 Jul 2015 14:15:58 -0700 Subject: [PATCH 04/52] A start on a C++/Cx support library. --- support-lib/cx/CppWrapperCache.h | 85 ++++++++++ support-lib/cx/CxWrapperCache.h | 84 ++++++++++ support-lib/cx/Marshal.h | 277 +++++++++++++++++++++++++++++++ 3 files changed, 446 insertions(+) create mode 100644 support-lib/cx/CppWrapperCache.h create mode 100644 support-lib/cx/CxWrapperCache.h create mode 100755 support-lib/cx/Marshal.h diff --git a/support-lib/cx/CppWrapperCache.h b/support-lib/cx/CppWrapperCache.h new file mode 100644 index 000000000..24807c913 --- /dev/null +++ b/support-lib/cx/CppWrapperCache.h @@ -0,0 +1,85 @@ +// +// Copyright 2014 Dropbox, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// This header can only be imported to Objective-C++ source code! + +#include +#include +#include + +namespace djinni { + +template +class CppWrapperCache { +public: + static const std::shared_ptr & getInstance() { + static const std::shared_ptr instance(new CppWrapperCache); + // Return by const-ref. This is safe to call any time except during static destruction. + // Returning by reference lets us avoid touching the refcount unless needed. + return instance; + } + + template + id get(const std::shared_ptr & cppRef, const AllocFunc & alloc) { + std::unique_lock lock(m_mutex); + T* ptr = cppRef.get(); + auto got = m_mapping.find(ptr); + id ret; + if (got != m_mapping.end()) { + ret = got->second; + if (ret == nil) { + ret = alloc(cppRef); + m_mapping[ptr] = ret; + } + } else { + ret = alloc(cppRef); + m_mapping[ptr] = ret; + } + return ret; + } + + void remove(const std::shared_ptr & cppRef) { + std::unique_lock lock(m_mutex); + T* ptr = cppRef.get(); + if (m_mapping[ptr] == nil) { + m_mapping.erase(ptr); + } + } + + class Handle { + public: + Handle() = default; + ~Handle() { + if (_ptr) { + _cache->remove(_ptr); + } + } + void assign(const std::shared_ptr& ptr) { _ptr = ptr; } + const std::shared_ptr& get() const noexcept { return _ptr; } + + private: + const std::shared_ptr _cache = getInstance(); + std::shared_ptr _ptr; + }; + +private: + std::unordered_map m_mapping; + std::mutex m_mutex; + + CppWrapperCache() {} +}; + +} // namespace djinni diff --git a/support-lib/cx/CxWrapperCache.h b/support-lib/cx/CxWrapperCache.h new file mode 100644 index 000000000..c0a77cab9 --- /dev/null +++ b/support-lib/cx/CxWrapperCache.h @@ -0,0 +1,84 @@ +// +// Copyright 2015 Slack Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// This header can only be imported to C++/Cx source code! + +#include +#include +#include + +namespace djinni { + +template +class CxWrapperCache { +public: + static const std::shared_ptr & getInstance() { + static const std::shared_ptr instance(new CxWrapperCache); + // Return by const-ref. This is safe to call any time except during static destruction. + // Returning by reference lets us avoid touching the refcount unless needed. + return instance; + } + + std::shared_ptr get(Platform::Object^ cxRef) { + std::unique_lock lock(m_mutex); + std::shared_ptr ret; + auto it = m_mapping.find(reinterpret_cast(cxRef)); + if (it != m_mapping.end()) { + ret = std::static_pointer_cast(it->second.lock()); + if (ret == nullptr) { + ret = new_wrapper(cxRef); + } + } else { + ret = new_wrapper(cxRef); + } + return ret; + } + + void remove(Platform::Object^ cxRef) { + std::unique_lock lock(m_mutex); + m_mapping.erase(reinterpret_cast(cxRef)); + } + + class Handle { + public: + Handle(Platform::Object^ cx) : _cx(cx) { }; + ~Handle() { + if (_cx) { + _cache->remove(_cx); + } + } + Platform::Object^ get() const noexcept { return _cx; } + + private: + const std::shared_ptr _cache = getInstance(); + const Platform::Object^ _cx; + }; + + +private: + std::unordered_map> m_mapping; + std::mutex m_mutex; + + std::shared_ptr new_wrapper(Platform::Object^ cxRef) { + std::shared_ptr ret = std::make_shared(reinterpret_cast(cxRef)); + std::weak_ptr ptr(std::static_pointer_cast(ret)); + m_mapping[reinterpret_cast(cxRef)] = ptr; + return ret; + } + +}; + +} // namespace djinni diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h new file mode 100755 index 000000000..5aad6b3d5 --- /dev/null +++ b/support-lib/cx/Marshal.h @@ -0,0 +1,277 @@ +// +// Marshal.h +// Djinni +// +// Created by D.E. Goodman-Wilson on 07.08.15. +// Copyright (c) 2015 Slack Technologies, Inc. All rights reserved. +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace djinni { + + struct Bool { + using CppType = bool; + using CxType = bool; + + static CppType toCpp(CxType x) { return x ? true : false; } + static CxType fromCpp(CppType x) { return x ? true : false; } + + struct Boxed { + using CxType = Platform::Object^; + static CppType toCpp(CxType x) { assert(x); return Bool::toCpp((bool)x); } + static CxType fromCpp(CppType x) { CxType cx = Bool::fromCpp(x); return cx; } + }; + }; + + template //, class CXT> + struct Primitive { + using CppType = T; + using CxType = T; + + static CppType toCpp(CxType x) { return x; } + static CxType fromCpp(CppType x) { return x; } + + struct Boxed { + using CxType = Platform::Object^; + static CppType toCpp(CxType x) { assert(x); return static_cast(Self::unbox(x)); } + static CxType fromCpp(CppType x) { return Self::box(x); } + }; + }; + + //stupid C++/Cx doesn't have int8_t; we'll pass it up as a uint8_t instead, and pray. + class I8 : public Primitive { + friend Primitive; + static int8_t unbox(Boxed::CxType x) { return safe_cast(x); } + static Boxed::CxType box(CppType x) { Platform::Object^ cx = (uint8_t)x; return cx; } + }; + + class I16 : public Primitive { + friend Primitive; + static uint16_t unbox(Boxed::CxType x) { return safe_cast(x); } + static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } + }; + + class I32 : public Primitive { + friend Primitive; + static int32_t unbox(Boxed::CxType x) { return safe_cast(x); } + static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } + }; + + class I64 : public Primitive { + friend Primitive; + static int64_t unbox(Boxed::CxType x) { return safe_cast(x); } + static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } + }; + + class F32 : public Primitive { + friend Primitive; + static float unbox(Boxed::CxType x) { return safe_cast(x); } + static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } + }; + + class F64 : public Primitive { + friend Primitive; + static double unbox(Boxed::CxType x) { return safe_cast(x); } + static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } + }; + + // + // template + // struct Enum { + // using CppType = CppEnum; + // using CxType = CxEnum; + // + // static CppType toCpp(CxType e) noexcept { return static_cast(e); } + // static CxType fromCpp(CppType e) noexcept { return static_cast(e); } + // + // struct Boxed { + // using CxType = NSNumber*; + // static CppType toCpp(CxType x) noexcept { return Enum::toCpp(static_cast([x integerValue])); } + // static CxType fromCpp(CppType x) noexcept { return [NSNumber numberWithInteger:static_cast(Enum::fromCpp(x))]; } + // }; + // }; + // + struct String { + using CppType = std::string; + using CxType = Platform::String; + + using Boxed = String; + + static CppType toCpp(CxType^ string) { + assert(string); + std::wstring wstring{ string->Data() }; + std::wstring_convert, wchar_t> converter; + return converter.to_bytes(wstring); + } + + static CxType^ fromCpp(const CppType& string) { + //TODO this doesn't seem to work. assert(string.size() <= std::numeric_limits::max()); + std::wstring_convert> converter; + + return ref new Platform::String(converter.from_bytes(string).c_str()); + } + }; + // + // struct Date { + // using CppType = std::chrono::system_clock::time_point; + // using CxType = NSDate*; + // + // using Boxed = Date; + // + // static CppType toCpp(CxType date) { + // using namespace std::chrono; + // static const auto POSIX_EPOCH = system_clock::from_time_t(0); + // auto timeIntervalSince1970 = duration([date timeIntervalSince1970]); + // return POSIX_EPOCH + duration_cast(timeIntervalSince1970); + // } + // + // static CxType fromCpp(const CppType& date) { + // using namespace std::chrono; + // static const auto POSIX_EPOCH = system_clock::from_time_t(0); + // return [NSDate dateWithTimeIntervalSince1970:duration_cast>(date - POSIX_EPOCH).count()]; + // + // } + // }; + // + // struct Binary { + // using CppType = std::vector; + // using CxType = NSData*; + // + // using Boxed = Binary; + // + // static CppType toCpp(CxType data) { + // assert(data); + // auto bytes = reinterpret_cast(data.bytes); + // return data.length > 0 ? CppType{bytes, bytes + data.length} : CppType{}; + // } + // + // static CxType fromCpp(const CppType& bytes) { + // assert(bytes.size() <= std::numeric_limits::max()); + // // Using the pointer from .data() on an empty vector is UB + // return bytes.empty() ? [NSData data] : [NSData dataWithBytes:bytes.data() + // length:static_cast(bytes.size())]; + // } + // }; + // + // template class OptionalType, class T> + // class Optional { + // public: + // using CppType = OptionalType; + // using CxType = typename T::Boxed::CxType; + // + // using Boxed = Optional; + // + // static CppType toCpp(CxType obj) { + // return obj ? CppType(T::Boxed::toCpp(obj)) : CppType(); + // } + // + // static CxType fromCpp(const CppType& opt) { + // return opt ? T::Boxed::fromCpp(*opt) : nil; + // } + // }; + // + // template + // class List { + // using ECppType = typename T::CppType; + // using ECxType = typename T::Boxed::CxType; + // + // public: + // using CppType = std::vector; + // using CxType = NSArray*; + // + // using Boxed = List; + // + // static CppType toCpp(CxType array) { + // assert(array); + // auto v = CppType(); + // v.reserve(array.count); + // for(ECxType value in array) { + // v.push_back(T::Boxed::toCpp(value)); + // } + // return v; + // } + // + // static CxType fromCpp(const CppType& v) { + // assert(v.size() <= std::numeric_limits::max()); + // auto array = [NSMutableArray arrayWithCapacity:static_cast(v.size())]; + // for(const auto& value : v) { + // [array addObject:T::Boxed::fromCpp(value)]; + // } + // return array; + // } + // }; + // + // template + // class Set { + // using ECppType = typename T::CppType; + // using ECxType = typename T::Boxed::CxType; + // + // public: + // using CppType = std::unordered_set; + // using CxType = NSSet*; + // + // using Boxed = Set; + // + // static CppType toCpp(CxType set) { + // assert(set); + // auto s = CppType(); + // for(ECxType value in set) { + // s.insert(T::Boxed::toCpp(value)); + // } + // return s; + // } + // + // static CxType fromCpp(const CppType& s) { + // assert(s.size() <= std::numeric_limits::max()); + // auto set = [NSMutableSet setWithCapacity:static_cast(s.size())]; + // for(const auto& value : s) { + // [set addObject:T::Boxed::fromCpp(value)]; + // } + // return set; + // } + // }; + // + // template + // class Map { + // using CppKeyType = typename Key::CppType; + // using CppValueType = typename Value::CppType; + // using CxKeyType = typename Key::Boxed::CxType; + // using CxValueType = typename Value::Boxed::CxType; + // + // public: + // using CppType = std::unordered_map; + // using CxType = NSDictionary*; + // + // using Boxed = Map; + // + // static CppType toCpp(CxType map) { + // assert(map); + // __block auto m = CppType(); + // m.reserve(map.count); + // [map enumerateKeysAndObjectsUsingBlock:^(CxKeyType key, CxValueType obj, BOOL *) { + // m.emplace(Key::Boxed::toCpp(key), Value::Boxed::toCpp(obj)); + // }]; + // return m; + // } + // + // static CxType fromCpp(const CppType& m) { + // assert(m.size() <= std::numeric_limits::max()); + // auto map = [NSMutableDictionary dictionaryWithCapacity:static_cast(m.size())]; + // for(const auto& kvp : m) { + // [map setObject:Value::Boxed::fromCpp(kvp.second) forKey:Key::Boxed::fromCpp(kvp.first)]; + // } + // return map; + // } + // }; + // +} // namespace djinni From eeba8f7d69ae7fde2cae40f99dc143c64d435386 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Wed, 8 Jul 2015 16:46:57 -0700 Subject: [PATCH 05/52] Much progress. Not there yet. --- support-lib/cx/CppWrapperCache.h | 20 +++++------ support-lib/cx/Marshal.h | 57 +++++++++++++++----------------- 2 files changed, 37 insertions(+), 40 deletions(-) mode change 100644 => 100755 support-lib/cx/CppWrapperCache.h diff --git a/support-lib/cx/CppWrapperCache.h b/support-lib/cx/CppWrapperCache.h old mode 100644 new mode 100755 index 24807c913..f38edf341 --- a/support-lib/cx/CppWrapperCache.h +++ b/support-lib/cx/CppWrapperCache.h @@ -14,7 +14,7 @@ // limitations under the License. // -// This header can only be imported to Objective-C++ source code! +// This header can only be imported to C++/Cx source code! #include #include @@ -33,20 +33,20 @@ class CppWrapperCache { } template - id get(const std::shared_ptr & cppRef, const AllocFunc & alloc) { + Platform::Object^ get(const std::shared_ptr & cppRef, const AllocFunc & alloc) { std::unique_lock lock(m_mutex); T* ptr = cppRef.get(); auto got = m_mapping.find(ptr); - id ret; + Platform::Object^ ret; if (got != m_mapping.end()) { - ret = got->second; - if (ret == nil) { + ret = reinterpret_cast(got->second); + if (ret == nullptr) { ret = alloc(cppRef); - m_mapping[ptr] = ret; + m_mapping[ptr] = Platform::WeakReference(ret); } } else { ret = alloc(cppRef); - m_mapping[ptr] = ret; + m_mapping[ptr] = Platform::WeakReference(ret); } return ret; } @@ -54,7 +54,7 @@ class CppWrapperCache { void remove(const std::shared_ptr & cppRef) { std::unique_lock lock(m_mutex); T* ptr = cppRef.get(); - if (m_mapping[ptr] == nil) { + if (m_mapping[ptr] == nullptr) { m_mapping.erase(ptr); } } @@ -68,7 +68,7 @@ class CppWrapperCache { } } void assign(const std::shared_ptr& ptr) { _ptr = ptr; } - const std::shared_ptr& get() const noexcept { return _ptr; } + const std::shared_ptr& get() const { return _ptr; } private: const std::shared_ptr _cache = getInstance(); @@ -76,7 +76,7 @@ class CppWrapperCache { }; private: - std::unordered_map m_mapping; + std::unordered_map m_mapping; std::mutex m_mutex; CppWrapperCache() {} diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index 5aad6b3d5..6c81fc8c6 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -17,6 +17,8 @@ #include #include +#include + namespace djinni { struct Bool { @@ -180,36 +182,31 @@ namespace djinni { // } // }; // - // template - // class List { - // using ECppType = typename T::CppType; - // using ECxType = typename T::Boxed::CxType; - // - // public: - // using CppType = std::vector; - // using CxType = NSArray*; - // - // using Boxed = List; - // - // static CppType toCpp(CxType array) { - // assert(array); - // auto v = CppType(); - // v.reserve(array.count); - // for(ECxType value in array) { - // v.push_back(T::Boxed::toCpp(value)); - // } - // return v; - // } - // - // static CxType fromCpp(const CppType& v) { - // assert(v.size() <= std::numeric_limits::max()); - // auto array = [NSMutableArray arrayWithCapacity:static_cast(v.size())]; - // for(const auto& value : v) { - // [array addObject:T::Boxed::fromCpp(value)]; - // } - // return array; - // } - // }; + template + class List { + using ECppType = typename T::CppType; + using ECxType = typename T::Boxed::CxType; + + public: + using CppType = std::vector; + using CxType = Windows::Foundation::Collections::IVector; + + using Boxed = List; + + static CppType toCpp(CxType^ v) { + assert(v); + std::vector nv; + for(int val : v) + { + nv.push_back(val); + } + return nv; + } + + static CxType^ fromCpp(const CppType& v) { + return ref new Platform::Collections::Vector(std::move(v)); + } + }; // // template // class Set { From 00d4f0515ea91d4a4b25170191201bc0c26ab018 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Thu, 9 Jul 2015 09:41:43 -0700 Subject: [PATCH 06/52] Proper handling of vectors. --- support-lib/cx/Marshal.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index 6c81fc8c6..faf33f1ee 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -195,17 +195,23 @@ namespace djinni { static CppType toCpp(CxType^ v) { assert(v); - std::vector nv; - for(int val : v) + CppType nv; + for(ECxType val : v) { - nv.push_back(val); + nv.push_back(T::Boxed::toCpp(val)); } return nv; } static CxType^ fromCpp(const CppType& v) { - return ref new Platform::Collections::Vector(std::move(v)); + Platform::Collections::Vector^ nv; + for (ECppType val : v) + { + nv->Append(T::Boxed::fromCpp(val)); + } + return nv; } + //We ought to specialize this for types C++/Cx knows how to convert for us. }; // // template From fd1dabba000fbd593182e58969c9c3f3a7a0b4e4 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 10 Jul 2015 17:03:19 -0700 Subject: [PATCH 07/52] Maybe this works? --- support-lib/cx/CppWrapperCache.h | 3 ++- support-lib/support_lib.gyp | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/support-lib/cx/CppWrapperCache.h b/support-lib/cx/CppWrapperCache.h index f38edf341..e16388242 100755 --- a/support-lib/cx/CppWrapperCache.h +++ b/support-lib/cx/CppWrapperCache.h @@ -15,6 +15,7 @@ // // This header can only be imported to C++/Cx source code! +#pragma once #include #include @@ -39,7 +40,7 @@ class CppWrapperCache { auto got = m_mapping.find(ptr); Platform::Object^ ret; if (got != m_mapping.end()) { - ret = reinterpret_cast(got->second); + ret = got->second.Resolve(); if (ret == nullptr) { ret = alloc(cppRef); m_mapping[ptr] = Platform::WeakReference(ret); diff --git a/support-lib/support_lib.gyp b/support-lib/support_lib.gyp index 7ac16a19b..8a150470c 100644 --- a/support-lib/support_lib.gyp +++ b/support-lib/support_lib.gyp @@ -43,5 +43,19 @@ ], }, }, + { + "target_name": "djinni_cx", + "type": "static_library", + "sources": [ + ], + "include_dirs": [ + "cx", + ], + "direct_dependent_settings": { + "include_dirs": [ + "cx", + ], + }, + }, ], } From ca4dd6db5bbe11d03b571a96a6b3a12a5d26197d Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 10 Jul 2015 17:24:44 -0700 Subject: [PATCH 08/52] Have to allocate that sucker. --- support-lib/cx/Marshal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index faf33f1ee..ff098553a 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -204,7 +204,7 @@ namespace djinni { } static CxType^ fromCpp(const CppType& v) { - Platform::Collections::Vector^ nv; + Platform::Collections::Vector^ nv = ref new Platform::Collections::Vector; for (ECppType val : v) { nv->Append(T::Boxed::fromCpp(val)); From b0e531d46b4f0e03293401191619a20f9ff84fc8 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Tue, 14 Jul 2015 07:53:11 -0700 Subject: [PATCH 09/52] Casts and constness and costs and losing my sanity need to take a step back. --- support-lib/cx/CxWrapperCache.h | 6 +++--- support-lib/cx/Marshal.h | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) mode change 100644 => 100755 support-lib/cx/CxWrapperCache.h diff --git a/support-lib/cx/CxWrapperCache.h b/support-lib/cx/CxWrapperCache.h old mode 100644 new mode 100755 index c0a77cab9..1e6f354a0 --- a/support-lib/cx/CxWrapperCache.h +++ b/support-lib/cx/CxWrapperCache.h @@ -47,9 +47,9 @@ class CxWrapperCache { return ret; } - void remove(Platform::Object^ cxRef) { + void remove(const Platform::Object^ cxRef) { std::unique_lock lock(m_mutex); - m_mapping.erase(reinterpret_cast(cxRef)); + m_mapping.erase(reinterpret_cast(cxRef)); } class Handle { @@ -60,7 +60,7 @@ class CxWrapperCache { _cache->remove(_cx); } } - Platform::Object^ get() const noexcept { return _cx; } + const Platform::Object^ get() const { return _cx; } private: const std::shared_ptr _cache = getInstance(); diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index ff098553a..a50384c78 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -119,7 +119,6 @@ namespace djinni { static CxType^ fromCpp(const CppType& string) { //TODO this doesn't seem to work. assert(string.size() <= std::numeric_limits::max()); std::wstring_convert> converter; - return ref new Platform::String(converter.from_bytes(string).c_str()); } }; From bb080382bb0bf62a8cc401b857e37bd554e1942b Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Tue, 14 Jul 2015 15:07:08 -0700 Subject: [PATCH 10/52] A CxWrapperCache that works. Huh. --- support-lib/cx/CxWrapperCache.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/support-lib/cx/CxWrapperCache.h b/support-lib/cx/CxWrapperCache.h index 1e6f354a0..49dfe4bd0 100755 --- a/support-lib/cx/CxWrapperCache.h +++ b/support-lib/cx/CxWrapperCache.h @@ -35,7 +35,7 @@ class CxWrapperCache { std::shared_ptr get(Platform::Object^ cxRef) { std::unique_lock lock(m_mutex); std::shared_ptr ret; - auto it = m_mapping.find(reinterpret_cast(cxRef)); + auto it = m_mapping.find(reinterpret_cast(cxRef)); if (it != m_mapping.end()) { ret = std::static_pointer_cast(it->second.lock()); if (ret == nullptr) { @@ -47,9 +47,9 @@ class CxWrapperCache { return ret; } - void remove(const Platform::Object^ cxRef) { + void remove(Platform::Object^ cxRef) { std::unique_lock lock(m_mutex); - m_mapping.erase(reinterpret_cast(cxRef)); + m_mapping.erase(reinterpret_cast(cxRef)); } class Handle { @@ -60,20 +60,20 @@ class CxWrapperCache { _cache->remove(_cx); } } - const Platform::Object^ get() const { return _cx; } + Platform::Object^ get() const { return _cx; } private: const std::shared_ptr _cache = getInstance(); - const Platform::Object^ _cx; + Platform::Object^ _cx; }; private: - std::unordered_map> m_mapping; + std::unordered_map> m_mapping; std::mutex m_mutex; - std::shared_ptr new_wrapper(Platform::Object^ cxRef) { - std::shared_ptr ret = std::make_shared(reinterpret_cast(cxRef)); + std::shared_ptr new_wrapper(Platform::Object^ cxRef) { + auto ret = std::shared_ptr(new T(cxRef)); std::weak_ptr ptr(std::static_pointer_cast(ret)); m_mapping[reinterpret_cast(cxRef)] = ptr; return ret; From e18f2d330abce10ede575fe83b9e219c3636146c Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Thu, 16 Jul 2015 07:43:30 -0700 Subject: [PATCH 11/52] Some half-hearted work on enums and datetime types. Not in a position to test it anyway. --- support-lib/cx/Marshal.h | 90 +++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index a50384c78..8900416d6 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -87,22 +87,22 @@ namespace djinni { static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } }; - // - // template - // struct Enum { - // using CppType = CppEnum; - // using CxType = CxEnum; - // - // static CppType toCpp(CxType e) noexcept { return static_cast(e); } - // static CxType fromCpp(CppType e) noexcept { return static_cast(e); } - // - // struct Boxed { - // using CxType = NSNumber*; - // static CppType toCpp(CxType x) noexcept { return Enum::toCpp(static_cast([x integerValue])); } - // static CxType fromCpp(CppType x) noexcept { return [NSNumber numberWithInteger:static_cast(Enum::fromCpp(x))]; } - // }; - // }; - // + +// template +// struct Enum { +// using CppType = CppEnum; +// using CxType = CxEnum; +// +// static CppType toCpp(CxType e) { return static_cast(e); } +// static CxType fromCpp(CppType e) { return static_cast(e); } +// +// struct Boxed { +// //yeah, I just don't know about these two lines. So...weird? How _do_ you box an enum? +// static CppType toCpp(CxType x) { return Enum::toCpp(static_cast(static_cast(x))); } +// static CxType fromCpp(CppType x) { return Enum::toCx(static_cast(static_cast(x))); } +// }; +// }; + struct String { using CppType = std::string; using CxType = Platform::String; @@ -122,28 +122,42 @@ namespace djinni { return ref new Platform::String(converter.from_bytes(string).c_str()); } }; - // - // struct Date { - // using CppType = std::chrono::system_clock::time_point; - // using CxType = NSDate*; - // - // using Boxed = Date; - // - // static CppType toCpp(CxType date) { - // using namespace std::chrono; - // static const auto POSIX_EPOCH = system_clock::from_time_t(0); - // auto timeIntervalSince1970 = duration([date timeIntervalSince1970]); - // return POSIX_EPOCH + duration_cast(timeIntervalSince1970); - // } - // - // static CxType fromCpp(const CppType& date) { - // using namespace std::chrono; - // static const auto POSIX_EPOCH = system_clock::from_time_t(0); - // return [NSDate dateWithTimeIntervalSince1970:duration_cast>(date - POSIX_EPOCH).count()]; - // - // } - // }; - // + +// struct Date { +// using CppType = std::chrono::system_clock::time_point; +// using CxType = Windows::Foundation::DateTime; +// +// using Boxed = Date; +// +// static CppType toCpp(CxType date) { +// // date is "A 64-bit signed integer that represents a point in time as the number of 100-nanosecond +// // intervals prior to or after midnight on January 1, 1601 (according to the Gregorian Calendar)." +// // So helpful +// using namespace std::chrono; +// static const auto POSIX_EPOCH = system_clock::from_time_t(0); +// +// // rather than calculate by hand the difference in time offsets between POSIX epoch and 1601, let's use a helper +// Windows::Globalization::Calendar^ calendar = ref new Windows::Globalization::Calendar(); +// calendar.year = 1970; +// calendar.month = 1; +// calendar.day = 1; +// calendar.hour = 0; +// calendar.minute = 0; +// calendar.second = 0; +// calendar.nanosecond = 0; +// uint64_t epochDate = (date.UniversalTime - calendar.GetDateTime().UniversalTime) / 10000; +// auto timeIntervalSince1970 = duration(epochDate); +// return POSIX_EPOCH + duration_cast(timeIntervalSince1970); +// } +// +// static CxType fromCpp(const CppType& date) { +// using namespace std::chrono; +// static const auto POSIX_EPOCH = system_clock::from_time_t(0); +// return [NSDate dateWithTimeIntervalSince1970:duration_cast>(date - POSIX_EPOCH).count()]; +// +// } +// }; + // struct Binary { // using CppType = std::vector; // using CxType = NSData*; From efdfc2c6f0feac2dff9fe13ac6aa6196827f4d9e Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Thu, 16 Jul 2015 11:55:47 -0700 Subject: [PATCH 12/52] Progress in doing Cx translation. This almsot certainly won't compile. --- src/source/{CxGenerator.scala => CxCppGenerator.scala} | 0 src/source/{CxMarshal.scala => CxCppMarshal.scala} | 0 src/source/CxWinRTGenerator.scala | 0 src/source/CxWinRtMarshal.scala | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/source/{CxGenerator.scala => CxCppGenerator.scala} (100%) rename src/source/{CxMarshal.scala => CxCppMarshal.scala} (100%) create mode 100644 src/source/CxWinRTGenerator.scala create mode 100644 src/source/CxWinRtMarshal.scala diff --git a/src/source/CxGenerator.scala b/src/source/CxCppGenerator.scala similarity index 100% rename from src/source/CxGenerator.scala rename to src/source/CxCppGenerator.scala diff --git a/src/source/CxMarshal.scala b/src/source/CxCppMarshal.scala similarity index 100% rename from src/source/CxMarshal.scala rename to src/source/CxCppMarshal.scala diff --git a/src/source/CxWinRTGenerator.scala b/src/source/CxWinRTGenerator.scala new file mode 100644 index 000000000..e69de29bb diff --git a/src/source/CxWinRtMarshal.scala b/src/source/CxWinRtMarshal.scala new file mode 100644 index 000000000..e69de29bb From 9b9ff5b929dfacce4d4d9aa169037977faa45d01 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Thu, 16 Jul 2015 16:46:53 -0700 Subject: [PATCH 13/52] Oh, so much closer. Now we are generating at least some of the code that needs to be generated! The code is _wrong_ but it exists! --- src/source/CxCppGenerator.scala | 308 ++++++++++++++---------------- src/source/CxCppMarshal.scala | 155 +++++++-------- src/source/CxGenerator.scala | 276 ++++++++++++++++++++++++++ src/source/CxMarshal.scala | 101 ++++++++++ src/source/CxWinRTGenerator.scala | 0 src/source/CxWinRtMarshal.scala | 0 src/source/Main.scala | 87 ++++++--- src/source/ast.scala | 2 +- src/source/generator.scala | 21 +- 9 files changed, 672 insertions(+), 278 deletions(-) create mode 100644 src/source/CxGenerator.scala create mode 100644 src/source/CxMarshal.scala delete mode 100644 src/source/CxWinRTGenerator.scala delete mode 100644 src/source/CxWinRtMarshal.scala diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index 3d3c901d7..6d93697a5 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -1,18 +1,18 @@ /** - * Copyright 2014 Dropbox, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + * Copyright 2014 Dropbox, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package djinni @@ -24,74 +24,111 @@ import djinni.writer.IndentWriter import scala.collection.mutable -class CxGenerator(spec: Spec) extends Generator(spec) { +class CxCppGenerator(spec: Spec) extends Generator(spec) { - val marshal = new CxMarshal(spec) + val cxcppMarshal = new CxCppMarshal(spec) + val cxMarshal = new CxMarshal(spec) - val writeCxFile = writeCppFileGeneric(spec.cxOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle, spec.cxIncludePrefix) _ - def writeHppFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = - writeHppFileGeneric(spec.cxHeaderOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle)(name, origin, includes, fwds, f, f2) + class CxRefs() { + var body = mutable.TreeSet[String]() + var privHeader = mutable.TreeSet[String]() - class CxRefs(name: String) { - var hpp = mutable.TreeSet[String]() - var hppFwds = mutable.TreeSet[String]() - var cx = mutable.TreeSet[String]() + def find(ty: TypeRef) { find(ty.resolved) } + def find(tm: MExpr) { + tm.args.foreach(find) + find(tm.base) + } + def find(m: Meta) = for(r <- cxcppMarshal.references(m)) r match { + case ImportRef(arg) => body.add("#import " + arg) + case _ => + } + } + + val writeCxCppFile = writeCppFileGeneric(spec.cxcppOutFolder.get, spec.cxcppNamespace, spec.cppFileIdentStyle, spec.cxcppIncludePrefix) _ + def writeHxFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = + writeHppFileGeneric(spec.cxcppHeaderOutFolder.get, spec.cxcppNamespace, spec.cppFileIdentStyle)(name, origin, includes, fwds, f, f2) + + class CxCppRefs(name: String) { + var hx = mutable.TreeSet[String]() + var hxFwds = mutable.TreeSet[String]() + var cxcpp = mutable.TreeSet[String]() def find(ty: TypeRef) { find(ty.resolved) } def find(tm: MExpr) { tm.args.foreach(find) find(tm.base) } - def find(m: Meta) = for(r <- marshal.references(m, name)) r match { - case ImportRef(arg) => hpp.add("#include " + arg) - case DeclRef(decl, Some(spec.cxNamespace)) => hppFwds.add(decl) + def find(m: Meta) = for(r <- cxcppMarshal.references(m)) r match { + case ImportRef(arg) => hx.add("#include " + arg) + case DeclRef(decl, Some(spec.cxcppNamespace)) => hxFwds.add(decl) case DeclRef(_, _) => } } override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { - val refs = new CxRefs(ident.name) - val self = marshal.typename(ident, e) + val refs = new CxCppRefs(ident.name) + val self = cxcppMarshal.typename(ident, e) + + if (spec.cppEnumHashWorkaround) { + refs.hx.add("#include ") // needed for std::hash + } - writeHppFile(ident, origin, refs.hpp, refs.hppFwds, w => { + writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { w.w(s"enum class $self : int").bracedSemi { for (o <- e.options) { writeDoc(w, o.doc) - w.wl(idCx.enum(o.ident.name) + ",") + w.wl(idCpp.enum(o.ident.name) + ",") } } - }) + }, + w => { + // std::hash specialization has to go *outside* of the wrapNs + if (spec.cppEnumHashWorkaround) { + val fqSelf = cxcppMarshal.fqTypename(ident, e) + w.wl + wrapNamespace(w, "std", + (w: IndentWriter) => { + w.wl("template <>") + w.w(s"struct hash<$fqSelf>").bracedSemi { + w.w(s"size_t operator()($fqSelf type) const").braced { + w.wl("return std::hash()(static_cast(type));") + } + } + } + ) + } + }) } - def generateHppConstants(w: IndentWriter, consts: Seq[Const]) = { + def generateHxConstants(w: IndentWriter, consts: Seq[Const]) = { for (c <- consts) { w.wl writeDoc(w, c.doc) - w.wl(s"static ${marshal.fieldType(c.ty)} const ${idCx.const(c.ident)};") + w.wl(s"static ${cxcppMarshal.fieldType(c.ty)} const ${idCpp.const(c.ident)};") } } - def generateCxConstants(w: IndentWriter, consts: Seq[Const], selfName: String) = { - def writeCxConst(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match { + def generateCxCppConstants(w: IndentWriter, consts: Seq[Const], selfName: String) = { + def writeCxCppConst(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match { case l: Long => w.w(l.toString) - case d: Double if marshal.fieldType(ty) == "float" => w.w(d.toString + "f") + case d: Double if cxcppMarshal.fieldType(ty) == "float" => w.w(d.toString + "f") case d: Double => w.w(d.toString) case b: Boolean => w.w(if (b) "true" else "false") case s: String => w.w(s) - case e: EnumValue => w.w(marshal.typename(ty) + "::" + idCx.enum(e.ty.name + "_" + e.name)) - case v: ConstRef => w.w(selfName + "::" + idCx.const(v)) + case e: EnumValue => w.w(cxcppMarshal.typename(ty) + "::" + idCpp.enum(e.ty.name + "_" + e.name)) + case v: ConstRef => w.w(selfName + "::" + idCpp.const(v)) case z: Map[_, _] => { // Value is record - val recordMdef = ty.resolved.base.asInstanceOf[MDef] + val recordMdef = ty.resolved.base.asInstanceOf[MDef] val record = recordMdef.body.asInstanceOf[Record] val vMap = z.asInstanceOf[Map[String, Any]] - w.wl(marshal.typename(ty) + "(") + w.wl(cxcppMarshal.typename(ty) + "(") w.increase() // Use exact sequence val skipFirst = SkipFirst() for (f <- record.fields) { skipFirst {w.wl(",")} - writeCxConst(w, f.ty, vMap.apply(f.ident.name)) - w.w(" /* " + idCx.field(f.ident) + " */ ") + writeCxCppConst(w, f.ty, vMap.apply(f.ident.name)) + w.w(" /* " + idCpp.field(f.ident) + " */ ") } w.w(")") w.decrease() @@ -101,138 +138,77 @@ class CxGenerator(spec: Spec) extends Generator(spec) { val skipFirst = SkipFirst() for (c <- consts) { skipFirst{ w.wl } - w.w(s"${marshal.fieldType(c.ty)} const $selfName::${idCx.const(c.ident)} = ") - writeCxConst(w, c.ty, c.value) + w.w(s"${cxcppMarshal.fieldType(c.ty)} const $selfName::${idCpp.const(c.ident)} = ") + writeCxCppConst(w, c.ty, c.value) w.wl(";") } } override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) { - val refs = new CxRefs(ident.name) - r.fields.foreach(f => refs.find(f.ty)) - r.consts.foreach(c => refs.find(c.ty)) - refs.hpp.add("#include ") // Add for std::move + val refs = new CxRefs() + for (c <- r.consts) + refs.find(c.ty) + for (f <- r.fields) + refs.find(f.ty) - val self = marshal.typename(ident, r) - val (cxName, cxFinal) = if (r.ext.cx) (ident.name + "_base", "") else (ident.name, " final") - val actualSelf = marshal.typename(cxName, r) + val cxName = ident.name + (if (r.ext.cx) "_base" else "") + val cxSelf = cxMarshal.fqTypename(ident, r) + val cppSelf = cxcppMarshal.fqTypename(ident, r) - // Requiring the extended class - if (r.ext.cx) { - refs.hpp.add(s"class $self; // Requiring extended class") - refs.cx.add("#include "+q("../" + spec.cxFileIdentStyle(ident) + "." + spec.cxHeaderExt)) - } - - // C++ Header - def writeCxPrototype(w: IndentWriter) { - writeDoc(w, doc) - writeCxTypeParams(w, params) - w.w("struct " + actualSelf + cxFinal).bracedSemi { - generateHppConstants(w, r.consts) - // Field definitions. - for (f <- r.fields) { - writeDoc(w, f.doc) - w.wl(marshal.fieldType(f.ty) + " " + idCx.field(f.ident) + ";") - } + refs.privHeader.add("!#include " + q(spec.cxcppIncludeCxPrefix + (if(r.ext.cx) "../" else "") + cxcppMarshal.headerName(ident))) + refs.privHeader.add("!#include " + q(spec.cxcppIncludeCppPrefix + (if(r.ext.cpp) "../" else "") + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt)) - if (r.derivingTypes.contains(DerivingType.Eq)) { - w.wl - w.wl(s"friend bool operator==(const $actualSelf& lhs, const $actualSelf& rhs);") - w.wl(s"friend bool operator!=(const $actualSelf& lhs, const $actualSelf& rhs);") - } - if (r.derivingTypes.contains(DerivingType.Ord)) { - w.wl - w.wl(s"friend bool operator<(const $actualSelf& lhs, const $actualSelf& rhs);") - w.wl(s"friend bool operator>(const $actualSelf& lhs, const $actualSelf& rhs);") - } - if (r.derivingTypes.contains(DerivingType.Eq) && r.derivingTypes.contains(DerivingType.Ord)) { - w.wl - w.wl(s"friend bool operator<=(const $actualSelf& lhs, const $actualSelf& rhs);") - w.wl(s"friend bool operator>=(const $actualSelf& lhs, const $actualSelf& rhs);") - } + refs.body.add("#include ") + refs.body.add("!#import " + q(spec.cxcppIncludePrefix + cxcppMarshal.headerName(cxName))) - // Constructor. - if(r.fields.nonEmpty) { - w.wl - writeAlignedCall(w, actualSelf + "(", r.fields, ")", f => marshal.fieldType(f.ty) + " " + idCx.local(f.ident)) - w.wl - val init = (f: Field) => idCx.field(f.ident) + "(std::move(" + idCx.local(f.ident) + "))" - w.wl(": " + init(r.fields.head)) - r.fields.tail.map(f => ", " + init(f)).foreach(w.wl) - w.wl("{}") - } - - if (r.ext.cx) { - w.wl - w.wl(s"virtual ~$actualSelf() = default;") - w.wl - // Defining the dtor disables implicit copy/move operation generation, so re-enable them - // Make them protected to avoid slicing - w.wlOutdent("protected:") - w.wl(s"$actualSelf(const $actualSelf&) = default;") - w.wl(s"$actualSelf($actualSelf&&) = default;") - w.wl(s"$actualSelf& operator=(const $actualSelf&) = default;") - w.wl(s"$actualSelf& operator=($actualSelf&&) = default;") - } - } + def checkMutable(tm: MExpr): Boolean = tm.base match { + case MOptional => checkMutable(tm.args.head) + case MString => true + case MBinary => true + case _ => false } - writeHppFile(cxName, origin, refs.hpp, refs.hppFwds, writeCxPrototype) + val helperClass = cxcppMarshal.helperClass(ident) - if (r.consts.nonEmpty || r.derivingTypes.nonEmpty) { - writeCxFile(cxName, origin, refs.cx, w => { - generateCxConstants(w, r.consts, actualSelf) - - if (r.derivingTypes.contains(DerivingType.Eq)) { + writeCxCppFile(cxcppMarshal.headerName(cxName), origin, refs.privHeader, w => { + w.wl + wrapNamespace(w, spec.cxcppNamespace, w => { + w.wl(s"struct $helperClass") + w.bracedSemi { + w.wl(s"using CppType = $cppSelf;") + w.wl(s"using CxType = $cxSelf^;"); w.wl - w.w(s"bool operator==(const $actualSelf& lhs, const $actualSelf& rhs)").braced { - if(!r.fields.isEmpty) { - writeAlignedCall(w, "return ", r.fields, " &&", "", f => s"lhs.${idCx.field(f.ident)} == rhs.${idCx.field(f.ident)}") - w.wl(";") - } else { - w.wl("return true;") - } - } + w.wl(s"using Boxed = $helperClass;") w.wl - w.w(s"bool operator!=(const $actualSelf& lhs, const $actualSelf& rhs)").braced { - w.wl("return !(lhs == rhs);") - } + w.wl(s"static CppType toCpp(CxType cxc);") + w.wl(s"static CxType fromCpp(const CppType& cpp);") } - if (r.derivingTypes.contains(DerivingType.Ord)) { - w.wl - w.w(s"bool operator<(const $actualSelf& lhs, const $actualSelf& rhs)").braced { - for(f <- r.fields) { - w.w(s"if (lhs.${idCx.field(f.ident)} < rhs.${idCx.field(f.ident)})").braced { - w.wl("return true;") - } - w.w(s"if (rhs.${idCx.field(f.ident)} < lhs.${idCx.field(f.ident)})").braced { - w.wl("return false;") - } - } - w.wl("return false;") - } - w.wl - w.w(s"bool operator>(const $actualSelf& lhs, const $actualSelf& rhs)").braced { - w.wl("return rhs < lhs;") - } + }) + }) + + writeCxCppFile(cxName, origin, refs.body, w => { + wrapNamespace(w, spec.cxcppNamespace, w => { + w.wl(s"auto $helperClass::toCpp(CxType obj) -> CppType") + w.braced { + w.wl("assert(obj);") + if(r.fields.isEmpty) w.wl("(void)obj; // Suppress warnings in relase builds for empty records") + writeAlignedCall(w, "return {", r.fields, "}", f => cxcppMarshal.toCpp(f.ty, "cx->" + idCx.field(f.ident))) + w.wl(";") } - if (r.derivingTypes.contains(DerivingType.Eq) && r.derivingTypes.contains(DerivingType.Ord)) { - w.wl - w.w(s"bool operator<=(const $actualSelf& lhs, const $actualSelf& rhs)").braced { - w.wl("return !(rhs < lhs);") - } - w.wl - w.w(s"bool operator>=(const $actualSelf& lhs, const $actualSelf& rhs)").braced { - w.wl("return !(lhs < rhs);") - } + w.wl + w.wl(s"auto $helperClass::fromCpp(const CppType& cpp) -> CxType") + w.braced { + if(r.fields.isEmpty) w.wl("(void)cpp; // Suppress warnings in relase builds for empty records") + val first = if(r.fields.isEmpty) "" else IdentStyle.camelUpper("with_" + r.fields.head.ident.name) + writeAlignedCall(w, "return ref new CxType(", r.fields, ")", f=> cxcppMarshal.fromCpp(f.ty, "cpp." + idCpp.field(f.ident))) + w.wl(";") } }) - } - + }) } override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) { - val refs = new CxRefs(ident.name) + val refs = new CxCppRefs(ident.name) i.methods.map(m => { m.params.map(p => refs.find(p.ty)) m.ret.foreach(refs.find) @@ -241,45 +217,45 @@ class CxGenerator(spec: Spec) extends Generator(spec) { refs.find(c.ty) }) - val self = marshal.typename(ident, i) + val self = cxcppMarshal.typename(ident, i) - writeHppFile(ident, origin, refs.hpp, refs.hppFwds, w => { + writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { writeDoc(w, doc) - writeCxTypeParams(w, typeParams) + writeCxCppTypeParams(w, typeParams) w.w(s"class $self").bracedSemi { w.wlOutdent("public:") // Destructor w.wl(s"virtual ~$self() {}") // Constants - generateHppConstants(w, i.consts) + generateHxConstants(w, i.consts) // Methods for (m <- i.methods) { w.wl writeDoc(w, m.doc) - val ret = marshal.returnType(m.ret) - val params = m.params.map(p => marshal.paramType(p.ty) + " " + idCx.local(p.ident)) + val ret = cxcppMarshal.returnType(m.ret) + val params = m.params.map(p => cxcppMarshal.paramType(p.ty) + " " + idCpp.local(p.ident)) if (m.static) { - w.wl(s"static $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")};") + w.wl(s"static $ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")};") } else { val constFlag = if (m.const) " const" else "" - w.wl(s"virtual $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag = 0;") + w.wl(s"virtual $ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag = 0;") } } } }) - // Cx only generated in need of Constants + // CxCpp only generated in need of Constants if (i.consts.nonEmpty) { - writeCxFile(ident, origin, refs.cx, w => { - generateCxConstants(w, i.consts, self) + writeCxCppFile(ident, origin, refs.cxcpp, w => { + generateCxCppConstants(w, i.consts, self) }) } } - def writeCxTypeParams(w: IndentWriter, params: Seq[TypeParam]) { + def writeCxCppTypeParams(w: IndentWriter, params: Seq[TypeParam]) { if (params.isEmpty) return - w.wl("template " + params.map(p => "typename " + idCx.typeParam(p.ident)).mkString("<", ", ", ">")) + w.wl("template " + params.map(p => "typename " + idCpp.typeParam(p.ident)).mkString("<", ", ", ">")) } } diff --git a/src/source/CxCppMarshal.scala b/src/source/CxCppMarshal.scala index affbcc7ea..75c54db44 100644 --- a/src/source/CxCppMarshal.scala +++ b/src/source/CxCppMarshal.scala @@ -4,101 +4,94 @@ import djinni.ast._ import djinni.generatorTools._ import djinni.meta._ -class CxMarshal(spec: Spec) extends Marshal(spec) { +class CxCppMarshal(spec: Spec) extends Marshal(spec) { + private val cppMarshal = new CppMarshal(spec) + private val cxMarshal = new CxMarshal(spec) - override def typename(tm: MExpr): String = toCxType(tm, None) - def typename(name: String, ty: TypeDef): String = ty match { - case e: Enum => idCx.enumType(name) - case i: Interface => idCx.ty(name) - case r: Record => idCx.ty(name) - } + override def typename(tm: MExpr): String = throw new AssertionError("not applicable") + def typename(name: String, ty: TypeDef): String = throw new AssertionError("not applicable") - override def fqTypename(tm: MExpr): String = toCxType(tm, Some(spec.cxNamespace)) - def fqTypename(name: String, ty: TypeDef): String = ty match { - case e: Enum => withNs(Some(spec.cxNamespace), idCx.enumType(name)) - case i: Interface => withNs(Some(spec.cxNamespace), idCx.ty(name)) - case r: Record => withNs(Some(spec.cxNamespace), idCx.ty(name)) - } + override def fqTypename(tm: MExpr): String = throw new AssertionError("not applicable") + def fqTypename(name: String, ty: TypeDef): String = throw new AssertionError("not applicable") - override def paramType(tm: MExpr): String = toCxParamType(tm) - override def fqParamType(tm: MExpr): String = toCxParamType(tm, Some(spec.cxNamespace)) + override def paramType(tm: MExpr): String = throw new AssertionError("not applicable") + override def fqParamType(tm: MExpr): String = throw new AssertionError("not applicable") - override def returnType(ret: Option[TypeRef]): String = ret.fold("void")(toCxType(_, None)) - override def fqReturnType(ret: Option[TypeRef]): String = ret.fold("void")(toCxType(_, Some(spec.cxNamespace))) + override def returnType(ret: Option[TypeRef]): String = throw new AssertionError("not applicable") + override def fqReturnType(ret: Option[TypeRef]): String = throw new AssertionError("not applicable") - override def fieldType(tm: MExpr): String = typename(tm) - override def fqFieldType(tm: MExpr): String = fqTypename(tm) + override def fieldType(tm: MExpr): String = throw new AssertionError("not applicable") + override def fqFieldType(tm: MExpr): String = throw new AssertionError("not applicable") - def references(m: Meta, exclude: String): Seq[SymbolReference] = m match { - case p: MPrimitive => p.idlName match { - case "i8" | "i16" | "i32" | "i64" => List(ImportRef("")) - case _ => List() - } - case MString => List(ImportRef("")) - case MDate => List(ImportRef("")) - case MBinary => List(ImportRef(""), ImportRef("")) - case MOptional => List() - case MList => List(ImportRef("")) - case MSet => List(ImportRef("")) - case MMap => List(ImportRef("")) + override def toCpp(tm: MExpr, expr: String): String = { + s"${helperClass(tm)}::toCpp($expr)" + } + override def fromCpp(tm: MExpr, expr: String): String = { + s"${helperClass(tm)}::fromCpp($expr)" + } + + def references(m: Meta): Seq[SymbolReference] = m match { + case o: MOpaque => + List(ImportRef(q(spec.cxBaseLibIncludePrefix + "Marshal.h"))) case d: MDef => d.defType match { - case DEnum | DRecord => - if (d.name != exclude) { - List(ImportRef(q(spec.cxIncludePrefix + spec.cxFileIdentStyle(d.name) + "." + spec.cxHeaderExt))) - } else { - List() - } + case DEnum => + List(ImportRef(q(spec.cxBaseLibIncludePrefix + "Marshal.h"))) case DInterface => - if (d.name != exclude) { - List(ImportRef(""), DeclRef(s"class ${typename(d.name, d.body)};", Some(spec.cxNamespace))) - } else { - List(ImportRef("")) - } + List(ImportRef(q(spec.cxcppIncludePrefix + headerName(d.name)))) + case DRecord => + val r = d.body.asInstanceOf[Record] + val cxName = d.name + (if (r.ext.cx) "_base" else "") + List(ImportRef(q(spec.cxcppIncludePrefix + headerName(cxName)))) } case p: MParam => List() - } - - private def toCxType(ty: TypeRef, namespace: Option[String] = None): String = toCxType(ty.resolved, namespace) - private def toCxType(tm: MExpr, namespace: Option[String]): String = { - def base(m: Meta): String = m match { - case p: MPrimitive => p.cName - case MString => "std::string" - case MDate => "std::chrono::system_clock::time_point" - case MBinary => "std::vector" - case MOptional => "" - case MList => "std::vector" - case MSet => "std::unordered_set" - case MMap => "std::unordered_map" - case d: MDef => - d.defType match { - case DEnum => withNs(namespace, idCx.enumType(d.name)) - case DRecord => withNs(namespace, idCx.ty(d.name)) - case DInterface => s"std::shared_ptr<${withNs(namespace, idCx.ty(d.name))}>" - } - case p: MParam => idCx.typeParam(p.name) - } - def expr(tm: MExpr): String = { - val args = if (tm.args.isEmpty) "" else tm.args.map(expr).mkString("<", ", ", ">") - base(tm.base) + args - } - expr(tm) } - // this can be used in c++ generation to know whether a const& should be applied to the parameter or not - private def toCxParamType(tm: MExpr, namespace: Option[String] = None): String = { - val cxType = toCxType(tm, namespace) - val refType = "const " + cxType + " &" - val valueType = cxType + def helperClass(name: String) = idCpp.ty(name) + private def helperClass(tm: MExpr): String = helperName(tm) + helperTemplates(tm) - def toType(expr: MExpr): String = expr.base match { - case p: MPrimitive => valueType - case d: MDef => d.defType match { - case DEnum => valueType - case _ => refType + def headerName(ident: String): String = idCx.ty(ident) + "_convert." + spec.cxHeaderExt + + private def helperName(tm: MExpr): String = tm.base match { + case d: MDef => d.defType match { + case DEnum => withNs(Some("djinni"), s"Enum<${cppMarshal.fqTypename(tm)}, ${cxMarshal.fqTypename(tm)}>") + case _ => withNs(Some(spec.cxcppNamespace), helperClass(d.name)) + } + case o => withNs(Some("djinni"), o match { + case p: MPrimitive => p.idlName match { + case "i8" => "I8" + case "i16" => "I16" + case "i32" => "I32" + case "i64" => "I64" + case "f32" => "F32" + case "f64" => "F64" + case "bool" => "Bool" } - case MOptional => toType(expr.args.head) - case _ => refType + case MOptional => "Optional" + case MBinary => "Binary" + case MDate => "Date" + case MString => "String" + case MList => "List" + case MSet => "Set" + case MMap => "Map" + case d: MDef => throw new AssertionError("unreachable") + case p: MParam => throw new AssertionError("not applicable") + }) + } + + private def helperTemplates(tm: MExpr): String = { + def f() = if(tm.args.isEmpty) "" else tm.args.map(helperClass).mkString("<", ", ", ">") + tm.base match { + case MOptional => + assert(tm.args.size == 1) + val argHelperClass = helperClass(tm.args.head) + s"<${spec.cppOptionalTemplate}, $argHelperClass>" + case MList | MSet => + assert(tm.args.size == 1) + f + case MMap => + assert(tm.args.size == 2) + f + case _ => f } - toType(tm) } } diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala new file mode 100644 index 000000000..b640fd4fc --- /dev/null +++ b/src/source/CxGenerator.scala @@ -0,0 +1,276 @@ +/** + * Copyright 2014 Dropbox, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package djinni + +import djinni.ast.Record.DerivingType +import djinni.ast._ +import djinni.generatorTools._ +import djinni.meta._ +import djinni.writer.IndentWriter + +import scala.collection.mutable + +class CxGenerator(spec: Spec) extends Generator(spec) { + + val marshal = new CxMarshal(spec) + + val writeCxFile = writeCppFileGeneric(spec.cxOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle, spec.cxIncludePrefix) _ + def writeHxFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = + writeHppFileGeneric(spec.cxHeaderOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle)(name, origin, includes, fwds, f, f2) + + class CxRefs(name: String) { + var hx = mutable.TreeSet[String]() + var hxFwds = mutable.TreeSet[String]() + var cx = mutable.TreeSet[String]() + + def find(ty: TypeRef) { find(ty.resolved) } + def find(tm: MExpr) { + tm.args.foreach(find) + find(tm.base) + } + def find(m: Meta) = for(r <- marshal.references(m, name)) r match { + case ImportRef(arg) => hx.add("#include " + arg) + case DeclRef(decl, Some(spec.cxNamespace)) => hxFwds.add(decl) + case DeclRef(_, _) => + } + } + + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { + val refs = new CxRefs(ident.name) + val self = marshal.typename(ident, e) + + writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { + w.w(s"enum class $self : int").bracedSemi { + for (o <- e.options) { + writeDoc(w, o.doc) + w.wl(idCx.enum(o.ident.name) + ",") + } + } + }, + w => { + // std::hash specialization has to go *outside* of the wrapNs + if (spec.cppEnumHashWorkaround) { + val fqSelf = marshal.fqTypename(ident, e) + w.wl + wrapNamespace(w, "std", + (w: IndentWriter) => { + w.wl("template <>") + w.w(s"struct hash<$fqSelf>").bracedSemi { + w.w(s"size_t operator()($fqSelf type) const").braced { + w.wl("return std::hash()(static_cast(type));") + } + } + } + ) + } + }) + } + + def generateHxConstants(w: IndentWriter, consts: Seq[Const]) = { + for (c <- consts) { + w.wl + writeDoc(w, c.doc) + w.wl(s"static ${marshal.fieldType(c.ty)} const ${idCx.const(c.ident)};") + } + } + + def generateCxConstants(w: IndentWriter, consts: Seq[Const], selfName: String) = { + def writeCxConst(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match { + case l: Long => w.w(l.toString) + case d: Double if marshal.fieldType(ty) == "float" => w.w(d.toString + "f") + case d: Double => w.w(d.toString) + case b: Boolean => w.w(if (b) "true" else "false") + case s: String => w.w(s) + case e: EnumValue => w.w(marshal.typename(ty) + "::" + idCx.enum(e.ty.name + "_" + e.name)) + case v: ConstRef => w.w(selfName + "::" + idCx.const(v)) + case z: Map[_, _] => { // Value is record + val recordMdef = ty.resolved.base.asInstanceOf[MDef] + val record = recordMdef.body.asInstanceOf[Record] + val vMap = z.asInstanceOf[Map[String, Any]] + w.wl(marshal.typename(ty) + "(") + w.increase() + // Use exact sequence + val skipFirst = SkipFirst() + for (f <- record.fields) { + skipFirst {w.wl(",")} + writeCxConst(w, f.ty, vMap.apply(f.ident.name)) + w.w(" /* " + idCx.field(f.ident) + " */ ") + } + w.w(")") + w.decrease() + } + } + + val skipFirst = SkipFirst() + for (c <- consts) { + skipFirst{ w.wl } + w.w(s"${marshal.fieldType(c.ty)} const $selfName::${idCx.const(c.ident)} = ") + writeCxConst(w, c.ty, c.value) + w.wl(";") + } + } + + override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) { + val refs = new CxRefs(ident.name) + r.fields.foreach(f => refs.find(f.ty)) + r.consts.foreach(c => refs.find(c.ty)) + + val self = marshal.typename(ident, r) + val (cxName, cxFinal) = if (r.ext.cx) (ident.name + "_base", "") else (ident.name, " final") + val actualSelf = marshal.typename(cxName, r) + + // Requiring the extended class + if (r.ext.cx) { + refs.hx.add(s"publc ref struct $self;") + refs.cx.add("#include " + q("../" + spec.cxFileIdentStyle(ident) + "." + spec.cxHeaderExt)) + } + + // C++ Header + def writeCxPrototype(w: IndentWriter) { + writeDoc(w, doc) + writeCxTypeParams(w, params) + w.w("public ref struct " + actualSelf + cxFinal).bracedSemi { + generateHxConstants(w, r.consts) + // Field definitions. + for (f <- r.fields) { + writeDoc(w, f.doc) + w.wl(marshal.fieldType(f.ty) + " " + idCx.field(f.ident) + ";") + } + + // Constructor. + if (r.fields.nonEmpty) { + w.wl + writeAlignedCall(w, actualSelf + "(", r.fields, ")", f => marshal.fieldType(f.ty) + " " + idCx.local(f.ident)) + w.wl + val init = (f: Field) => idCx.field(f.ident) + "(" + idCx.local(f.ident) + ")" + w.wl(": " + init(r.fields.head)) + r.fields.tail.map(f => ", " + init(f)).foreach(w.wl) + w.wl("{}") + } + + if (r.derivingTypes.contains(DerivingType.Eq)) { + w.wl + w.wl(s"bool Equals($actualSelf^ rhs);") + } + if (r.derivingTypes.contains(DerivingType.Ord)) { + w.wl + w.wl(s"int32 CompareTo($actualSelf^ rhs);") + } + + } + } + + writeHxFile(cxName, origin, refs.hx, refs.hxFwds, writeCxPrototype) + + if (r.consts.nonEmpty || r.derivingTypes.nonEmpty) { + writeCxFile(cxName, origin, refs.cx, w => { + generateCxConstants(w, r.consts, actualSelf) + + if (r.derivingTypes.contains(DerivingType.Eq)) { + w.wl + w.w(s"bool $actualSelf::Equals($actualSelf^ rhs)").braced { + if (!r.fields.isEmpty) { + writeAlignedCall(w, "return ", r.fields, " &&", "", f => s"this->${idCx.field(f.ident)} == rhs->${idCx.field(f.ident)}") + w.wl(";") + } else { + w.wl("return true;") + } + } + } + if (r.derivingTypes.contains(DerivingType.Ord)) { + w.wl + w.w(s"int32 $actualSelf::CompareTo($actualSelf^ rhs)").braced { + w.wl(s"if (rhs == nullptr) return 1;") + w.wl("int32 tempResult;") + for (f <- r.fields) { + f.ty.resolved.base match { + case MString => w.wl(s"tempResult = Platform::String::CompareOrdinal(this->${idCx.field(f.ident)}, rhs->${idCx.field(f.ident)});") + case t: MPrimitive => + w.wl(s"if (this->${idCx.field(f.ident)} < rhs->${idCx.field(f.ident)}) {").nested { + w.wl(s"tempResult = -1;") + } + w.wl(s"} else if (this->${idCx.field(f.ident)} > rhs->${idCx.field(f.ident)}) {").nested { + w.wl(s"tempResult = 1;") + } + w.wl(s"} else {").nested { + w.wl(s"tempResult = 0;") + } + w.wl("}") + case df: MDef => df.defType match { + case DRecord => w.wl(s"tempResult = this->${idCx.field(f.ident)}->CompareTo(rhs->${idCx.field(f.ident)});") + case DEnum => w.w(s"tempResult = this->${idCx.field(f.ident)}->CompareTo(rhs->${idJava.field(f.ident)});") + case _ => throw new AssertionError("Unreachable") + } + case _ => throw new AssertionError("Unreachable") + } + } + } + } + }) + } + } + + override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) { + val refs = new CxRefs(ident.name) + i.methods.map(m => { + m.params.map(p => refs.find(p.ty)) + m.ret.foreach(refs.find) + }) + i.consts.map(c => { + refs.find(c.ty) + }) + + val self = marshal.typename(ident, i) + + writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { + writeDoc(w, doc) + writeCxTypeParams(w, typeParams) + w.w(s"public interface class I$self").bracedSemi { + w.wlOutdent("public:") + // Constants + generateHxConstants(w, i.consts) + // Methods + for (m <- i.methods) { + w.wl + writeDoc(w, m.doc) + val ret = marshal.returnType(m.ret) + val params = m.params.map(p => marshal.paramType(p.ty) + " " + idCx.local(p.ident)) + if (m.static) { + w.wl(s"static $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")};") + } else { + val constFlag = if (m.const) " const" else "" + w.wl(s"$ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag;") + } + } + } + }) + + // Cx only generated in need of Constants + if (i.consts.nonEmpty) { + writeCxFile(ident, origin, refs.cx, w => { + generateCxConstants(w, i.consts, self) + }) + } + + } + + def writeCxTypeParams(w: IndentWriter, params: Seq[TypeParam]) { + if (params.isEmpty) return + w.wl("template " + params.map(p => "typename " + idCx.typeParam(p.ident)).mkString("<", ", ", ">")) + } + +} diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala new file mode 100644 index 000000000..b8c3529df --- /dev/null +++ b/src/source/CxMarshal.scala @@ -0,0 +1,101 @@ +package djinni + +import djinni.ast._ +import djinni.generatorTools._ +import djinni.meta._ + +class CxMarshal(spec: Spec) extends Marshal(spec) { + + override def typename(tm: MExpr): String = toCxType(tm, None) + def typename(name: String, ty: TypeDef): String = ty match { + case e: Enum => idCx.enumType(name) + case i: Interface => idCx.ty(name) + case r: Record => idCx.ty(name) + } + + override def fqTypename(tm: MExpr): String = toCxType(tm, Some(spec.cxNamespace)) + def fqTypename(name: String, ty: TypeDef): String = ty match { + case e: Enum => withNs(Some(spec.cxNamespace), idCx.enumType(name)) + case i: Interface => withNs(Some(spec.cxNamespace), idCx.ty(name)) + case r: Record => withNs(Some(spec.cxNamespace), idCx.ty(name)) + } + + override def paramType(tm: MExpr): String = toCxParamType(tm) + override def fqParamType(tm: MExpr): String = toCxParamType(tm, Some(spec.cxNamespace)) + + override def returnType(ret: Option[TypeRef]): String = ret.fold("void")(toCxType(_, None)) + override def fqReturnType(ret: Option[TypeRef]): String = ret.fold("void")(toCxType(_, Some(spec.cxNamespace))) + + override def fieldType(tm: MExpr): String = typename(tm) + override def fqFieldType(tm: MExpr): String = fqTypename(tm) + + override def toCpp(tm: MExpr, expr: String): String = throw new AssertionError("cx to cx conversion") + override def fromCpp(tm: MExpr, expr: String): String = throw new AssertionError("cx to cx conversion") + + def references(m: Meta, exclude: String): Seq[SymbolReference] = m match { + case p: MPrimitive => p.idlName match { + case "i8" | "i16" | "i32" | "i64" => List() + case _ => List() + } + case MString | MDate | MBinary | MOptional | MList | MSet | MMap => List() + case d: MDef => d.defType match { + case DEnum | DRecord => + if (d.name != exclude) { + List(ImportRef(q(spec.cxIncludePrefix + spec.cxFileIdentStyle(d.name) + "." + spec.cxHeaderExt))) + } else { + List() + } + case DInterface => + if (d.name != exclude) { + List(DeclRef(s"class ${typename(d.name, d.body)};", Some(spec.cxNamespace))) + } else { + List() + } + } + case p: MParam => List() + } + + private def toCxType(ty: TypeRef, namespace: Option[String] = None): String = toCxType(ty.resolved, namespace) + private def toCxType(tm: MExpr, namespace: Option[String]): String = { + def base(m: Meta): String = m match { + case p: MPrimitive => p.cName + case MString => "Platform::String" + case MDate => "Windows::Foundation::DateTime" + case MBinary => "Platform::Array" //no uint8_t in Cx + case MOptional => "" + case MList => "Platform::Collections::Vector" + case MSet => "Platform::Collections::Map" //no set in C++/Cx + case MMap => "Platform::Collections::Map" + case d: MDef => + d.defType match { + case DEnum => withNs(namespace, idCx.enumType(d.name)) + case DRecord => withNs(namespace, idCx.ty(d.name)) + case DInterface => withNs(namespace, s"I${idCx.ty(d.name)}") + } + case p: MParam => idCx.typeParam(p.name) + } + def expr(tm: MExpr): String = { + val args = if (tm.args.isEmpty) "" else tm.args.map(expr).mkString("<", ", ", ">") + base(tm.base) + args + } + expr(tm) + } + + // this can be used in c++ generation to know whether a const& should be applied to the parameter or not + private def toCxParamType(tm: MExpr, namespace: Option[String] = None): String = { + val cxType = toCxType(tm, namespace) + val refType = "const " + cxType + val valueType = cxType + + def toType(expr: MExpr): String = expr.base match { + case p: MPrimitive => valueType + case d: MDef => d.defType match { + case DEnum => valueType + "^" + case _ => refType + "^" + } + case MOptional => toType(expr.args.head) + case _ => refType + } + toType(tm) + } +} diff --git a/src/source/CxWinRTGenerator.scala b/src/source/CxWinRTGenerator.scala deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/source/CxWinRtMarshal.scala b/src/source/CxWinRtMarshal.scala deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/source/Main.scala b/src/source/Main.scala index 71a874ee2..715cf15b2 100644 --- a/src/source/Main.scala +++ b/src/source/Main.scala @@ -75,14 +75,25 @@ object Main { var yamlOutFile: Option[String] = None var yamlPrefix: String = "" var cxOutFolder: Option[File] = None - var cxNamespace: String = "djinni_generated" - var cxIdentStyle = IdentStyle.cxDefault - var cxTypeEnumIdentStyle: IdentConverter = null - var cxFileIdentStyle: IdentConverter = IdentStyle.underLower + var cxcppOutFolder: Option[File] = None var cxHeaderOutFolderOptional: Option[File] = None + var cxcppHeaderOutFolderOptional: Option[File] = None var cxIncludePrefix: String = "" - var cxExt: String = "cx" - var cxHeaderExt: String = "hpp" + var cxcppIncludePrefix: String = "" + var cxcppIncludeCppPrefix: String = "" + var cxcppIncludeCxPrefix: String = "" + var cxIdentStyle: CxIdentStyle = IdentStyle.cxDefault + var cxFileIdentStyle: IdentConverter = IdentStyle.camelUpper + var cxExt: String = "cpp" + var cxHeaderExt: String = "h" + var cxcppExt: String = "cpp" + var cxcppHeaderExt: String = "h" + var cxNamespace: String = "djinni" + var cxcppNamespace: String = "djinni_generated" + var cxBaseLibIncludePrefix: String = "" + var inFileListPath: Option[File] = None + var outFileListPath: Option[File] = None + var skipGeneration: Boolean = false val argParser = new scopt.OptionParser[Unit]("djinni") { @@ -174,7 +185,6 @@ object Main { .text("The namespace name to use for generated Objective-C++ classes.") opt[String]("objc-base-lib-include-prefix").valueName("...").foreach(x => objcBaseLibIncludePrefix = x) .text("The Objective-C++ base library's include path, relative to the Objective-C++ classes.") -<<<<<<< HEAD note("") opt[File]("yaml-out").valueName("").foreach(x => yamlOutFolder = Some(x)) .text("The output folder for YAML files (Generator disabled if unspecified).") @@ -189,21 +199,37 @@ 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[File]("cx-out").valueName("").foreach(x => cxOutFolder = Some(x)) - .text("The output folder for Cx files (Generator disabled if unspecified).") + .text("HAHAHAHAHAHAHAHAHAHA") + opt[File]("cxcpp-out").valueName("").foreach(x => cxcppOutFolder = Some(x)) + .text("HAHAHAHAHAHAHAHAHAHA") opt[File]("cx-header-out").valueName("").foreach(x => cxHeaderOutFolderOptional = Some(x)) - .text("The output folder for Cx header files (default: the same as --cx-out).") + .text("HAHAHAHAHAHAHAHAHAHA") + opt[File]("cxcpp-header-out").valueName("").foreach(x => cxcppHeaderOutFolderOptional = Some(x)) + .text("HAHAHAHAHAHAHAHAHAHA") opt[String]("cx-include-prefix").valueName("").foreach(cxIncludePrefix = _) - .text("The prefix for #includes of header files from Cx files.") - opt[String]("cx-namespace").valueName("...").foreach(x => cxNamespace = x) - .text("The namespace name to use for generated Cx classes.") + .text("HAHAHAHAHAHAHAHAHAHA") + opt[String]("cxcpp-include-prefix").valueName("").foreach(cxcppIncludePrefix = _) + .text("HAHAHAHAHAHAHAHAHAHA") + opt[String]("cxcpp-include-cpp-prefix").valueName("").foreach(cxcppIncludeCppPrefix = _) + .text("HAHAHAHAHAHAHAHAHAHA") + opt[String]("cxcpp-include-cx-prefix").valueName("").foreach(cxcppIncludeCxPrefix = _) + .text("HAHAHAHAHAHAHAHAHAHA") opt[String]("cx-ext").valueName("").foreach(cxExt = _) - .text("The filename extension for Cx files (default: \"cpp\").") - opt[String]("hpp-ext").valueName("").foreach(cxHeaderExt = _) - .text("The filename extension for Cx header files (default: \"hpp\").") + .text("HAHAHAHAHAHAHAHAHAHA") + opt[String]("cx-h-ext").valueName("").foreach(cxHeaderExt = _) + .text("HAHAHAHAHAHAHAHAHAHA") + opt[String]("cxcpp-ext").valueName("").foreach(cxcppExt = _) + .text("HAHAHAHAHAHAHAHAHAHA") + opt[String]("cxcpp-h-ext").valueName("").foreach(cxcppHeaderExt = _) + .text("HAHAHAHAHAHAHAHAHAHA") + opt[String]("cx-namespace").valueName("").foreach(cxNamespace = _) + .text("HAHAHAHAHAHAHAHAHAHA") + opt[String]("cxcpp-namespace").valueName("").foreach(cxcppNamespace = _) + .text("HAHAHAHAHAHAHAHAHAHA") + opt[String]("cx-base-lib-include-prefix").valueName("...").foreach(x => cxBaseLibIncludePrefix = x) + .text("The C++/Cx base library's include path, relative to the C++/Cx classes.") ->>>>>>> 3e64732... Closer to compilation! note("\nIdentifier styles (ex: \"FooBar\", \"fooBar\", \"foo_bar\", \"FOO_BAR\", \"m_fooBar\")\n") identStyle("ident-java-enum", c => { javaIdentStyle = javaIdentStyle.copy(enum = c) }) @@ -229,7 +255,6 @@ object Main { identStyle("ident-cx-field", c => { cxIdentStyle = cxIdentStyle.copy(field = c) }) identStyle("ident-cx-method", c => { cxIdentStyle = cxIdentStyle.copy(method = c) }) identStyle("ident-cx-type", c => { cxIdentStyle = cxIdentStyle.copy(ty = c) }) - identStyle("ident-cx-enum-type", c => { cppTypeEnumIdentStyle = c }) identStyle("ident-cx-type-param", c => { cxIdentStyle = cxIdentStyle.copy(typeParam = c) }) identStyle("ident-cx-local", c => { cxIdentStyle = cxIdentStyle.copy(local = c) }) identStyle("ident-cx-file", c => { cxFileIdentStyle = c }) @@ -248,6 +273,7 @@ object Main { var objcFileIdentStyle = objcFileIdentStyleOptional.getOrElse(objcIdentStyle.ty) val objcppIncludeObjcPrefix = objcppIncludeObjcPrefixOptional.getOrElse(objcppIncludePrefix) val cxHeaderOutFolder = if (cxHeaderOutFolderOptional.isDefined) cxHeaderOutFolderOptional else cxOutFolder + val cxcppHeaderOutFolder = if (cxcppHeaderOutFolderOptional.isDefined) cxcppHeaderOutFolderOptional else cxcppOutFolder // Add ObjC prefix to identstyle objcIdentStyle = objcIdentStyle.copy(ty = IdentStyle.prefix(objcTypePrefix,objcIdentStyle.ty)) @@ -257,9 +283,9 @@ object Main { cppIdentStyle = cppIdentStyle.copy(enumType = cppTypeEnumIdentStyle) } - if (cxTypeEnumIdentStyle != null) { - cxIdentStyle = cxIdentStyle.copy(enumType = cxTypeEnumIdentStyle) - } +// if (cxTypeEnumIdentStyle != null) { +// cxIdentStyle = cxIdentStyle.copy(enumType = cxTypeEnumIdentStyle) +// } // Parse IDL file. System.out.println("Parsing...") @@ -348,13 +374,24 @@ object Main { yamlOutFile, yamlPrefix, cxOutFolder, - cxExt, - cxHeaderExt, + cxcppOutFolder, cxHeaderOutFolder, + cxcppHeaderOutFolder, cxIncludePrefix, - cxNamespace, + cxcppIncludePrefix, + cxcppIncludeCppPrefix, + cxcppIncludeCxPrefix, cxIdentStyle, - cxFileIdentStyle) + cxFileIdentStyle, + cxExt, + cxHeaderExt, + cxcppExt, + cxcppHeaderExt, + cxNamespace, + cxcppNamespace, + cxBaseLibIncludePrefix, + outFileListWriter, + skipGeneration) try { diff --git a/src/source/ast.scala b/src/source/ast.scala index 0371bcb90..f238997e7 100644 --- a/src/source/ast.scala +++ b/src/source/ast.scala @@ -46,7 +46,7 @@ sealed abstract class TypeDecl { case class InternTypeDecl(override val ident: Ident, override val params: Seq[TypeParam], override val body: TypeDef, doc: Doc, override val origin: String) extends TypeDecl case class ExternTypeDecl(override val ident: Ident, override val params: Seq[TypeParam], override val body: TypeDef, properties: Map[String, Any], override val origin: String) extends TypeDecl -case class Ext(java: Boolean, cpp: Boolean, objc: Boolean) { +case class Ext(java: Boolean, cpp: Boolean, objc: Boolean, cx: Boolean) { def any(): Boolean = { java || cpp || objc } diff --git a/src/source/generator.scala b/src/source/generator.scala index ebdd9fe6e..4692ad855 100644 --- a/src/source/generator.scala +++ b/src/source/generator.scala @@ -75,13 +75,24 @@ package object generatorTools { yamlOutFile: Option[String], yamlPrefix: String, cxOutFolder: Option[File], - cxExt: String, - cxHeaderExt: String, + cxcppOutFolder: Option[File], cxHeaderOutFolder: Option[File], + cxcppHeaderOutFolder: Option[File], cxIncludePrefix: String, - cxNamespace: String, + cxcppIncludePrefix: String, + cxcppIncludeCppPrefix: String, + cxcppIncludeCxPrefix: String, cxIdentStyle: CxIdentStyle, - cxFileIdentStyle: IdentConverter) + cxFileIdentStyle: IdentConverter, + cxExt: String, + cxHeaderExt: String, + cxcppExt: String, + cxcppHeaderExt: String, + cxNamespace: String, + cxcppNamespace: String, + cxBaseLibIncludePrefix: String, + outFileListWriter: Option[Writer], + skipGeneration: Boolean) def preComma(s: String) = { if (s.isEmpty) s else ", " + s @@ -121,7 +132,7 @@ package object generatorTools { val javaDefault = JavaIdentStyle(camelUpper, camelUpper, camelLower, camelLower, camelLower, underCaps, underCaps) val cppDefault = CppIdentStyle(camelUpper, camelUpper, camelUpper, underLower, underLower, underLower, underCaps, underCaps) val objcDefault = ObjcIdentStyle(camelUpper, camelUpper, camelLower, camelLower, camelLower, camelUpper, camelUpper) - val cxDefault = CxIdentStyle(camelUpper, camelUpper, camelUpper, underLower, underLower, underLower, underCaps, underCaps) + val cxDefault = CxIdentStyle(camelUpper, camelUpper, camelUpper, camelUpper, underLower, underLower, camelUpper, underCaps) val styles = Map( "FooBar" -> camelUpper, From 24cec1f808d4585d1080716d3196c301369fdf36 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Thu, 16 Jul 2015 18:10:21 -0700 Subject: [PATCH 14/52] Let's do something more sophisticated about the various file name extensions in play --- src/source/CppGenerator.scala | 4 +- src/source/CxCppGenerator.scala | 6 +-- src/source/CxCppMarshal.scala | 75 ++++++++++++++++++++++++++++----- src/source/CxGenerator.scala | 4 +- src/source/JNIGenerator.scala | 4 +- src/source/generator.scala | 15 ++++--- 6 files changed, 83 insertions(+), 25 deletions(-) diff --git a/src/source/CppGenerator.scala b/src/source/CppGenerator.scala index b75049115..d7feba133 100644 --- a/src/source/CppGenerator.scala +++ b/src/source/CppGenerator.scala @@ -28,9 +28,9 @@ class CppGenerator(spec: Spec) extends Generator(spec) { val marshal = new CppMarshal(spec) - val writeCppFile = writeCppFileGeneric(spec.cppOutFolder.get, spec.cppNamespace, spec.cppFileIdentStyle, spec.cppIncludePrefix) _ + val writeCppFile = writeCppFileGeneric(spec.cppOutFolder.get, spec.cppNamespace, spec.cppFileIdentStyle, spec.cppIncludePrefix, spec.cppExt, spec.cppHeaderExt) _ def writeHppFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = - writeHppFileGeneric(spec.cppHeaderOutFolder.get, spec.cppNamespace, spec.cppFileIdentStyle)(name, origin, includes, fwds, f, f2) + writeHppFileGeneric(spec.cppHeaderOutFolder.get, spec.cppNamespace, spec.cppFileIdentStyle, spec.cppHeaderExt)(name, origin, includes, fwds, f, f2) class CppRefs(name: String) { var hpp = mutable.TreeSet[String]() diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index 6d93697a5..97ba0b328 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -44,9 +44,9 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { } } - val writeCxCppFile = writeCppFileGeneric(spec.cxcppOutFolder.get, spec.cxcppNamespace, spec.cppFileIdentStyle, spec.cxcppIncludePrefix) _ + val writeCxCppFile = writeCppFileGeneric(spec.cxcppOutFolder.get, spec.cxcppNamespace, spec.cppFileIdentStyle, spec.cxcppIncludePrefix, spec.cxcppExt, spec.cxcppHeaderExt) _ def writeHxFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = - writeHppFileGeneric(spec.cxcppHeaderOutFolder.get, spec.cxcppNamespace, spec.cppFileIdentStyle)(name, origin, includes, fwds, f, f2) + writeHppFileGeneric(spec.cxcppHeaderOutFolder.get, spec.cxcppNamespace, spec.cppFileIdentStyle, spec.cxcppExt)(name, origin, includes, fwds, f, f2) class CxCppRefs(name: String) { var hx = mutable.TreeSet[String]() @@ -186,7 +186,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { }) }) - writeCxCppFile(cxName, origin, refs.body, w => { + writeCxCppFile(cxcppMarshal.bodyName(cxName), origin, refs.body, w => { wrapNamespace(w, spec.cxcppNamespace, w => { w.wl(s"auto $helperClass::toCpp(CxType obj) -> CppType") w.braced { diff --git a/src/source/CxCppMarshal.scala b/src/source/CxCppMarshal.scala index 75c54db44..dbd4769ec 100644 --- a/src/source/CxCppMarshal.scala +++ b/src/source/CxCppMarshal.scala @@ -8,20 +8,28 @@ class CxCppMarshal(spec: Spec) extends Marshal(spec) { private val cppMarshal = new CppMarshal(spec) private val cxMarshal = new CxMarshal(spec) - override def typename(tm: MExpr): String = throw new AssertionError("not applicable") - def typename(name: String, ty: TypeDef): String = throw new AssertionError("not applicable") + override def typename(tm: MExpr): String = toCxCppType(tm, None) + def typename(name: String, ty: TypeDef): String = ty match { + case e: Enum => idCx.enumType(name) + case i: Interface => idCx.ty(name) + case r: Record => idCx.ty(name) + } - override def fqTypename(tm: MExpr): String = throw new AssertionError("not applicable") - def fqTypename(name: String, ty: TypeDef): String = throw new AssertionError("not applicable") + override def fqTypename(tm: MExpr): String = toCxCppType(tm, Some(spec.cxNamespace)) + def fqTypename(name: String, ty: TypeDef): String = ty match { + case e: Enum => withNs(Some(spec.cxNamespace), idCx.enumType(name)) + case i: Interface => withNs(Some(spec.cxNamespace), idCx.ty(name)) + case r: Record => withNs(Some(spec.cxNamespace), idCx.ty(name)) + } - override def paramType(tm: MExpr): String = throw new AssertionError("not applicable") - override def fqParamType(tm: MExpr): String = throw new AssertionError("not applicable") + override def paramType(tm: MExpr): String = toCxCppParamType(tm) + override def fqParamType(tm: MExpr): String = toCxCppParamType(tm, Some(spec.cxNamespace)) - override def returnType(ret: Option[TypeRef]): String = throw new AssertionError("not applicable") - override def fqReturnType(ret: Option[TypeRef]): String = throw new AssertionError("not applicable") + override def returnType(ret: Option[TypeRef]): String = ret.fold("void")(toCxCppType(_, None)) + override def fqReturnType(ret: Option[TypeRef]): String = ret.fold("void")(toCxCppType(_, Some(spec.cxNamespace))) - override def fieldType(tm: MExpr): String = throw new AssertionError("not applicable") - override def fqFieldType(tm: MExpr): String = throw new AssertionError("not applicable") + override def fieldType(tm: MExpr): String = typename(tm) + override def fqFieldType(tm: MExpr): String = fqTypename(tm) override def toCpp(tm: MExpr, expr: String): String = { s"${helperClass(tm)}::toCpp($expr)" @@ -49,7 +57,8 @@ class CxCppMarshal(spec: Spec) extends Marshal(spec) { def helperClass(name: String) = idCpp.ty(name) private def helperClass(tm: MExpr): String = helperName(tm) + helperTemplates(tm) - def headerName(ident: String): String = idCx.ty(ident) + "_convert." + spec.cxHeaderExt + def headerName(ident: String): String = idCx.ty(ident) + "_convert." + spec.cxcppHeaderExt + def bodyName(ident: String): String = idCx.ty(ident) + "_convert." + spec.cxcppExt private def helperName(tm: MExpr): String = tm.base match { case d: MDef => d.defType match { @@ -94,4 +103,48 @@ class CxCppMarshal(spec: Spec) extends Marshal(spec) { case _ => f } } + + private def toCxCppType(ty: TypeRef, namespace: Option[String] = None): String = toCxCppType(ty.resolved, namespace) + private def toCxCppType(tm: MExpr, namespace: Option[String]): String = { + def base(m: Meta): String = m match { + case p: MPrimitive => p.cName + case MString => "std::string" + case MDate => "std::chrono::system_clock::time_point" + case MBinary => "std::vector" + case MOptional => spec.cppOptionalTemplate + case MList => "std::vector" + case MSet => "std::unordered_set" + case MMap => "std::unordered_map" + case d: MDef => + d.defType match { + case DEnum => withNs(namespace, idCpp.enumType(d.name)) + case DRecord => withNs(namespace, idCpp.ty(d.name)) + case DInterface => s"std::shared_ptr<${withNs(namespace, idCpp.ty(d.name))}>" + } + case p: MParam => idCpp.typeParam(p.name) + } + def expr(tm: MExpr): String = { + val args = if (tm.args.isEmpty) "" else tm.args.map(expr).mkString("<", ", ", ">") + base(tm.base) + args + } + expr(tm) + } + + // this can be used in c++ generation to know whether a const& should be applied to the parameter or not + private def toCxCppParamType(tm: MExpr, namespace: Option[String] = None): String = { + val cppType = toCxCppType(tm, namespace) + val refType = "const " + cppType + " &" + val valueType = cppType + + def toType(expr: MExpr): String = expr.base match { + case p: MPrimitive => valueType + case d: MDef => d.defType match { + case DEnum => valueType + case _ => refType + } + case MOptional => toType(expr.args.head) + case _ => refType + } + toType(tm) + } } diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index b640fd4fc..d93f0ca37 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -28,9 +28,9 @@ class CxGenerator(spec: Spec) extends Generator(spec) { val marshal = new CxMarshal(spec) - val writeCxFile = writeCppFileGeneric(spec.cxOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle, spec.cxIncludePrefix) _ + val writeCxFile = writeCppFileGeneric(spec.cxOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle, spec.cxIncludePrefix, spec.cxExt, spec.cxHeaderExt) _ def writeHxFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = - writeHppFileGeneric(spec.cxHeaderOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle)(name, origin, includes, fwds, f, f2) + writeHppFileGeneric(spec.cxHeaderOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle, spec.cxHeaderExt)(name, origin, includes, fwds, f, f2) class CxRefs(name: String) { var hx = mutable.TreeSet[String]() diff --git a/src/source/JNIGenerator.scala b/src/source/JNIGenerator.scala index 75e5bb6df..3d080fd5d 100644 --- a/src/source/JNIGenerator.scala +++ b/src/source/JNIGenerator.scala @@ -31,9 +31,9 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { val jniBaseLibClassIdentStyle = IdentStyle.prefix("H", IdentStyle.camelUpper) val jniBaseLibFileIdentStyle = jniBaseLibClassIdentStyle - val writeJniCppFile = writeCppFileGeneric(spec.jniOutFolder.get, spec.jniNamespace, spec.jniFileIdentStyle, spec.jniIncludePrefix) _ + val writeJniCppFile = writeCppFileGeneric(spec.jniOutFolder.get, spec.jniNamespace, spec.jniFileIdentStyle, spec.jniIncludePrefix, spec.cppExt, spec.cppHeaderExt) _ def writeJniHppFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = - writeHppFileGeneric(spec.jniHeaderOutFolder.get, spec.jniNamespace, spec.jniFileIdentStyle)(name, origin, includes, fwds, f, f2) + writeHppFileGeneric(spec.jniHeaderOutFolder.get, spec.jniNamespace, spec.jniFileIdentStyle, spec.cppHeaderExt)(name, origin, includes, fwds, f, f2) class JNIRefs(name: String) { var jniHpp = mutable.TreeSet[String]() diff --git a/src/source/generator.scala b/src/source/generator.scala index 4692ad855..92ad0555d 100644 --- a/src/source/generator.scala +++ b/src/source/generator.scala @@ -224,6 +224,11 @@ package object generatorTools { new YamlGenerator(spec).generate(idl) } } + if (spec.cxcppOutFolder.isDefined) { + createFolder("CxCpp", spec.cxcppOutFolder.get) + createFolder("CxCpp header", spec.cxcppHeaderOutFolder.get) + new CxCppGenerator(spec).generate(idl) + } None } catch { @@ -299,8 +304,8 @@ abstract class Generator(spec: Spec) w.wl("} // end anonymous namespace") } - def writeHppFileGeneric(folder: File, namespace: String, fileIdentStyle: IdentConverter)(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit) { - createFile(folder, fileIdentStyle(name) + "." + spec.cppHeaderExt, (w: IndentWriter) => { + def writeHppFileGeneric(folder: File, namespace: String, fileIdentStyle: IdentConverter, headerExt: String)(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit) { + createFile(folder, fileIdentStyle(name) + "." + headerExt, (w: IndentWriter) => { w.wl("// AUTOGENERATED FILE - DO NOT MODIFY!") w.wl("// This file generated by Djinni from " + origin) w.wl @@ -323,12 +328,12 @@ abstract class Generator(spec: Spec) }) } - def writeCppFileGeneric(folder: File, namespace: String, fileIdentStyle: IdentConverter, includePrefix: String)(name: String, origin: String, includes: Iterable[String], f: IndentWriter => Unit) { - createFile(folder, fileIdentStyle(name) + "." + spec.cppExt, (w: IndentWriter) => { + def writeCppFileGeneric(folder: File, namespace: String, fileIdentStyle: IdentConverter, includePrefix: String, bodyExt: String, headerExt: String)(name: String, origin: String, includes: Iterable[String], f: IndentWriter => Unit) { + createFile(folder, fileIdentStyle(name) + "." + bodyExt, (w: IndentWriter) => { w.wl("// AUTOGENERATED FILE - DO NOT MODIFY!") w.wl("// This file generated by Djinni from " + origin) w.wl - val myHeader = q(includePrefix + fileIdentStyle(name) + "." + spec.cppHeaderExt) + val myHeader = q(includePrefix + fileIdentStyle(name) + "." + headerExt) w.wl(s"#include $myHeader // my header") includes.foreach(w.wl(_)) w.wl From 0bbf65e9535b5b45f2d2592374ed9ee18e11b5e6 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Thu, 16 Jul 2015 18:13:33 -0700 Subject: [PATCH 15/52] No double extensions --- src/source/CxCppGenerator.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index 97ba0b328..dd55902af 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -170,7 +170,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val helperClass = cxcppMarshal.helperClass(ident) - writeCxCppFile(cxcppMarshal.headerName(cxName), origin, refs.privHeader, w => { + writeCxCppFile(cxName, origin, refs.privHeader, w => { w.wl wrapNamespace(w, spec.cxcppNamespace, w => { w.wl(s"struct $helperClass") @@ -186,7 +186,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { }) }) - writeCxCppFile(cxcppMarshal.bodyName(cxName), origin, refs.body, w => { + writeCxCppFile(cxName, origin, refs.body, w => { wrapNamespace(w, spec.cxcppNamespace, w => { w.wl(s"auto $helperClass::toCpp(CxType obj) -> CppType") w.braced { From cb1d66969696ae9a635af23a4650bf92d5e698a5 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 09:22:49 -0700 Subject: [PATCH 16/52] Fixed problem with cxcpp filenames being repeated and hence overwriting each other. --- src/source/CxCppGenerator.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index dd55902af..97ba0b328 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -170,7 +170,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val helperClass = cxcppMarshal.helperClass(ident) - writeCxCppFile(cxName, origin, refs.privHeader, w => { + writeCxCppFile(cxcppMarshal.headerName(cxName), origin, refs.privHeader, w => { w.wl wrapNamespace(w, spec.cxcppNamespace, w => { w.wl(s"struct $helperClass") @@ -186,7 +186,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { }) }) - writeCxCppFile(cxName, origin, refs.body, w => { + writeCxCppFile(cxcppMarshal.bodyName(cxName), origin, refs.body, w => { wrapNamespace(w, spec.cxcppNamespace, w => { w.wl(s"auto $helperClass::toCpp(CxType obj) -> CppType") w.braced { From cb8a3855ae28b860cbd54e043b7b9f735c25df2c Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 09:47:02 -0700 Subject: [PATCH 17/52] Filenaming appears to be sorted out. --- src/source/CxCppGenerator.scala | 35 ++++++++++----------------------- src/source/CxCppMarshal.scala | 4 ++-- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index 97ba0b328..d19f480a2 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -29,24 +29,9 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val cxcppMarshal = new CxCppMarshal(spec) val cxMarshal = new CxMarshal(spec) - class CxRefs() { - var body = mutable.TreeSet[String]() - var privHeader = mutable.TreeSet[String]() - - def find(ty: TypeRef) { find(ty.resolved) } - def find(tm: MExpr) { - tm.args.foreach(find) - find(tm.base) - } - def find(m: Meta) = for(r <- cxcppMarshal.references(m)) r match { - case ImportRef(arg) => body.add("#import " + arg) - case _ => - } - } - val writeCxCppFile = writeCppFileGeneric(spec.cxcppOutFolder.get, spec.cxcppNamespace, spec.cppFileIdentStyle, spec.cxcppIncludePrefix, spec.cxcppExt, spec.cxcppHeaderExt) _ def writeHxFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = - writeHppFileGeneric(spec.cxcppHeaderOutFolder.get, spec.cxcppNamespace, spec.cppFileIdentStyle, spec.cxcppExt)(name, origin, includes, fwds, f, f2) + writeHppFileGeneric(spec.cxcppHeaderOutFolder.get, spec.cxcppNamespace, spec.cppFileIdentStyle, spec.cxcppHeaderExt)(name, origin, includes, fwds, f, f2) class CxCppRefs(name: String) { var hx = mutable.TreeSet[String]() @@ -145,7 +130,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { } override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) { - val refs = new CxRefs() + val refs = new CxCppRefs(ident.name) for (c <- r.consts) refs.find(c.ty) for (f <- r.fields) @@ -155,11 +140,11 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val cxSelf = cxMarshal.fqTypename(ident, r) val cppSelf = cxcppMarshal.fqTypename(ident, r) - refs.privHeader.add("!#include " + q(spec.cxcppIncludeCxPrefix + (if(r.ext.cx) "../" else "") + cxcppMarshal.headerName(ident))) - refs.privHeader.add("!#include " + q(spec.cxcppIncludeCppPrefix + (if(r.ext.cpp) "../" else "") + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt)) + refs.hx.add("!#include " + q(spec.cxcppIncludeCxPrefix + (if(r.ext.cx) "../" else "") + cxcppMarshal.headerName(ident))) + refs.hx.add("!#include " + q(spec.cxcppIncludeCppPrefix + (if(r.ext.cpp) "../" else "") + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt)) - refs.body.add("#include ") - refs.body.add("!#import " + q(spec.cxcppIncludePrefix + cxcppMarshal.headerName(cxName))) + refs.cxcpp.add("#include ") + refs.cxcpp.add("!#import " + q(spec.cxcppIncludePrefix + cxcppMarshal.headerName(cxName))) def checkMutable(tm: MExpr): Boolean = tm.base match { case MOptional => checkMutable(tm.args.head) @@ -170,7 +155,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val helperClass = cxcppMarshal.helperClass(ident) - writeCxCppFile(cxcppMarshal.headerName(cxName), origin, refs.privHeader, w => { + writeHxFile(cxcppMarshal.headerName(cxName), origin, refs.hx, refs.hxFwds, w => { w.wl wrapNamespace(w, spec.cxcppNamespace, w => { w.wl(s"struct $helperClass") @@ -186,7 +171,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { }) }) - writeCxCppFile(cxcppMarshal.bodyName(cxName), origin, refs.body, w => { + writeCxCppFile(cxcppMarshal.bodyName(cxName), origin, refs.cxcpp, w => { wrapNamespace(w, spec.cxcppNamespace, w => { w.wl(s"auto $helperClass::toCpp(CxType obj) -> CppType") w.braced { @@ -219,7 +204,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val self = cxcppMarshal.typename(ident, i) - writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { + writeHxFile(cxcppMarshal.headerName(ident.name), origin, refs.hx, refs.hxFwds, w => { writeDoc(w, doc) writeCxCppTypeParams(w, typeParams) w.w(s"class $self").bracedSemi { @@ -246,7 +231,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { // CxCpp only generated in need of Constants if (i.consts.nonEmpty) { - writeCxCppFile(ident, origin, refs.cxcpp, w => { + writeCxCppFile(cxcppMarshal.bodyName(ident.name), origin, refs.cxcpp, w => { generateCxCppConstants(w, i.consts, self) }) } diff --git a/src/source/CxCppMarshal.scala b/src/source/CxCppMarshal.scala index dbd4769ec..e926abb05 100644 --- a/src/source/CxCppMarshal.scala +++ b/src/source/CxCppMarshal.scala @@ -57,8 +57,8 @@ class CxCppMarshal(spec: Spec) extends Marshal(spec) { def helperClass(name: String) = idCpp.ty(name) private def helperClass(tm: MExpr): String = helperName(tm) + helperTemplates(tm) - def headerName(ident: String): String = idCx.ty(ident) + "_convert." + spec.cxcppHeaderExt - def bodyName(ident: String): String = idCx.ty(ident) + "_convert." + spec.cxcppExt + def headerName(ident: String): String = idCx.ty(ident) + "_convert" + def bodyName(ident: String): String = idCx.ty(ident) + "_convert" private def helperName(tm: MExpr): String = tm.base match { case d: MDef => d.defType match { From 656f5dcdd2dafd5b8b177b5dee95ec88febc8655 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 10:02:13 -0700 Subject: [PATCH 18/52] Add proper djinni_generated clas headers, and get rid of double namespace warpping --- src/source/CxCppGenerator.scala | 42 ++++++++++++--------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index d19f480a2..f80f8f7d7 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -157,7 +157,6 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { writeHxFile(cxcppMarshal.headerName(cxName), origin, refs.hx, refs.hxFwds, w => { w.wl - wrapNamespace(w, spec.cxcppNamespace, w => { w.wl(s"struct $helperClass") w.bracedSemi { w.wl(s"using CppType = $cppSelf;") @@ -168,15 +167,13 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.wl(s"static CppType toCpp(CxType cxc);") w.wl(s"static CxType fromCpp(const CppType& cpp);") } - }) }) writeCxCppFile(cxcppMarshal.bodyName(cxName), origin, refs.cxcpp, w => { - wrapNamespace(w, spec.cxcppNamespace, w => { - w.wl(s"auto $helperClass::toCpp(CxType obj) -> CppType") + w.wl(s"auto $helperClass::toCpp(CxType cx) -> CppType") w.braced { - w.wl("assert(obj);") - if(r.fields.isEmpty) w.wl("(void)obj; // Suppress warnings in relase builds for empty records") + w.wl("assert(cx);") + if(r.fields.isEmpty) w.wl("(void)cx; // Suppress warnings in relase builds for empty records") writeAlignedCall(w, "return {", r.fields, "}", f => cxcppMarshal.toCpp(f.ty, "cx->" + idCx.field(f.ident))) w.wl(";") } @@ -188,7 +185,6 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { writeAlignedCall(w, "return ref new CxType(", r.fields, ")", f=> cxcppMarshal.fromCpp(f.ty, "cpp." + idCpp.field(f.ident))) w.wl(";") } - }) }) } @@ -203,30 +199,22 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { }) val self = cxcppMarshal.typename(ident, i) + val cxSelf = cxMarshal.fqTypename(ident, i) + val cppSelf = cxcppMarshal.fqTypename(ident, i) + val helperClass = cxcppMarshal.helperClass(ident) writeHxFile(cxcppMarshal.headerName(ident.name), origin, refs.hx, refs.hxFwds, w => { - writeDoc(w, doc) - writeCxCppTypeParams(w, typeParams) - w.w(s"class $self").bracedSemi { - w.wlOutdent("public:") - // Destructor - w.wl(s"virtual ~$self() {}") - // Constants - generateHxConstants(w, i.consts) - // Methods - for (m <- i.methods) { + w.wl + w.wl(s"struct $helperClass") + w.bracedSemi { + w.wl(s"using CppType = $cppSelf;") + w.wl(s"using CxType = $cxSelf^;"); + w.wl + w.wl(s"using Boxed = $helperClass;") w.wl - writeDoc(w, m.doc) - val ret = cxcppMarshal.returnType(m.ret) - val params = m.params.map(p => cxcppMarshal.paramType(p.ty) + " " + idCpp.local(p.ident)) - if (m.static) { - w.wl(s"static $ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")};") - } else { - val constFlag = if (m.const) " const" else "" - w.wl(s"virtual $ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag = 0;") - } + w.wl(s"static CppType toCpp(CxType cxc);") + w.wl(s"static CxType fromCpp(const CppType& cpp);") } - } }) // CxCpp only generated in need of Constants From e3afd06535acba54f505be45ca880614e43bca03 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 11:20:03 -0700 Subject: [PATCH 19/52] I think we have C++/Cx types worked out, mostly? Set is going to be a tough nut! --- src/source/CxMarshal.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index b8c3529df..dd3e24aad 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -59,9 +59,9 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { private def toCxType(tm: MExpr, namespace: Option[String]): String = { def base(m: Meta): String = m match { case p: MPrimitive => p.cName - case MString => "Platform::String" - case MDate => "Windows::Foundation::DateTime" - case MBinary => "Platform::Array" //no uint8_t in Cx + case MString => "Platform::String^" + case MDate => "Windows::Foundation::DateTime^" + case MBinary => "Platform::Array^" //no uint8_t in Cx case MOptional => "" case MList => "Platform::Collections::Vector" case MSet => "Platform::Collections::Map" //no set in C++/Cx @@ -69,13 +69,13 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { case d: MDef => d.defType match { case DEnum => withNs(namespace, idCx.enumType(d.name)) - case DRecord => withNs(namespace, idCx.ty(d.name)) - case DInterface => withNs(namespace, s"I${idCx.ty(d.name)}") + case DRecord => s"${withNs(namespace, idCx.ty(d.name))}^" + case DInterface => s"${withNs(namespace, idCx.ty(d.name))}^" } case p: MParam => idCx.typeParam(p.name) } def expr(tm: MExpr): String = { - val args = if (tm.args.isEmpty) "" else tm.args.map(expr).mkString("<", ", ", ">") + val args = if (tm.args.isEmpty) "" else tm.args.map(expr).mkString("<", ", ", ">^") base(tm.base) + args } expr(tm) From 92948ac3fbd7b4f66d70b9894fff526f811a495d Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 12:28:57 -0700 Subject: [PATCH 20/52] More type problems dealt with --- src/source/CxGenerator.scala | 23 ++++++++++++++++++++++- src/source/CxMarshal.scala | 6 +++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index d93f0ca37..40a488ff2 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -239,7 +239,24 @@ class CxGenerator(spec: Spec) extends Generator(spec) { writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { writeDoc(w, doc) writeCxTypeParams(w, typeParams) - w.w(s"public interface class I$self").bracedSemi { + if (i.ext.cx) w.wl(s"public interface class I$self").bracedSemi { + // Constants + generateHxConstants(w, i.consts) + // Methods + for (m <- i.methods) { + w.wl + writeDoc(w, m.doc) + val ret = marshal.returnType(m.ret) + val params = m.params.map(p => marshal.paramType(p.ty) + " " + idCx.local(p.ident)) + if (m.static) { + w.wl(s"static $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")};") + } else { + val constFlag = if (m.const) " const" else "" + w.wl(s"$ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag;") + } + } + } + else w.wl(s"public ref class $self sealed : public Platform::Object").bracedSemi { w.wlOutdent("public:") // Constants generateHxConstants(w, i.consts) @@ -256,6 +273,10 @@ class CxGenerator(spec: Spec) extends Generator(spec) { w.wl(s"$ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag;") } } + //private members + w.wlOutdent("internal:") + //construct from a cpp ref + } }) diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index dd3e24aad..c8652751b 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -64,7 +64,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { case MBinary => "Platform::Array^" //no uint8_t in Cx case MOptional => "" case MList => "Platform::Collections::Vector" - case MSet => "Platform::Collections::Map" //no set in C++/Cx + case MSet => "Platform::Collections::Map" //no set in C++/Cx FOr now this shit is broken until I can figure out how to make something map onto itself. case MMap => "Platform::Collections::Map" case d: MDef => d.defType match { @@ -90,8 +90,8 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { def toType(expr: MExpr): String = expr.base match { case p: MPrimitive => valueType case d: MDef => d.defType match { - case DEnum => valueType + "^" - case _ => refType + "^" + case DEnum => valueType + case _ => refType } case MOptional => toType(expr.args.head) case _ => refType From 517c007f18c603dba01fede1a64ee8417659d586 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 13:00:57 -0700 Subject: [PATCH 21/52] More progress. --- src/source/CxGenerator.scala | 19 +++++++++++++------ src/source/CxMarshal.scala | 2 +- src/source/generator.scala | 12 ++++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index 40a488ff2..e8501c293 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -27,6 +27,7 @@ import scala.collection.mutable class CxGenerator(spec: Spec) extends Generator(spec) { val marshal = new CxMarshal(spec) + val cppMarshal = new CppMarshal(spec) val writeCxFile = writeCppFileGeneric(spec.cxOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle, spec.cxIncludePrefix, spec.cxExt, spec.cxHeaderExt) _ def writeHxFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = @@ -235,11 +236,12 @@ class CxGenerator(spec: Spec) extends Generator(spec) { }) val self = marshal.typename(ident, i) + val cppSelf = cppMarshal.fqTypename(ident, i) writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { writeDoc(w, doc) writeCxTypeParams(w, typeParams) - if (i.ext.cx) w.wl(s"public interface class I$self").bracedSemi { + if (i.ext.cx) w.wl(s"public interface class $self").bracedSemi { // Constants generateHxConstants(w, i.consts) // Methods @@ -276,16 +278,21 @@ class CxGenerator(spec: Spec) extends Generator(spec) { //private members w.wlOutdent("internal:") //construct from a cpp ref - + w.wl(s"$self(const std::shared_ptr<$cppSelf>& cppRef);") + w.wl(s"::djinni::CppWrapperCache<$cppSelf>::Handle cppRef;") } }) // Cx only generated in need of Constants - if (i.consts.nonEmpty) { - writeCxFile(ident, origin, refs.cx, w => { + writeCxFile(ident, origin, refs.cx, w => { + if (i.consts.nonEmpty) { generateCxConstants(w, i.consts, self) - }) - } + } + + if (! i.ext.cx) { + w.wl("HAHHAH") + } + }) } diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index c8652751b..30d8e5c20 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -9,7 +9,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { override def typename(tm: MExpr): String = toCxType(tm, None) def typename(name: String, ty: TypeDef): String = ty match { case e: Enum => idCx.enumType(name) - case i: Interface => idCx.ty(name) + case i: Interface => if(i.ext.cx) s"I${idCx.ty(name)}" else idCx.ty(name) case r: Record => idCx.ty(name) } diff --git a/src/source/generator.scala b/src/source/generator.scala index 92ad0555d..85d2d5617 100644 --- a/src/source/generator.scala +++ b/src/source/generator.scala @@ -223,10 +223,18 @@ package object generatorTools { createFolder("YAML", spec.yamlOutFolder.get) new YamlGenerator(spec).generate(idl) } + if (spec.cxOutFolder.isDefined) { + if (!spec.skipGeneration) { + createFolder("Cx", spec.cxOutFolder.get) + createFolder("Cx header", spec.cxHeaderOutFolder.get) + } + new CxGenerator(spec).generate(idl) } if (spec.cxcppOutFolder.isDefined) { - createFolder("CxCpp", spec.cxcppOutFolder.get) - createFolder("CxCpp header", spec.cxcppHeaderOutFolder.get) + if (!spec.skipGeneration) { + createFolder("CxCpp", spec.cxcppOutFolder.get) + createFolder("CxCpp header", spec.cxcppHeaderOutFolder.get) + } new CxCppGenerator(spec).generate(idl) } None From 1dc7555948ed72105b04060267f63fb73df6ddc6 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 14:02:55 -0700 Subject: [PATCH 22/52] Now we are on our way to generating real Cx code! --- src/source/CxGenerator.scala | 2 +- src/source/CxMarshal.scala | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index e8501c293..9f41384dc 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -213,7 +213,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { w.wl("}") case df: MDef => df.defType match { case DRecord => w.wl(s"tempResult = this->${idCx.field(f.ident)}->CompareTo(rhs->${idCx.field(f.ident)});") - case DEnum => w.w(s"tempResult = this->${idCx.field(f.ident)}->CompareTo(rhs->${idJava.field(f.ident)});") + case DEnum => w.w(s"tempResult = this->${idCx.field(f.ident)}->CompareTo(rhs->${idCx.field(f.ident)});") case _ => throw new AssertionError("Unreachable") } case _ => throw new AssertionError("Unreachable") diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index 30d8e5c20..96d4a5c44 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -70,7 +70,11 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { d.defType match { case DEnum => withNs(namespace, idCx.enumType(d.name)) case DRecord => s"${withNs(namespace, idCx.ty(d.name))}^" - case DInterface => s"${withNs(namespace, idCx.ty(d.name))}^" + case DInterface => d.body match { + case e: Enum => s"${withNs(namespace, idCx.ty(d.name))}^" + case i: Interface => if(i.ext.cx) s"I${withNs(namespace, idCx.ty(d.name))}^" else s"${withNs(namespace, idCx.ty(d.name))}^" + case r: Record => s"${withNs(namespace, idCx.ty(d.name))}^" + } } case p: MParam => idCx.typeParam(p.name) } From 326918ef2f52c6d392ebc1db3484ae71f0d8958f Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 14:35:07 -0700 Subject: [PATCH 23/52] Nearly there to valid Cx code! Cpp is next! --- src/source/CxGenerator.scala | 67 +++++++++++++++++++++++++----------- src/source/CxMarshal.scala | 63 ++++++++++++++++++++++++++++++--- 2 files changed, 105 insertions(+), 25 deletions(-) diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index 9f41384dc..3d52f8e43 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -26,7 +26,7 @@ import scala.collection.mutable class CxGenerator(spec: Spec) extends Generator(spec) { - val marshal = new CxMarshal(spec) + val cxMarshal = new CxMarshal(spec) val cppMarshal = new CppMarshal(spec) val writeCxFile = writeCppFileGeneric(spec.cxOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle, spec.cxIncludePrefix, spec.cxExt, spec.cxHeaderExt) _ @@ -43,16 +43,23 @@ class CxGenerator(spec: Spec) extends Generator(spec) { tm.args.foreach(find) find(tm.base) } - def find(m: Meta) = for(r <- marshal.references(m, name)) r match { + def find(m: Meta) = for(r <- cxMarshal.references(m, name)) r match { case ImportRef(arg) => hx.add("#include " + arg) case DeclRef(decl, Some(spec.cxNamespace)) => hxFwds.add(decl) case DeclRef(_, _) => } } + def writeCxFuncDecl(method: Interface.Method, w: IndentWriter) { + val label = if (method.static) "static " else "" + val ret = cxMarshal.fqReturnType(method.ret) + val decl = s"$label ($ret)${idObjc.method(method.ident)}" + writeAlignedCall(w, decl, method.params, "X", p => s"(${cxMarshal.paramType(p.ty)})${idObjc.local(p.ident)}") + } + override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { val refs = new CxRefs(ident.name) - val self = marshal.typename(ident, e) + val self = cxMarshal.typename(ident, e) writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { w.w(s"enum class $self : int").bracedSemi { @@ -65,7 +72,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { w => { // std::hash specialization has to go *outside* of the wrapNs if (spec.cppEnumHashWorkaround) { - val fqSelf = marshal.fqTypename(ident, e) + val fqSelf = cxMarshal.fqTypename(ident, e) w.wl wrapNamespace(w, "std", (w: IndentWriter) => { @@ -85,24 +92,24 @@ class CxGenerator(spec: Spec) extends Generator(spec) { for (c <- consts) { w.wl writeDoc(w, c.doc) - w.wl(s"static ${marshal.fieldType(c.ty)} const ${idCx.const(c.ident)};") + w.wl(s"static ${cxMarshal.fieldType(c.ty)} const ${idCx.const(c.ident)};") } } def generateCxConstants(w: IndentWriter, consts: Seq[Const], selfName: String) = { def writeCxConst(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match { case l: Long => w.w(l.toString) - case d: Double if marshal.fieldType(ty) == "float" => w.w(d.toString + "f") + case d: Double if cxMarshal.fieldType(ty) == "float" => w.w(d.toString + "f") case d: Double => w.w(d.toString) case b: Boolean => w.w(if (b) "true" else "false") case s: String => w.w(s) - case e: EnumValue => w.w(marshal.typename(ty) + "::" + idCx.enum(e.ty.name + "_" + e.name)) + case e: EnumValue => w.w(cxMarshal.typename(ty) + "::" + idCx.enum(e.ty.name + "_" + e.name)) case v: ConstRef => w.w(selfName + "::" + idCx.const(v)) case z: Map[_, _] => { // Value is record val recordMdef = ty.resolved.base.asInstanceOf[MDef] val record = recordMdef.body.asInstanceOf[Record] val vMap = z.asInstanceOf[Map[String, Any]] - w.wl(marshal.typename(ty) + "(") + w.wl(cxMarshal.typename(ty) + "(") w.increase() // Use exact sequence val skipFirst = SkipFirst() @@ -119,7 +126,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { val skipFirst = SkipFirst() for (c <- consts) { skipFirst{ w.wl } - w.w(s"${marshal.fieldType(c.ty)} const $selfName::${idCx.const(c.ident)} = ") + w.w(s"${cxMarshal.fieldType(c.ty)} const $selfName::${idCx.const(c.ident)} = ") writeCxConst(w, c.ty, c.value) w.wl(";") } @@ -130,9 +137,9 @@ class CxGenerator(spec: Spec) extends Generator(spec) { r.fields.foreach(f => refs.find(f.ty)) r.consts.foreach(c => refs.find(c.ty)) - val self = marshal.typename(ident, r) + val self = cxMarshal.typename(ident, r) val (cxName, cxFinal) = if (r.ext.cx) (ident.name + "_base", "") else (ident.name, " final") - val actualSelf = marshal.typename(cxName, r) + val actualSelf = cxMarshal.typename(cxName, r) // Requiring the extended class if (r.ext.cx) { @@ -149,13 +156,13 @@ class CxGenerator(spec: Spec) extends Generator(spec) { // Field definitions. for (f <- r.fields) { writeDoc(w, f.doc) - w.wl(marshal.fieldType(f.ty) + " " + idCx.field(f.ident) + ";") + w.wl(cxMarshal.fieldType(f.ty) + " " + idCx.field(f.ident) + ";") } // Constructor. if (r.fields.nonEmpty) { w.wl - writeAlignedCall(w, actualSelf + "(", r.fields, ")", f => marshal.fieldType(f.ty) + " " + idCx.local(f.ident)) + writeAlignedCall(w, actualSelf + "(", r.fields, ")", f => cxMarshal.fieldType(f.ty) + " " + idCx.local(f.ident)) w.wl val init = (f: Field) => idCx.field(f.ident) + "(" + idCx.local(f.ident) + ")" w.wl(": " + init(r.fields.head)) @@ -235,7 +242,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { refs.find(c.ty) }) - val self = marshal.typename(ident, i) + val self = cxMarshal.typename(ident, i) val cppSelf = cppMarshal.fqTypename(ident, i) writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { @@ -248,8 +255,8 @@ class CxGenerator(spec: Spec) extends Generator(spec) { for (m <- i.methods) { w.wl writeDoc(w, m.doc) - val ret = marshal.returnType(m.ret) - val params = m.params.map(p => marshal.paramType(p.ty) + " " + idCx.local(p.ident)) + val ret = cxMarshal.returnType(m.ret) + val params = m.params.map(p => cxMarshal.paramType(p.ty) + " " + idCx.local(p.ident)) if (m.static) { w.wl(s"static $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")};") } else { @@ -266,8 +273,8 @@ class CxGenerator(spec: Spec) extends Generator(spec) { for (m <- i.methods) { w.wl writeDoc(w, m.doc) - val ret = marshal.returnType(m.ret) - val params = m.params.map(p => marshal.paramType(p.ty) + " " + idCx.local(p.ident)) + val ret = cxMarshal.returnType(m.ret) + val params = m.params.map(p => cxMarshal.paramType(p.ty) + " " + idCx.local(p.ident)) if (m.static) { w.wl(s"static $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")};") } else { @@ -278,7 +285,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { //private members w.wlOutdent("internal:") //construct from a cpp ref - w.wl(s"$self(const std::shared_ptr<$cppSelf>& cppRef);") + w.wl(s"$self(const std::shared_ptr<$cppSelf>& m_cppRef);") w.wl(s"::djinni::CppWrapperCache<$cppSelf>::Handle cppRef;") } }) @@ -290,7 +297,27 @@ class CxGenerator(spec: Spec) extends Generator(spec) { } if (! i.ext.cx) { - w.wl("HAHHAH") + //constructor + w.wl(s"$self::$self(const std::shared_ptr<$cppSelf>&)cppRef)") + w.braced { + w.w("if (self = [super init])").braced { + w.wl("m_cppRef.assign(cppRef);") + } + } + //methods + for (m <- i.methods) { + w.wl + writeCxFuncDecl(m, w) + w.braced { +// w.w("try").bracedEnd(" DJINNI_TRANSLATE_EXCEPTIONS()") { + val ret = m.ret.fold("")(_ => "auto r = ") + val call = ret + (if (!m.static) "m_cppRef.get()->" else cppSelf + "::") + idCpp.method(m.ident) + "(" + writeAlignedCall(w, call, m.params, ")", p => cxMarshal.toCpp(p.ty, idCx.local(p.ident.name))) + w.wl(";") + m.ret.fold()(r => w.wl(s"return ${cxMarshal.fromCpp(r, "r")};")) + // } + } + } } }) diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index 96d4a5c44..612aa2f22 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -6,6 +6,8 @@ import djinni.meta._ class CxMarshal(spec: Spec) extends Marshal(spec) { + private val cppMarshal = new CppMarshal(spec) + override def typename(tm: MExpr): String = toCxType(tm, None) def typename(name: String, ty: TypeDef): String = ty match { case e: Enum => idCx.enumType(name) @@ -29,8 +31,59 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { override def fieldType(tm: MExpr): String = typename(tm) override def fqFieldType(tm: MExpr): String = fqTypename(tm) - override def toCpp(tm: MExpr, expr: String): String = throw new AssertionError("cx to cx conversion") - override def fromCpp(tm: MExpr, expr: String): String = throw new AssertionError("cx to cx conversion") + override def toCpp(tm: MExpr, expr: String): String = { + s"${helperClass(tm)}::toCpp($expr)" + } + override def fromCpp(tm: MExpr, expr: String): String = { + s"${helperClass(tm)}::fromCpp($expr)" + } + + def helperClass(name: String) = idCpp.ty(name) + private def helperClass(tm: MExpr): String = helperName(tm) + helperTemplates(tm) + + private def helperName(tm: MExpr): String = tm.base match { + case d: MDef => d.defType match { + case DEnum => withNs(Some("djinni"), s"Enum<${cppMarshal.fqTypename(tm)}, ${fqTypename(tm)}>") + case _ => withNs(Some(spec.objcppNamespace), helperClass(d.name)) + } + case o => withNs(Some("djinni"), o match { + case p: MPrimitive => p.idlName match { + case "i8" => "I8" + case "i16" => "I16" + case "i32" => "I32" + case "i64" => "I64" + case "f32" => "F32" + case "f64" => "F64" + case "bool" => "Bool" + } + case MOptional => "Optional" + case MBinary => "Binary" + case MDate => "Date" + case MString => "String" + case MList => "List" + case MSet => "Set" + case MMap => "Map" + case d: MDef => throw new AssertionError("unreachable") + case p: MParam => throw new AssertionError("not applicable") + }) + } + + private def helperTemplates(tm: MExpr): String = { + def f() = if(tm.args.isEmpty) "" else tm.args.map(helperClass).mkString("<", ", ", ">") + tm.base match { + case MOptional => + assert(tm.args.size == 1) + val argHelperClass = helperClass(tm.args.head) + s"<${spec.cppOptionalTemplate}, $argHelperClass>" + case MList | MSet => + assert(tm.args.size == 1) + f + case MMap => + assert(tm.args.size == 2) + f + case _ => f + } + } def references(m: Meta, exclude: String): Seq[SymbolReference] = m match { case p: MPrimitive => p.idlName match { @@ -63,9 +116,9 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { case MDate => "Windows::Foundation::DateTime^" case MBinary => "Platform::Array^" //no uint8_t in Cx case MOptional => "" - case MList => "Platform::Collections::Vector" - case MSet => "Platform::Collections::Map" //no set in C++/Cx FOr now this shit is broken until I can figure out how to make something map onto itself. - case MMap => "Platform::Collections::Map" + case MList => "Windows::Foundation::Collections::IVector" + case MSet => "Windows::Foundation::Collections::IMap" //no set in C++/Cx FOr now this shit is broken until I can figure out how to make something map onto itself. + case MMap => "Windows::Foundation::Collections::IMap" case d: MDef => d.defType match { case DEnum => withNs(namespace, idCx.enumType(d.name)) From 6af9cc037375708bd9c63f36d1b49ea9f0d148e8 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 15:29:06 -0700 Subject: [PATCH 24/52] And everything is basically looking good! Time to check it out in Visual Studio --- src/source/CxCppGenerator.scala | 137 +++++++++++++++++++++++--------- src/source/CxCppMarshal.scala | 2 +- 2 files changed, 99 insertions(+), 40 deletions(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index f80f8f7d7..04a73fe88 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -28,6 +28,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val cxcppMarshal = new CxCppMarshal(spec) val cxMarshal = new CxMarshal(spec) + val cppMarshal = new CppMarshal(spec) val writeCxCppFile = writeCppFileGeneric(spec.cxcppOutFolder.get, spec.cxcppNamespace, spec.cppFileIdentStyle, spec.cxcppIncludePrefix, spec.cxcppExt, spec.cxcppHeaderExt) _ def writeHxFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) = @@ -157,34 +158,34 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { writeHxFile(cxcppMarshal.headerName(cxName), origin, refs.hx, refs.hxFwds, w => { w.wl - w.wl(s"struct $helperClass") - w.bracedSemi { - w.wl(s"using CppType = $cppSelf;") - w.wl(s"using CxType = $cxSelf^;"); - w.wl - w.wl(s"using Boxed = $helperClass;") - w.wl - w.wl(s"static CppType toCpp(CxType cxc);") - w.wl(s"static CxType fromCpp(const CppType& cpp);") - } + w.wl(s"struct $helperClass") + w.bracedSemi { + w.wl(s"using CppType = $cppSelf;") + w.wl(s"using CxType = $cxSelf^;"); + w.wl + w.wl(s"using Boxed = $helperClass;") + w.wl + w.wl(s"static CppType toCpp(CxType cxc);") + w.wl(s"static CxType fromCpp(const CppType& cpp);") + } }) writeCxCppFile(cxcppMarshal.bodyName(cxName), origin, refs.cxcpp, w => { - w.wl(s"auto $helperClass::toCpp(CxType cx) -> CppType") - w.braced { - w.wl("assert(cx);") - if(r.fields.isEmpty) w.wl("(void)cx; // Suppress warnings in relase builds for empty records") - writeAlignedCall(w, "return {", r.fields, "}", f => cxcppMarshal.toCpp(f.ty, "cx->" + idCx.field(f.ident))) - w.wl(";") - } - w.wl - w.wl(s"auto $helperClass::fromCpp(const CppType& cpp) -> CxType") - w.braced { - if(r.fields.isEmpty) w.wl("(void)cpp; // Suppress warnings in relase builds for empty records") - val first = if(r.fields.isEmpty) "" else IdentStyle.camelUpper("with_" + r.fields.head.ident.name) - writeAlignedCall(w, "return ref new CxType(", r.fields, ")", f=> cxcppMarshal.fromCpp(f.ty, "cpp." + idCpp.field(f.ident))) - w.wl(";") - } + w.wl(s"auto $helperClass::toCpp(CxType cx) -> CppType") + w.braced { + w.wl("assert(cx);") + if(r.fields.isEmpty) w.wl("(void)cx; // Suppress warnings in relase builds for empty records") + writeAlignedCall(w, "return {", r.fields, "}", f => cxcppMarshal.toCpp(f.ty, "cx->" + idCx.field(f.ident))) + w.wl(";") + } + w.wl + w.wl(s"auto $helperClass::fromCpp(const CppType& cpp) -> CxType") + w.braced { + if(r.fields.isEmpty) w.wl("(void)cpp; // Suppress warnings in relase builds for empty records") + val first = if(r.fields.isEmpty) "" else IdentStyle.camelUpper("with_" + r.fields.head.ident.name) + writeAlignedCall(w, "return ref new CxType(", r.fields, ")", f=> cxcppMarshal.fromCpp(f.ty, "cpp." + idCpp.field(f.ident))) + w.wl(";") + } }) } @@ -205,27 +206,85 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { writeHxFile(cxcppMarshal.headerName(ident.name), origin, refs.hx, refs.hxFwds, w => { w.wl - w.wl(s"struct $helperClass") - w.bracedSemi { - w.wl(s"using CppType = $cppSelf;") - w.wl(s"using CxType = $cxSelf^;"); - w.wl - w.wl(s"using Boxed = $helperClass;") + w.wl(s"class $self") + w.bracedSemi { + w.wlOutdent("public:") + w.wl(s"using CppType = $cppSelf;") + w.wl(s"using CxType = $cxSelf^;"); + w.wl + w.wl(s"using Boxed = $self;") + w.wl + w.wl(s"static CppType toCpp(CxType cxc);") + w.wl(s"static CxType fromCpp(const CppType& cpp);") + if (i.ext.cx) { w.wl - w.wl(s"static CppType toCpp(CxType cxc);") - w.wl(s"static CxType fromCpp(const CppType& cpp);") + w.wlOutdent("private:") + w.wl(s"class CxProxy;") } + + } + }) - // CxCpp only generated in need of Constants - if (i.consts.nonEmpty) { - writeCxCppFile(cxcppMarshal.bodyName(ident.name), origin, refs.cxcpp, w => { - generateCxCppConstants(w, i.consts, self) - }) - } + writeCxCppFile(cxcppMarshal.bodyName(ident.name), origin, refs.cxcpp, w => { + //only interface classes have proxy objects + if (i.ext.cx) { + w.wl(s"class $helperClass final : public $cppSelf, public ::djinni::CxWrapperCache::Handle").braced { + w.wlOutdent("public:") + w.wl("using Handle::Handle;") + w.wl(s"using CxType = $cxSelf") + w.wl + w.wl("CxProxy(Platform::Object^ cx) : ::djinni::CxWrapperCache::Handle{ cx } {}") + w.wl + //methods + for (m <- i.methods) { + w.wl + writeDoc(w, m.doc) + val ret = cppMarshal.returnType(m.ret) + val params = m.params.map(p => cppMarshal.paramType(p.ty) + " " + idCpp.local(p.ident)) + if (m.static) { + w.wl(s"static $ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")} override") + } else { + val constFlag = if (m.const) " const" else "" + w.wl(s"$ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag override") + } + w.braced { + val call = (if (!m.static) "auto r = static_cast(Handle::get())->" else cppSelf + "::") + idCpp.method(m.ident) + "(" + writeAlignedCall(w, call, m.params, ")", p => cxcppMarshal.fromCpp(p.ty, idCpp.local(p.ident.name))) + w.wl(";") + m.ret.fold()(r => w.wl(s"return ${cxcppMarshal.toCpp(r, "r")};")) + } + } + } + w.wl + } + + if (i.consts.nonEmpty) { + generateCxCppConstants(w, i.consts, self) + w.wl + } + w.wl(s"auto $self::toCpp(CxType^ cx) -> CppType") + w.braced { + w.wl("if (!cx)").braced { + w.wl("return nullptr;") + } + w.wl("return ::djinni::CxWrapperCache::getInstance()->get(cx);") + } + w.wl + w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType^") + w.braced { + w.braced { + w.wl("if (!cpp)").braced { + w.wl("return nullptr;") + } + w.wl("return static_cast(dynamic_cast(*cpp).Handle::get());") + } + } + }) } + def writeCxCppTypeParams(w: IndentWriter, params: Seq[TypeParam]) { if (params.isEmpty) return w.wl("template " + params.map(p => "typename " + idCpp.typeParam(p.ident)).mkString("<", ", ", ">")) diff --git a/src/source/CxCppMarshal.scala b/src/source/CxCppMarshal.scala index e926abb05..9d1fcbf30 100644 --- a/src/source/CxCppMarshal.scala +++ b/src/source/CxCppMarshal.scala @@ -54,7 +54,7 @@ class CxCppMarshal(spec: Spec) extends Marshal(spec) { case p: MParam => List() } - def helperClass(name: String) = idCpp.ty(name) + def helperClass(name: String) = s"${idCpp.ty(name)}Proxy" private def helperClass(tm: MExpr): String = helperName(tm) + helperTemplates(tm) def headerName(ident: String): String = idCx.ty(ident) + "_convert" From c0317dceb101d7683ed552bc19f23c0fecded800 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 15:55:42 -0700 Subject: [PATCH 25/52] Records seem to be coming out right now in Cx land --- src/source/CxCppGenerator.scala | 2 +- src/source/CxGenerator.scala | 13 ++++++------- src/source/generator.scala | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index 04a73fe88..d72f15b89 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -230,7 +230,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { //only interface classes have proxy objects if (i.ext.cx) { - w.wl(s"class $helperClass final : public $cppSelf, public ::djinni::CxWrapperCache::Handle").braced { + w.wl(s"class $helperClass sealed : public $cppSelf, public ::djinni::CxWrapperCache::Handle").braced { w.wlOutdent("public:") w.wl("using Handle::Handle;") w.wl(s"using CxType = $cxSelf") diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index 3d52f8e43..5f51e2b2f 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -138,7 +138,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { r.consts.foreach(c => refs.find(c.ty)) val self = cxMarshal.typename(ident, r) - val (cxName, cxFinal) = if (r.ext.cx) (ident.name + "_base", "") else (ident.name, " final") + val (cxName, cxFinal) = if (r.ext.cx) (ident.name + "_base", "") else (ident.name, " sealed") val actualSelf = cxMarshal.typename(cxName, r) // Requiring the extended class @@ -156,18 +156,17 @@ class CxGenerator(spec: Spec) extends Generator(spec) { // Field definitions. for (f <- r.fields) { writeDoc(w, f.doc) - w.wl(cxMarshal.fieldType(f.ty) + " " + idCx.field(f.ident) + ";") + w.wl("property " + cxMarshal.fieldType(f.ty) + " " + idCx.field(f.ident) + ";") } // Constructor. if (r.fields.nonEmpty) { w.wl - writeAlignedCall(w, actualSelf + "(", r.fields, ")", f => cxMarshal.fieldType(f.ty) + " " + idCx.local(f.ident)) + writeAlignedCall(w, actualSelf + "(", r.fields, ")", f => cxMarshal.fieldType(f.ty) + " " + idCx.local("new_"+f.ident.name)) w.wl - val init = (f: Field) => idCx.field(f.ident) + "(" + idCx.local(f.ident) + ")" - w.wl(": " + init(r.fields.head)) - r.fields.tail.map(f => ", " + init(f)).foreach(w.wl) - w.wl("{}") + w.braced { + r.fields.map(f => w.wl(s"${idCx.local(f.ident)} = ${idCx.local("new_"+f.ident.name)};") ) + } } if (r.derivingTypes.contains(DerivingType.Eq)) { diff --git a/src/source/generator.scala b/src/source/generator.scala index 85d2d5617..d772e1229 100644 --- a/src/source/generator.scala +++ b/src/source/generator.scala @@ -132,7 +132,7 @@ package object generatorTools { val javaDefault = JavaIdentStyle(camelUpper, camelUpper, camelLower, camelLower, camelLower, underCaps, underCaps) val cppDefault = CppIdentStyle(camelUpper, camelUpper, camelUpper, underLower, underLower, underLower, underCaps, underCaps) val objcDefault = ObjcIdentStyle(camelUpper, camelUpper, camelLower, camelLower, camelLower, camelUpper, camelUpper) - val cxDefault = CxIdentStyle(camelUpper, camelUpper, camelUpper, camelUpper, underLower, underLower, camelUpper, underCaps) + val cxDefault = CxIdentStyle(camelUpper, camelUpper, camelUpper, camelUpper, camelLower, camelLower, camelUpper, underCaps) val styles = Map( "FooBar" -> camelUpper, From a43b010e6979a4347ad8f8d8491c32331dff87fa Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 17:22:32 -0700 Subject: [PATCH 26/52] The Cx code is getting cleaner and cleaner. Still some lingering issues in the C++/Cx code. --- src/source/CxGenerator.scala | 15 ++++++++------- src/source/CxMarshal.scala | 6 +++--- src/source/meta.scala | 16 ++++++++-------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index 5f51e2b2f..0a198fd75 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -92,7 +92,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { for (c <- consts) { w.wl writeDoc(w, c.doc) - w.wl(s"static ${cxMarshal.fieldType(c.ty)} const ${idCx.const(c.ident)};") + w.wl(s"static const ${cxMarshal.fieldType(c.ty)} ${idCx.const(c.ident)};") } } @@ -233,6 +233,9 @@ class CxGenerator(spec: Spec) extends Generator(spec) { override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) { val refs = new CxRefs(ident.name) + refs.hx.add("#include ") + refs.hx.add("#include ") + refs.hx.add("#include \""+spec.cppIncludePrefix + spec.cppFileIdentStyle(ident.name) + "." + spec.cppHeaderExt+"\"") i.methods.map(m => { m.params.map(p => refs.find(p.ty)) m.ret.foreach(refs.find) @@ -249,10 +252,9 @@ class CxGenerator(spec: Spec) extends Generator(spec) { writeCxTypeParams(w, typeParams) if (i.ext.cx) w.wl(s"public interface class $self").bracedSemi { // Constants - generateHxConstants(w, i.consts) +// generateHxConstants(w, i.consts) //TODO no can do! Not gonna happen. Nuuh. We can make this a property with no setter and agetter that reaches into C++ land tho // Methods for (m <- i.methods) { - w.wl writeDoc(w, m.doc) val ret = cxMarshal.returnType(m.ret) val params = m.params.map(p => cxMarshal.paramType(p.ty) + " " + idCx.local(p.ident)) @@ -291,11 +293,10 @@ class CxGenerator(spec: Spec) extends Generator(spec) { // Cx only generated in need of Constants writeCxFile(ident, origin, refs.cx, w => { - if (i.consts.nonEmpty) { - generateCxConstants(w, i.consts, self) - } - if (! i.ext.cx) { + if (i.consts.nonEmpty) { + generateCxConstants(w, i.consts, self) + } //constructor w.wl(s"$self::$self(const std::shared_ptr<$cppSelf>&)cppRef)") w.braced { diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index 612aa2f22..825804c6d 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -100,7 +100,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { } case DInterface => if (d.name != exclude) { - List(DeclRef(s"class ${typename(d.name, d.body)};", Some(spec.cxNamespace))) + List(ImportRef(q(spec.cxIncludePrefix + spec.cxFileIdentStyle(d.name) + "." + spec.cxHeaderExt))) } else { List() } @@ -111,7 +111,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { private def toCxType(ty: TypeRef, namespace: Option[String] = None): String = toCxType(ty.resolved, namespace) private def toCxType(tm: MExpr, namespace: Option[String]): String = { def base(m: Meta): String = m match { - case p: MPrimitive => p.cName + case p: MPrimitive => p.cxName case MString => "Platform::String^" case MDate => "Windows::Foundation::DateTime^" case MBinary => "Platform::Array^" //no uint8_t in Cx @@ -141,7 +141,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { // this can be used in c++ generation to know whether a const& should be applied to the parameter or not private def toCxParamType(tm: MExpr, namespace: Option[String] = None): String = { val cxType = toCxType(tm, namespace) - val refType = "const " + cxType + val refType = cxType val valueType = cxType def toType(expr: MExpr): String = expr.base match { diff --git a/src/source/meta.scala b/src/source/meta.scala index 13ab7ba53..e7e350ca9 100644 --- a/src/source/meta.scala +++ b/src/source/meta.scala @@ -74,7 +74,7 @@ case object DEnum extends DefType case object DInterface extends DefType case object DRecord extends DefType -case class MPrimitive(_idlName: String, jName: String, jniName: String, cName: String, jBoxed: String, jSig: String, objcName: String, objcBoxed: String) extends MOpaque { val numParams = 0; val idlName = _idlName } +case class MPrimitive(_idlName: String, jName: String, jniName: String, cName: String, jBoxed: String, jSig: String, objcName: String, objcBoxed: String, cxName: String, cxBoxed: String) extends MOpaque { val numParams = 0; val idlName = _idlName } case object MString extends MOpaque { val numParams = 0; val idlName = "string" } case object MDate extends MOpaque { val numParams = 0; val idlName = "date" } case object MBinary extends MOpaque { val numParams = 0; val idlName = "binary" } @@ -84,13 +84,13 @@ case object MSet extends MOpaque { val numParams = 1; val idlName = "set" } case object MMap extends MOpaque { val numParams = 2; val idlName = "map" } val defaults: Map[String,MOpaque] = immutable.HashMap( - ("i8", MPrimitive("i8", "byte", "jbyte", "int8_t", "Byte", "B", "int8_t", "NSNumber")), - ("i16", MPrimitive("i16", "short", "jshort", "int16_t", "Short", "S", "int16_t", "NSNumber")), - ("i32", MPrimitive("i32", "int", "jint", "int32_t", "Integer", "I", "int32_t", "NSNumber")), - ("i64", MPrimitive("i64", "long", "jlong", "int64_t", "Long", "J", "int64_t", "NSNumber")), - ("f32", MPrimitive("f32", "float", "jfloat", "float", "Float", "F", "float", "NSNumber")), - ("f64", MPrimitive("f64", "double", "jdouble", "double", "Double", "D", "double", "NSNumber")), - ("bool", MPrimitive("bool", "boolean", "jboolean", "bool", "Boolean", "Z", "BOOL", "NSNumber")), + ("i8", MPrimitive("i8", "byte", "jbyte", "int8_t", "Byte", "B", "int8_t", "NSNumber", "int16", "Platform::Object^")), + ("i16", MPrimitive("i16", "short", "jshort", "int16_t", "Short", "S", "int16_t", "NSNumber", "int16", "Platform::Object^")), + ("i32", MPrimitive("i32", "int", "jint", "int32_t", "Integer", "I", "int32_t", "NSNumber", "int32", "Platform::Object^")), + ("i64", MPrimitive("i64", "long", "jlong", "int64_t", "Long", "J", "int64_t", "NSNumber", "int64", "Platform::Object^")), + ("f32", MPrimitive("f32", "float", "jfloat", "float", "Float", "F", "float", "NSNumber", "float32", "Platform::Object^")), + ("f64", MPrimitive("f64", "double", "jdouble", "double", "Double", "D", "double", "NSNumber", "float64", "Platform::Object^")), + ("bool", MPrimitive("bool", "boolean", "jboolean", "bool", "Boolean", "Z", "BOOL", "NSNumber", "bool", "Platform::Object^")), ("string", MString), ("binary", MBinary), ("optional", MOptional), From b162ba9060530539b28360ad863a9880204b1ac0 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 17 Jul 2015 17:29:31 -0700 Subject: [PATCH 27/52] So many bugs dead. --- src/source/CxCppGenerator.scala | 18 ++++++++++-------- src/source/CxMarshal.scala | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index d72f15b89..7fdf7ec88 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -139,7 +139,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val cxName = ident.name + (if (r.ext.cx) "_base" else "") val cxSelf = cxMarshal.fqTypename(ident, r) - val cppSelf = cxcppMarshal.fqTypename(ident, r) + val cppSelf = cppMarshal.fqTypename(ident, r) refs.hx.add("!#include " + q(spec.cxcppIncludeCxPrefix + (if(r.ext.cx) "../" else "") + cxcppMarshal.headerName(ident))) refs.hx.add("!#include " + q(spec.cxcppIncludeCppPrefix + (if(r.ext.cpp) "../" else "") + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt)) @@ -161,11 +161,11 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.wl(s"struct $helperClass") w.bracedSemi { w.wl(s"using CppType = $cppSelf;") - w.wl(s"using CxType = $cxSelf^;"); + w.wl(s"using CxType = $cxSelf;"); w.wl w.wl(s"using Boxed = $helperClass;") w.wl - w.wl(s"static CppType toCpp(CxType cxc);") + w.wl(s"static CppType toCpp(CxType^ cx);") w.wl(s"static CxType fromCpp(const CppType& cpp);") } }) @@ -191,6 +191,8 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) { val refs = new CxCppRefs(ident.name) + refs.hx.add("#include \""+spec.cppIncludePrefix + spec.cppFileIdentStyle(ident.name) + "." + spec.cppHeaderExt+"\"") + refs.hx.add("#include \""+spec.cxIncludePrefix + spec.cxFileIdentStyle(ident.name) + "." + spec.cxHeaderExt+"\"") i.methods.map(m => { m.params.map(p => refs.find(p.ty)) m.ret.foreach(refs.find) @@ -200,8 +202,8 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { }) val self = cxcppMarshal.typename(ident, i) - val cxSelf = cxMarshal.fqTypename(ident, i) - val cppSelf = cxcppMarshal.fqTypename(ident, i) + val cxSelf = if(i.ext.cx) cxMarshal.fqTypename(ident, i) + val cppSelf = cppMarshal.fqTypename(ident, i) val helperClass = cxcppMarshal.helperClass(ident) writeHxFile(cxcppMarshal.headerName(ident.name), origin, refs.hx, refs.hxFwds, w => { @@ -210,12 +212,12 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.bracedSemi { w.wlOutdent("public:") w.wl(s"using CppType = $cppSelf;") - w.wl(s"using CxType = $cxSelf^;"); + w.wl(s"using CxType = $cxSelf;"); w.wl w.wl(s"using Boxed = $self;") w.wl - w.wl(s"static CppType toCpp(CxType cxc);") - w.wl(s"static CxType fromCpp(const CppType& cpp);") + w.wl(s"static CppType toCpp(CxType^ cx);") + w.wl(s"static CxType^ fromCpp(const CppType& cpp);") if (i.ext.cx) { w.wl w.wlOutdent("private:") diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index 825804c6d..05314e9b1 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -18,7 +18,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { override def fqTypename(tm: MExpr): String = toCxType(tm, Some(spec.cxNamespace)) def fqTypename(name: String, ty: TypeDef): String = ty match { case e: Enum => withNs(Some(spec.cxNamespace), idCx.enumType(name)) - case i: Interface => withNs(Some(spec.cxNamespace), idCx.ty(name)) + case i: Interface => if(i.ext.cx) withNs(Some(spec.cxNamespace), s"I${idCx.ty(name)}") else withNs(Some(spec.cxNamespace), idCx.ty(name)) case r: Record => withNs(Some(spec.cxNamespace), idCx.ty(name)) } From 49e9dfb388a4d4a265b1689128f5e7c3b0338745 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Mon, 20 Jul 2015 16:42:37 -0700 Subject: [PATCH 28/52] Now works with a simple test project. Lots of work to go, but we now have basic C++/Cx support. Ask me how! --- src/source/CxCppGenerator.scala | 78 +++++++++++++++++++-------------- src/source/CxCppMarshal.scala | 57 +++++++++++++++++++++--- src/source/CxGenerator.scala | 51 ++++++++++++++------- src/source/CxMarshal.scala | 23 ++++++++++ support-lib/cx/Marshal.h | 2 +- support-lib/support-lib.iml | 3 +- 6 files changed, 157 insertions(+), 57 deletions(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index 7fdf7ec88..e1a5e319c 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -141,11 +141,12 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val cxSelf = cxMarshal.fqTypename(ident, r) val cppSelf = cppMarshal.fqTypename(ident, r) - refs.hx.add("!#include " + q(spec.cxcppIncludeCxPrefix + (if(r.ext.cx) "../" else "") + cxcppMarshal.headerName(ident))) - refs.hx.add("!#include " + q(spec.cxcppIncludeCppPrefix + (if(r.ext.cpp) "../" else "") + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt)) + refs.hx.add("#include " + q(spec.cxcppIncludeCxPrefix + (if(r.ext.cx) "../" else "") + cxcppMarshal.headerName(ident)+ "." + spec.cxcppHeaderExt)) + refs.hx.add("#include " + q(spec.cxIncludePrefix + (if(r.ext.cx) "../" else "") + spec.cxFileIdentStyle(ident) + "." + spec.cxHeaderExt)) + refs.hx.add("#include " + q(spec.cxcppIncludeCppPrefix + (if(r.ext.cpp) "../" else "") + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt)) + refs.cxcpp = refs.hx.clone() refs.cxcpp.add("#include ") - refs.cxcpp.add("!#import " + q(spec.cxcppIncludePrefix + cxcppMarshal.headerName(cxName))) def checkMutable(tm: MExpr): Boolean = tm.base match { case MOptional => checkMutable(tm.args.head) @@ -154,24 +155,24 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { case _ => false } - val helperClass = cxcppMarshal.helperClass(ident) + val self = cxcppMarshal.typename(ident, r) writeHxFile(cxcppMarshal.headerName(cxName), origin, refs.hx, refs.hxFwds, w => { w.wl - w.wl(s"struct $helperClass") + w.wl(s"struct $self") w.bracedSemi { w.wl(s"using CppType = $cppSelf;") w.wl(s"using CxType = $cxSelf;"); w.wl - w.wl(s"using Boxed = $helperClass;") + w.wl(s"using Boxed = $self;") w.wl w.wl(s"static CppType toCpp(CxType^ cx);") - w.wl(s"static CxType fromCpp(const CppType& cpp);") + w.wl(s"static CxType^ fromCpp(const CppType& cpp);") } }) - writeCxCppFile(cxcppMarshal.bodyName(cxName), origin, refs.cxcpp, w => { - w.wl(s"auto $helperClass::toCpp(CxType cx) -> CppType") + writeCxCppFile(cxcppMarshal.bodyName(self), origin, refs.cxcpp, w => { + w.wl(s"auto $self::toCpp(CxType^ cx) -> CppType") w.braced { w.wl("assert(cx);") if(r.fields.isEmpty) w.wl("(void)cx; // Suppress warnings in relase builds for empty records") @@ -179,10 +180,9 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.wl(";") } w.wl - w.wl(s"auto $helperClass::fromCpp(const CppType& cpp) -> CxType") + w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType^") w.braced { if(r.fields.isEmpty) w.wl("(void)cpp; // Suppress warnings in relase builds for empty records") - val first = if(r.fields.isEmpty) "" else IdentStyle.camelUpper("with_" + r.fields.head.ident.name) writeAlignedCall(w, "return ref new CxType(", r.fields, ")", f=> cxcppMarshal.fromCpp(f.ty, "cpp." + idCpp.field(f.ident))) w.wl(";") } @@ -193,6 +193,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val refs = new CxCppRefs(ident.name) refs.hx.add("#include \""+spec.cppIncludePrefix + spec.cppFileIdentStyle(ident.name) + "." + spec.cppHeaderExt+"\"") refs.hx.add("#include \""+spec.cxIncludePrefix + spec.cxFileIdentStyle(ident.name) + "." + spec.cxHeaderExt+"\"") + refs.hx.add("#include ") i.methods.map(m => { m.params.map(p => refs.find(p.ty)) m.ret.foreach(refs.find) @@ -201,8 +202,10 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { refs.find(c.ty) }) + refs.cxcpp.add("#include \"CxWrapperCache.h\"") + val self = cxcppMarshal.typename(ident, i) - val cxSelf = if(i.ext.cx) cxMarshal.fqTypename(ident, i) + val cxSelf = cxMarshal.fqTypename(ident, i) val cppSelf = cppMarshal.fqTypename(ident, i) val helperClass = cxcppMarshal.helperClass(ident) @@ -211,7 +214,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.wl(s"class $self") w.bracedSemi { w.wlOutdent("public:") - w.wl(s"using CppType = $cppSelf;") + w.wl(s"using CppType = std::shared_ptr<$cppSelf>;") w.wl(s"using CxType = $cxSelf;"); w.wl w.wl(s"using Boxed = $self;") @@ -232,10 +235,10 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { //only interface classes have proxy objects if (i.ext.cx) { - w.wl(s"class $helperClass sealed : public $cppSelf, public ::djinni::CxWrapperCache::Handle").braced { + w.wl(s"class $helperClass final : public $cppSelf, public ::djinni::CxWrapperCache::Handle").bracedSemi { w.wlOutdent("public:") w.wl("using Handle::Handle;") - w.wl(s"using CxType = $cxSelf") + w.wl(s"using CxType = $cxSelf;") w.wl w.wl("CxProxy(Platform::Object^ cx) : ::djinni::CxWrapperCache::Handle{ cx } {}") w.wl @@ -252,7 +255,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.wl(s"$ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag override") } w.braced { - val call = (if (!m.static) "auto r = static_cast(Handle::get())->" else cppSelf + "::") + idCpp.method(m.ident) + "(" + val call = (if (!m.static) "auto r = static_cast(Handle::get())->" else cppSelf + "::") + idCx.method(m.ident) + "(" writeAlignedCall(w, call, m.params, ")", p => cxcppMarshal.fromCpp(p.ty, idCpp.local(p.ident.name))) w.wl(";") m.ret.fold()(r => w.wl(s"return ${cxcppMarshal.toCpp(r, "r")};")) @@ -260,29 +263,38 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { } } w.wl - } - - if (i.consts.nonEmpty) { - generateCxCppConstants(w, i.consts, self) + w.wl(s"auto $self::toCpp(CxType^ cx) -> CppType") + w.braced { + w.wl("if (!cx)").braced { + w.wl("return nullptr;") + } + w.wl("return ::djinni::CxWrapperCache::getInstance()->get(cx);") + } w.wl - } - w.wl(s"auto $self::toCpp(CxType^ cx) -> CppType") - w.braced { - w.wl("if (!cx)").braced { - w.wl("return nullptr;") + w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType^") + w.braced { + w.braced { + w.wl("if (!cpp)").braced { + w.wl("return nullptr;") + } + w.wl("return static_cast(dynamic_cast(*cpp).Handle::get());") + } } - w.wl("return ::djinni::CxWrapperCache::getInstance()->get(cx);") - } - w.wl - w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType^") - w.braced { + } else { + w.wl(s"auto $self::toCpp(CxType^ cx) -> CppType") w.braced { - w.wl("if (!cpp)").braced { - w.wl("return nullptr;") + w.wl("return cx->m_cppRef.get();") + } + w.wl + w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType^") + w.braced { + w.wl(s"return (CxType^)::djinni::CppWrapperCache<$cppSelf>::getInstance()->get(cpp, [](const std::shared_ptr<$cppSelf>& p)").bracedEnd(");") { + w.wl(s"return ref new $cxSelf(p);") } - w.wl("return static_cast(dynamic_cast(*cpp).Handle::get());") + } } + }) } diff --git a/src/source/CxCppMarshal.scala b/src/source/CxCppMarshal.scala index 9d1fcbf30..f1cddf941 100644 --- a/src/source/CxCppMarshal.scala +++ b/src/source/CxCppMarshal.scala @@ -32,10 +32,10 @@ class CxCppMarshal(spec: Spec) extends Marshal(spec) { override def fqFieldType(tm: MExpr): String = fqTypename(tm) override def toCpp(tm: MExpr, expr: String): String = { - s"${helperClass(tm)}::toCpp($expr)" + s"${ownClass(tm)}::toCpp($expr)" } override def fromCpp(tm: MExpr, expr: String): String = { - s"${helperClass(tm)}::fromCpp($expr)" + s"${ownClass(tm)}::fromCpp($expr)" } def references(m: Meta): Seq[SymbolReference] = m match { @@ -45,16 +45,63 @@ class CxCppMarshal(spec: Spec) extends Marshal(spec) { case DEnum => List(ImportRef(q(spec.cxBaseLibIncludePrefix + "Marshal.h"))) case DInterface => - List(ImportRef(q(spec.cxcppIncludePrefix + headerName(d.name)))) + List(ImportRef(q(spec.cxcppIncludePrefix + headerName(d.name) + "." + spec.cxcppHeaderExt))) case DRecord => val r = d.body.asInstanceOf[Record] val cxName = d.name + (if (r.ext.cx) "_base" else "") - List(ImportRef(q(spec.cxcppIncludePrefix + headerName(cxName)))) + List(ImportRef(q(spec.cxcppIncludePrefix + headerName(cxName) + "." + spec.cxcppHeaderExt))) } case p: MParam => List() } - def helperClass(name: String) = s"${idCpp.ty(name)}Proxy" + def ownClass(name: String) = s"${idCpp.ty(name)}" + private def ownClass(tm: MExpr): String = ownName(tm) + ownTemplates(tm) + + private def ownName(tm: MExpr): String = tm.base match { + case d: MDef => d.defType match { + case DEnum => withNs(Some("djinni"), s"Enum<${cppMarshal.fqTypename(tm)}, ${cxMarshal.fqTypename(tm)}>") + case _ => withNs(Some(spec.cxcppNamespace), ownClass(d.name)) + } + case o => withNs(Some("djinni"), o match { + case p: MPrimitive => p.idlName match { + case "i8" => "I8" + case "i16" => "I16" + case "i32" => "I32" + case "i64" => "I64" + case "f32" => "F32" + case "f64" => "F64" + case "bool" => "Bool" + } + case MOptional => "Optional" + case MBinary => "Binary" + case MDate => "Date" + case MString => "String" + case MList => "List" + case MSet => "Set" + case MMap => "Map" + case d: MDef => throw new AssertionError("unreachable") + case p: MParam => throw new AssertionError("not applicable") + }) + } + + private def ownTemplates(tm: MExpr): String = { + def f() = if(tm.args.isEmpty) "" else tm.args.map(ownClass).mkString("<", ", ", ">") + tm.base match { + case MOptional => + assert(tm.args.size == 1) + val argHelperClass = ownClass(tm.args.head) + s"<${spec.cppOptionalTemplate}, $argHelperClass>" + case MList | MSet => + assert(tm.args.size == 1) + f + case MMap => + assert(tm.args.size == 2) + f + case _ => f + } + } + + def helperClass(name: String) = s"${idCpp.ty(name)}::CxProxy" private def helperClass(tm: MExpr): String = helperName(tm) + helperTemplates(tm) def headerName(ident: String): String = idCx.ty(ident) + "_convert" diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index 0a198fd75..768d8b62e 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -27,6 +27,7 @@ import scala.collection.mutable class CxGenerator(spec: Spec) extends Generator(spec) { val cxMarshal = new CxMarshal(spec) + val cxcppMarshal = new CxCppMarshal(spec) val cppMarshal = new CppMarshal(spec) val writeCxFile = writeCppFileGeneric(spec.cxOutFolder.get, spec.cxNamespace, spec.cxFileIdentStyle, spec.cxIncludePrefix, spec.cxExt, spec.cxHeaderExt) _ @@ -48,13 +49,22 @@ class CxGenerator(spec: Spec) extends Generator(spec) { case DeclRef(decl, Some(spec.cxNamespace)) => hxFwds.add(decl) case DeclRef(_, _) => } + + def findConvert(ty: TypeRef) { findConvert(ty.resolved) } + def findConvert(tm: MExpr) { + tm.args.foreach(find) + findConvert(tm.base) + } + def findConvert(m: Meta) = for(r <- cxMarshal.convertReferences(m, name)) r match { + case ImportRef(arg) => cx.add("#include " + arg) + } } - def writeCxFuncDecl(method: Interface.Method, w: IndentWriter) { - val label = if (method.static) "static " else "" - val ret = cxMarshal.fqReturnType(method.ret) - val decl = s"$label ($ret)${idObjc.method(method.ident)}" - writeAlignedCall(w, decl, method.params, "X", p => s"(${cxMarshal.paramType(p.ty)})${idObjc.local(p.ident)}") + def writeCxFuncDecl(klass: String, method: Interface.Method, w: IndentWriter) { + val ret = cxMarshal.returnType(method.ret) + val params = method.params.map(p => cxMarshal.paramType(p.ty) + " " + idCx.local(p.ident)) + val constFlag = if (method.const) " const" else "" + w.wl(s"$ret $klass::${idCx.method(method.ident)}${params.mkString("(", ", ", ")")}") } override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { @@ -234,7 +244,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) { val refs = new CxRefs(ident.name) refs.hx.add("#include ") - refs.hx.add("#include ") + refs.hx.add("#include \"CppWrapperCache.h\"") refs.hx.add("#include \""+spec.cppIncludePrefix + spec.cppFileIdentStyle(ident.name) + "." + spec.cppHeaderExt+"\"") i.methods.map(m => { m.params.map(p => refs.find(p.ty)) @@ -244,6 +254,14 @@ class CxGenerator(spec: Spec) extends Generator(spec) { refs.find(c.ty) }) + refs.cx = refs.hx.clone() + i.methods.map(m => { + m.params.map(p => refs.findConvert(p.ty)) + m.ret.foreach(refs.findConvert) + }) + refs.cx.add("#include \"Marshal.h\"") + refs.cx.add("#include \""+ cxcppMarshal.headerName(ident.name) + "." + spec.cxcppHeaderExt + "\"") + val self = cxMarshal.typename(ident, i) val cppSelf = cppMarshal.fqTypename(ident, i) @@ -255,14 +273,15 @@ class CxGenerator(spec: Spec) extends Generator(spec) { // generateHxConstants(w, i.consts) //TODO no can do! Not gonna happen. Nuuh. We can make this a property with no setter and agetter that reaches into C++ land tho // Methods for (m <- i.methods) { + w.wl writeDoc(w, m.doc) val ret = cxMarshal.returnType(m.ret) - val params = m.params.map(p => cxMarshal.paramType(p.ty) + " " + idCx.local(p.ident)) + val params = m.params.map(p => cxMarshal.paramType(p.ty) + " " + idCpp.local(p.ident)) if (m.static) { w.wl(s"static $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")};") } else { val constFlag = if (m.const) " const" else "" - w.wl(s"$ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag;") + w.wl(s"virtual $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")} = 0;") } } } @@ -275,19 +294,19 @@ class CxGenerator(spec: Spec) extends Generator(spec) { w.wl writeDoc(w, m.doc) val ret = cxMarshal.returnType(m.ret) - val params = m.params.map(p => cxMarshal.paramType(p.ty) + " " + idCx.local(p.ident)) + val params = m.params.map(p => cxMarshal.paramType(p.ty) + " " + idCpp.local(p.ident)) if (m.static) { w.wl(s"static $ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")};") } else { val constFlag = if (m.const) " const" else "" - w.wl(s"$ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag;") + w.wl(s"$ret ${idCx.method(m.ident)}${params.mkString("(", ", ", ")")};") } } //private members w.wlOutdent("internal:") //construct from a cpp ref - w.wl(s"$self(const std::shared_ptr<$cppSelf>& m_cppRef);") - w.wl(s"::djinni::CppWrapperCache<$cppSelf>::Handle cppRef;") + w.wl(s"$self(const std::shared_ptr<$cppSelf>& cppRef);") + w.wl(s"::djinni::CppWrapperCache<$cppSelf>::Handle m_cppRef;") } }) @@ -298,16 +317,14 @@ class CxGenerator(spec: Spec) extends Generator(spec) { generateCxConstants(w, i.consts, self) } //constructor - w.wl(s"$self::$self(const std::shared_ptr<$cppSelf>&)cppRef)") + w.wl(s"$self::$self(const std::shared_ptr<$cppSelf>& cppRef)") w.braced { - w.w("if (self = [super init])").braced { - w.wl("m_cppRef.assign(cppRef);") - } + w.wl("m_cppRef.assign(cppRef);") } //methods for (m <- i.methods) { w.wl - writeCxFuncDecl(m, w) + writeCxFuncDecl(self, m, w) w.braced { // w.w("try").bracedEnd(" DJINNI_TRANSLATE_EXCEPTIONS()") { val ret = m.ret.fold("")(_ => "auto r = ") diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index 05314e9b1..03b13da89 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -108,6 +108,29 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { case p: MParam => List() } + def convertReferences(m: Meta, exclude: String): Seq[SymbolReference] = m match { + case p: MPrimitive => p.idlName match { + case "i8" | "i16" | "i32" | "i64" => List() + case _ => List() + } + case MString | MDate | MBinary | MOptional | MList | MSet | MMap => List() + case d: MDef => d.defType match { + case DEnum | DRecord => + if (d.name != exclude) { + List(ImportRef(q(spec.cxcppIncludePrefix + spec.cxFileIdentStyle(d.name) + "_convert." + spec.cxcppHeaderExt))) + } else { + List() + } + case DInterface => + if (d.name != exclude) { + List(ImportRef(q(spec.cxcppIncludePrefix + spec.cxFileIdentStyle(d.name) + "_convert." + spec.cxcppHeaderExt))) + } else { + List() + } + } + case p: MParam => List() + } + private def toCxType(ty: TypeRef, namespace: Option[String] = None): String = toCxType(ty.resolved, namespace) private def toCxType(tm: MExpr, namespace: Option[String]): String = { def base(m: Meta): String = m match { diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index 8900416d6..21274580f 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -209,7 +209,7 @@ namespace djinni { static CppType toCpp(CxType^ v) { assert(v); CppType nv; - for(ECxType val : v) + for(ECxType^ val : v) { nv.push_back(T::Boxed::toCpp(val)); } diff --git a/support-lib/support-lib.iml b/support-lib/support-lib.iml index 8021953ed..284429d94 100644 --- a/support-lib/support-lib.iml +++ b/support-lib/support-lib.iml @@ -6,4 +6,5 @@ - \ No newline at end of file + + From 8b0bccea392194c80248783f93ab7cdba0ac7b93 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Wed, 22 Jul 2015 18:13:17 -0700 Subject: [PATCH 29/52] Optional types for Cx --- support-lib/cx/Marshal.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index 21274580f..9cd164348 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -88,7 +88,7 @@ namespace djinni { }; -// template +// template // struct Enum { // using CppType = CppEnum; // using CxType = CxEnum; @@ -178,23 +178,23 @@ namespace djinni { // } // }; // - // template class OptionalType, class T> - // class Optional { - // public: - // using CppType = OptionalType; - // using CxType = typename T::Boxed::CxType; - // - // using Boxed = Optional; - // - // static CppType toCpp(CxType obj) { - // return obj ? CppType(T::Boxed::toCpp(obj)) : CppType(); - // } - // - // static CxType fromCpp(const CppType& opt) { - // return opt ? T::Boxed::fromCpp(*opt) : nil; - // } - // }; - // + template class OptionalType, class T> + class Optional { + public: + using CppType = OptionalType; + using CxType = typename T::Boxed::CxType; + + using Boxed = Optional; + + static CppType toCpp(CxType cx) { + return obj ? CppType(T::Boxed::toCpp(cx)) : CppType(); + } + + static CxType fromCpp(const CppType& opt) { + return opt ? T::Boxed::fromCpp(*opt) : nil; + } + }; + template class List { using ECppType = typename T::CppType; From 80367cd242a54efdab8d1b78bc2e212b0f43c05f Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Wed, 22 Jul 2015 18:32:41 -0700 Subject: [PATCH 30/52] Bugfixes --- src/source/ObjcMarshal.scala | 2 +- src/source/generator.scala | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/source/ObjcMarshal.scala b/src/source/ObjcMarshal.scala index 471b6697e..294366a2b 100644 --- a/src/source/ObjcMarshal.scala +++ b/src/source/ObjcMarshal.scala @@ -21,7 +21,7 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { val interfaceNullity = if (spec.cppNnType.nonEmpty) nonnull else nullable tm.base match { case MOptional => nullable - case MPrimitive(_,_,_,_,_,_,_,_) => None + case MPrimitive(_,_,_,_,_,_,_,_,_,_) => None case d: MDef => d.defType match { case DEnum => None case DInterface => interfaceNullity diff --git a/src/source/generator.scala b/src/source/generator.scala index d772e1229..fa709004f 100644 --- a/src/source/generator.scala +++ b/src/source/generator.scala @@ -237,6 +237,13 @@ package object generatorTools { } new CxCppGenerator(spec).generate(idl) } + + if (spec.yamlOutFolder.isDefined) { + if (!spec.skipGeneration) { + createFolder("YAML", spec.yamlOutFolder.get) + new YamlGenerator(spec).generate(idl) + } + } None } catch { From 60653f02d10adffbfec8e682141999e266d62991 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Thu, 23 Jul 2015 14:04:15 -0700 Subject: [PATCH 31/52] Proper boxing of primitive types --- support-lib/cx/Marshal.h | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index 9cd164348..ee9dea30e 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -44,47 +44,47 @@ namespace djinni { static CxType fromCpp(CppType x) { return x; } struct Boxed { - using CxType = Platform::Object^; - static CppType toCpp(CxType x) { assert(x); return static_cast(Self::unbox(x)); } - static CxType fromCpp(CppType x) { return Self::box(x); } + using CxType = Platform::Object; + static CppType toCpp(CxType^ x) { assert(x); return Self::unbox(x); } + static CxType^ fromCpp(CppType x) { return Self::box(x); } }; }; //stupid C++/Cx doesn't have int8_t; we'll pass it up as a uint8_t instead, and pray. - class I8 : public Primitive { - friend Primitive; - static int8_t unbox(Boxed::CxType x) { return safe_cast(x); } - static Boxed::CxType box(CppType x) { Platform::Object^ cx = (uint8_t)x; return cx; } + class I8 : public Primitive { + friend Primitive; + static int16 unbox(Boxed::CxType^ x) { return (uint8)x; } + static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = (uint8)x; return cx; } }; - class I16 : public Primitive { - friend Primitive; - static uint16_t unbox(Boxed::CxType x) { return safe_cast(x); } - static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } + class I16 : public Primitive { + friend Primitive; + static int16 unbox(Boxed::CxType^ x) { return (int16)x; } + static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = x; return cx; } }; - class I32 : public Primitive { - friend Primitive; - static int32_t unbox(Boxed::CxType x) { return safe_cast(x); } - static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } + class I32 : public Primitive { + friend Primitive; + static int32 unbox(Boxed::CxType^ x) { return (int32)x; } + static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = x; return cx; } }; - class I64 : public Primitive { - friend Primitive; - static int64_t unbox(Boxed::CxType x) { return safe_cast(x); } - static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } + class I64 : public Primitive { + friend Primitive; + static int64 unbox(Boxed::CxType^ x) { return (int64)x; } + static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = x; return cx; } }; class F32 : public Primitive { friend Primitive; - static float unbox(Boxed::CxType x) { return safe_cast(x); } - static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } + static float unbox(Boxed::CxType^ x) { return (float)x; } + static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = x; return cx; } }; class F64 : public Primitive { friend Primitive; - static double unbox(Boxed::CxType x) { return safe_cast(x); } - static Boxed::CxType box(CppType x) { Platform::Object^ cx = x; return cx; } + static double unbox(Boxed::CxType^ x) { return (double)x; } + static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = x; return cx; } }; From 8828393a4ea8d4c4bddfae34ee91d46947cfc41e Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Thu, 23 Jul 2015 16:22:18 -0700 Subject: [PATCH 32/52] Getting close to being able to do optional types right. --- src/source/CxCppMarshal.scala | 24 +++++++ src/source/CxMarshal.scala | 113 ++++++++++++++++++++------------- src/source/ObjcMarshal.scala | 2 +- src/source/YamlGenerator.scala | 28 +++++++- src/source/meta.scala | 27 +++++--- 5 files changed, 138 insertions(+), 56 deletions(-) diff --git a/src/source/CxCppMarshal.scala b/src/source/CxCppMarshal.scala index f1cddf941..56fb158bb 100644 --- a/src/source/CxCppMarshal.scala +++ b/src/source/CxCppMarshal.scala @@ -151,6 +151,30 @@ class CxCppMarshal(spec: Spec) extends Marshal(spec) { } } + def include(ident: String): String = q(spec.cxcppIncludePrefix + spec.cxFileIdentStyle(ident) + "." + spec.cxcppHeaderExt) + + + def byValue(tm: MExpr): Boolean = tm.base match { + case p: MPrimitive => true + case d: MDef => d.defType match { + case DEnum => true + case _ => false + } + case e: MExtern => e.defType match { + case DInterface => false + case DEnum => true + case DRecord => e.cxcpp.byValue + } + case MOptional => byValue(tm.args.head) + case _ => false + } + + def byValue(td: TypeDecl): Boolean = td.body match { + case i: Interface => false + case r: Record => false + case e: Enum => true + } + private def toCxCppType(ty: TypeRef, namespace: Option[String] = None): String = toCxCppType(ty.resolved, namespace) private def toCxCppType(tm: MExpr, namespace: Option[String]): String = { def base(m: Meta): String = m match { diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index 03b13da89..235fa2363 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -8,14 +8,14 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { private val cppMarshal = new CppMarshal(spec) - override def typename(tm: MExpr): String = toCxType(tm, None) + override def typename(tm: MExpr): String = toCxType(tm, None)._1 def typename(name: String, ty: TypeDef): String = ty match { case e: Enum => idCx.enumType(name) case i: Interface => if(i.ext.cx) s"I${idCx.ty(name)}" else idCx.ty(name) case r: Record => idCx.ty(name) } - override def fqTypename(tm: MExpr): String = toCxType(tm, Some(spec.cxNamespace)) + override def fqTypename(tm: MExpr): String = toCxType(tm, Some(spec.cxNamespace))._1 def fqTypename(name: String, ty: TypeDef): String = ty match { case e: Enum => withNs(Some(spec.cxNamespace), idCx.enumType(name)) case i: Interface => if(i.ext.cx) withNs(Some(spec.cxNamespace), s"I${idCx.ty(name)}") else withNs(Some(spec.cxNamespace), idCx.ty(name)) @@ -25,8 +25,8 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { override def paramType(tm: MExpr): String = toCxParamType(tm) override def fqParamType(tm: MExpr): String = toCxParamType(tm, Some(spec.cxNamespace)) - override def returnType(ret: Option[TypeRef]): String = ret.fold("void")(toCxType(_, None)) - override def fqReturnType(ret: Option[TypeRef]): String = ret.fold("void")(toCxType(_, Some(spec.cxNamespace))) + override def returnType(ret: Option[TypeRef]): String = ret.fold("void")((t: TypeRef) => toCxParamType(t.resolved)) + override def fqReturnType(ret: Option[TypeRef]): String = ret.fold("void")((t: TypeRef) => toCxParamType(t.resolved, Some(spec.cxNamespace))) override def fieldType(tm: MExpr): String = typename(tm) override def fqFieldType(tm: MExpr): String = fqTypename(tm) @@ -44,7 +44,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { private def helperName(tm: MExpr): String = tm.base match { case d: MDef => d.defType match { case DEnum => withNs(Some("djinni"), s"Enum<${cppMarshal.fqTypename(tm)}, ${fqTypename(tm)}>") - case _ => withNs(Some(spec.objcppNamespace), helperClass(d.name)) + case _ => withNs(Some(spec.cxcppNamespace), helperClass(d.name)) } case o => withNs(Some("djinni"), o match { case p: MPrimitive => p.idlName match { @@ -131,51 +131,74 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { case p: MParam => List() } - private def toCxType(ty: TypeRef, namespace: Option[String] = None): String = toCxType(ty.resolved, namespace) - private def toCxType(tm: MExpr, namespace: Option[String]): String = { - def base(m: Meta): String = m match { - case p: MPrimitive => p.cxName - case MString => "Platform::String^" - case MDate => "Windows::Foundation::DateTime^" - case MBinary => "Platform::Array^" //no uint8_t in Cx - case MOptional => "" - case MList => "Windows::Foundation::Collections::IVector" - case MSet => "Windows::Foundation::Collections::IMap" //no set in C++/Cx FOr now this shit is broken until I can figure out how to make something map onto itself. - case MMap => "Windows::Foundation::Collections::IMap" - case d: MDef => - d.defType match { - case DEnum => withNs(namespace, idCx.enumType(d.name)) - case DRecord => s"${withNs(namespace, idCx.ty(d.name))}^" - case DInterface => d.body match { - case e: Enum => s"${withNs(namespace, idCx.ty(d.name))}^" - case i: Interface => if(i.ext.cx) s"I${withNs(namespace, idCx.ty(d.name))}^" else s"${withNs(namespace, idCx.ty(d.name))}^" - case r: Record => s"${withNs(namespace, idCx.ty(d.name))}^" + def headerName(ident: String) = idCx.ty(ident) + "." + spec.cxHeaderExt + def include(ident: String) = q(spec.cxIncludePrefix + headerName(ident)) + + + def isReference(td: TypeDecl) = td.body match { + case i: Interface => true + case r: Record => true + case e: Enum => true + } + + def boxedTypename(td: TypeDecl) = td.body match { + case i: Interface => typename(td.ident, i) + case r: Record => typename(td.ident, r) + case e: Enum => "Platform::Object" + } + + + // Return value: (Type_Name, Is_Class_Or_Not) + def toCxType(ty: TypeRef, namespace: Option[String] = None): (String, Boolean) = toCxType(ty.resolved, namespace, false) + def toCxType(ty: TypeRef, namespace: Option[String], needRef: Boolean): (String, Boolean) = toCxType(ty.resolved, namespace, needRef) + def toCxType(tm: MExpr, namespace: Option[String]): (String, Boolean) = toCxType(tm, namespace, false) + def toCxType(tm: MExpr, namespace: Option[String], needRef: Boolean): (String, Boolean) = { + def f(tm: MExpr, needRef: Boolean): (String, Boolean) = { + tm.base match { + case MOptional => + // We use "nil" for the empty optional. + assert(tm.args.size == 1) + val arg = tm.args.head + arg.base match { + case MOptional => throw new AssertionError("nested optional?") + case m => f(arg, true) } - } - case p: MParam => idCx.typeParam(p.name) - } - def expr(tm: MExpr): String = { - val args = if (tm.args.isEmpty) "" else tm.args.map(expr).mkString("<", ", ", ">^") - base(tm.base) + args + case o => + val base = o match { + case p: MPrimitive => if (needRef) (p.cxBoxed, true) else (p.cxName, false) + case MString => ("Platform::String", true) + case MDate => ("Windows::Foundation::DateTime", true) + case MBinary => ("Platform::Array", true) + case MOptional => throw new AssertionError("optional should have been special cased") + case MList => ("Windows::Foundation::Collections::IVector", true) + case MSet => ("Windows::Foundation::Collections::IMap", true) //no set in C++/Cx FOr now this shit is broken until I can figure out how to make something map onto itself. + case MMap => ("Windows::Foundation::Collections::IMap", true) + case d: MDef => d.defType match { + case DEnum => (idCx.ty(d.name), true) + case DRecord => (idCx.ty(d.name), true) + case DInterface => + val ext = d.body.asInstanceOf[Interface].ext + if (ext.cpp && !ext.cx) + (idCx.ty(d.name), true) + else + (s"I${withNs(namespace, idCx.ty(d.name))}", true) + } + case e: MExtern => e.body match { + case i: Interface => if(i.ext.cx) (s"I${e.cx.typename}", true) else (e.cx.typename, true) + case _ => if(needRef) (e.cx.boxed, true) else (e.cx.typename, e.cx.reference) + } + case p: MParam => throw new AssertionError("Parameter should not happen at Cx top level") + } + base + } } - expr(tm) + f(tm, needRef) } // this can be used in c++ generation to know whether a const& should be applied to the parameter or not private def toCxParamType(tm: MExpr, namespace: Option[String] = None): String = { - val cxType = toCxType(tm, namespace) - val refType = cxType - val valueType = cxType - - def toType(expr: MExpr): String = expr.base match { - case p: MPrimitive => valueType - case d: MDef => d.defType match { - case DEnum => valueType - case _ => refType - } - case MOptional => toType(expr.args.head) - case _ => refType - } - toType(tm) + val (name, needRef) = toCxType(tm, namespace) + name + (if(needRef) "^" else "") } + } diff --git a/src/source/ObjcMarshal.scala b/src/source/ObjcMarshal.scala index 294366a2b..e875113fd 100644 --- a/src/source/ObjcMarshal.scala +++ b/src/source/ObjcMarshal.scala @@ -45,9 +45,9 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { override def fqReturnType(ret: Option[TypeRef]): String = returnType(ret) override def fieldType(tm: MExpr): String = toObjcParamType(tm) + override def toCpp(tm: MExpr, expr: String): String = throw new AssertionError("direct objc to cpp conversion not possible") override def fqFieldType(tm: MExpr): String = toObjcParamType(tm) - override def toCpp(tm: MExpr, expr: String): String = throw new AssertionError("direct objc to cpp conversion not possible") override def fromCpp(tm: MExpr, expr: String): String = throw new AssertionError("direct cpp to objc conversion not possible") def references(m: Meta, exclude: String = ""): Seq[SymbolReference] = m match { diff --git a/src/source/YamlGenerator.scala b/src/source/YamlGenerator.scala index 266fbce90..d06f1cb4f 100644 --- a/src/source/YamlGenerator.scala +++ b/src/source/YamlGenerator.scala @@ -16,6 +16,8 @@ class YamlGenerator(spec: Spec) extends Generator(spec) { val objcppMarshal = new ObjcppMarshal(spec) val javaMarshal = new JavaMarshal(spec) val jniMarshal = new JNIMarshal(spec) + val cxMarshal = new CxMarshal(spec) + val cxcppMarshal = new CxCppMarshal(spec) case class QuotedString(str: String) // For anything that migt require escaping @@ -144,6 +146,19 @@ class YamlGenerator(spec: Spec) extends Generator(spec) { "typeSignature" -> QuotedString(jniMarshal.fqTypename(td.ident, td.body)) ) + private def cx(td: TypeDecl) = Map[String, Any]( + "typename" -> QuotedString(cxMarshal.fqTypename(td.ident, td.body)), + "header" -> QuotedString(cxMarshal.include(td.ident)), + "boxed" -> QuotedString(cxMarshal.boxedTypename(td)), + "reference" -> cxMarshal.isReference(td) + ) + + private def cxcpp(td: TypeDecl) = Map[String, Any]( + "typename" -> QuotedString(cxcppMarshal.fqTypename(td.ident, td.body)), + "header" -> QuotedString(cxcppMarshal.include(td.ident)), + "byValue" -> cxcppMarshal.byValue(td) + ) + // TODO: there has to be a way to do all this without the MExpr/Meta conversions? private def mexpr(td: TypeDecl) = MExpr(meta(td), List()) @@ -209,8 +224,17 @@ object YamlGenerator { nested(td, "jni")("translator").toString, nested(td, "jni")("header").toString, nested(td, "jni")("typename").toString, - nested(td, "jni")("typeSignature").toString) - ) + nested(td, "jni")("typeSignature").toString), + MExtern.Cx( + nested(td, "cx")("typename").toString, + nested(td, "cx")("header").toString, + nested(td, "cx")("boxed").toString, + nested(td, "cx")("reference").asInstanceOf[Boolean]), + MExtern.CxCpp( + nested(td, "cxcpp")("typename").toString, + nested(td, "cxcpp")("header").toString, + nested(td, "cxcpp")("byValue").asInstanceOf[Boolean]) + ); private def nested(td: ExternTypeDecl, key: String) = { td.properties.get(key).collect { case m: JMap[_, _] => m.collect { case (k: String, v: Any) => (k, v) } } getOrElse(Map[String, Any]()) diff --git a/src/source/meta.scala b/src/source/meta.scala index e7e350ca9..36ae71d89 100644 --- a/src/source/meta.scala +++ b/src/source/meta.scala @@ -30,7 +30,7 @@ abstract sealed class Meta case class MParam(name: String) extends Meta { val numParams = 0 } case class MDef(name: String, override val numParams: Int, defType: DefType, body: TypeDef) extends Meta -case class MExtern(name: String, override val numParams: Int, defType: DefType, body: TypeDef, cpp: MExtern.Cpp, objc: MExtern.Objc, objcpp: MExtern.Objcpp, java: MExtern.Java, jni: MExtern.Jni) extends Meta +case class MExtern(name: String, override val numParams: Int, defType: DefType, body: TypeDef, cpp: MExtern.Cpp, objc: MExtern.Objc, objcpp: MExtern.Objcpp, java: MExtern.Java, jni: MExtern.Jni, cx: MExtern.Cx, cxcpp: MExtern.CxCpp) extends Meta object MExtern { // These hold the information marshals need to interface with existing types correctly // All include paths are complete including quotation marks "a/b/c" or angle brackets . @@ -65,6 +65,17 @@ object MExtern { typename: String, // The JNI type to use (e.g. jobject, jstring) typeSignature: String // The mangled Java type signature (e.g. "Ljava/lang/String;") ) + case class Cx( + typename: String, + header: String, + boxed: String, + reference: Boolean + ) + case class CxCpp( + typename: String, + header: String, + byValue: Boolean + ) } abstract sealed class MOpaque extends Meta { val idlName: String } @@ -84,13 +95,13 @@ case object MSet extends MOpaque { val numParams = 1; val idlName = "set" } case object MMap extends MOpaque { val numParams = 2; val idlName = "map" } val defaults: Map[String,MOpaque] = immutable.HashMap( - ("i8", MPrimitive("i8", "byte", "jbyte", "int8_t", "Byte", "B", "int8_t", "NSNumber", "int16", "Platform::Object^")), - ("i16", MPrimitive("i16", "short", "jshort", "int16_t", "Short", "S", "int16_t", "NSNumber", "int16", "Platform::Object^")), - ("i32", MPrimitive("i32", "int", "jint", "int32_t", "Integer", "I", "int32_t", "NSNumber", "int32", "Platform::Object^")), - ("i64", MPrimitive("i64", "long", "jlong", "int64_t", "Long", "J", "int64_t", "NSNumber", "int64", "Platform::Object^")), - ("f32", MPrimitive("f32", "float", "jfloat", "float", "Float", "F", "float", "NSNumber", "float32", "Platform::Object^")), - ("f64", MPrimitive("f64", "double", "jdouble", "double", "Double", "D", "double", "NSNumber", "float64", "Platform::Object^")), - ("bool", MPrimitive("bool", "boolean", "jboolean", "bool", "Boolean", "Z", "BOOL", "NSNumber", "bool", "Platform::Object^")), + ("i8", MPrimitive("i8", "byte", "jbyte", "int8_t", "Byte", "B", "int8_t", "NSNumber", "int16", "Platform::Object")), + ("i16", MPrimitive("i16", "short", "jshort", "int16_t", "Short", "S", "int16_t", "NSNumber", "int16", "Platform::Object")), + ("i32", MPrimitive("i32", "int", "jint", "int32_t", "Integer", "I", "int32_t", "NSNumber", "int32", "Platform::Object")), + ("i64", MPrimitive("i64", "long", "jlong", "int64_t", "Long", "J", "int64_t", "NSNumber", "int64", "Platform::Object")), + ("f32", MPrimitive("f32", "float", "jfloat", "float", "Float", "F", "float", "NSNumber", "float32", "Platform::Object")), + ("f64", MPrimitive("f64", "double", "jdouble", "double", "Double", "D", "double", "NSNumber", "float64", "Platform::Object")), + ("bool", MPrimitive("bool", "boolean", "jboolean", "bool", "Boolean", "Z", "BOOL", "NSNumber", "bool", "Platform::Object")), ("string", MString), ("binary", MBinary), ("optional", MOptional), From 171a1aecfb94cc7fb5cfc6df25f16065c299de25 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 24 Jul 2015 10:56:42 -0700 Subject: [PATCH 33/52] Now I think optional types are doing the right thing. --- src/source/CxMarshal.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index 235fa2363..d03b6a7f7 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -8,7 +8,13 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { private val cppMarshal = new CppMarshal(spec) - override def typename(tm: MExpr): String = toCxType(tm, None)._1 +// override def typename(tm: MExpr): String = toCxType(tm, None)._1 + override def typename(tm: MExpr): String = { + val (name, needRef) = toCxType(tm, None) + val result = if(needRef) (s"${name}^") else (s"${name}") + result + } + def typename(name: String, ty: TypeDef): String = ty match { case e: Enum => idCx.enumType(name) case i: Interface => if(i.ext.cx) s"I${idCx.ty(name)}" else idCx.ty(name) From 8fd21adcd9c729fd521a8848804e8d7cd8832420 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 24 Jul 2015 12:30:48 -0700 Subject: [PATCH 34/52] Maybe now we have vectors right? ANd maybe even maps? --- src/source/CxMarshal.scala | 200 ++++++++++++++++++++++++++++--------- 1 file changed, 155 insertions(+), 45 deletions(-) diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index d03b6a7f7..e22ec9925 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -37,11 +37,60 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { override def fieldType(tm: MExpr): String = typename(tm) override def fqFieldType(tm: MExpr): String = fqTypename(tm) + override def toCpp(tm: MExpr, expr: String): String = { - s"${helperClass(tm)}::toCpp($expr)" + s"${ownClass(tm)}::toCpp($expr)" } override def fromCpp(tm: MExpr, expr: String): String = { - s"${helperClass(tm)}::fromCpp($expr)" + s"${ownClass(tm)}::fromCpp($expr)" + } + + + def ownClass(name: String) = s"${idCx.ty(name)}" + private def ownClass(tm: MExpr): String = ownName(tm) + ownTemplates(tm) + + private def ownName(tm: MExpr): String = tm.base match { + case d: MDef => d.defType match { + case DEnum => withNs(Some("djinni"), s"Enum<${fqTypename(tm)}, ${fqTypename(tm)}>") + case _ => withNs(Some(spec.cxcppNamespace), ownClass(d.name)) + } + case o => withNs(Some("djinni"), o match { + case p: MPrimitive => p.idlName match { + case "i8" => "I8" + case "i16" => "I16" + case "i32" => "I32" + case "i64" => "I64" + case "f32" => "F32" + case "f64" => "F64" + case "bool" => "Bool" + } + case MOptional => "Optional" + case MBinary => "Binary" + case MDate => "Date" + case MString => "String" + case MList => "List" + case MSet => "Set" + case MMap => "Map" + case d: MDef => throw new AssertionError("unreachable") + case p: MParam => throw new AssertionError("not applicable") + }) + } + + private def ownTemplates(tm: MExpr): String = { + def f() = if(tm.args.isEmpty) "" else tm.args.map(ownClass).mkString("<", ", ", ">") + tm.base match { + case MOptional => + assert(tm.args.size == 1) + val argHelperClass = ownClass(tm.args.head) + s"<${spec.cppOptionalTemplate}, $argHelperClass>" + case MList | MSet => + assert(tm.args.size == 1) + f + case MMap => + assert(tm.args.size == 2) + f + case _ => f + } } def helperClass(name: String) = idCpp.ty(name) @@ -154,51 +203,112 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { } - // Return value: (Type_Name, Is_Class_Or_Not) - def toCxType(ty: TypeRef, namespace: Option[String] = None): (String, Boolean) = toCxType(ty.resolved, namespace, false) - def toCxType(ty: TypeRef, namespace: Option[String], needRef: Boolean): (String, Boolean) = toCxType(ty.resolved, namespace, needRef) - def toCxType(tm: MExpr, namespace: Option[String]): (String, Boolean) = toCxType(tm, namespace, false) - def toCxType(tm: MExpr, namespace: Option[String], needRef: Boolean): (String, Boolean) = { - def f(tm: MExpr, needRef: Boolean): (String, Boolean) = { - tm.base match { - case MOptional => - // We use "nil" for the empty optional. - assert(tm.args.size == 1) - val arg = tm.args.head - arg.base match { - case MOptional => throw new AssertionError("nested optional?") - case m => f(arg, true) - } - case o => - val base = o match { - case p: MPrimitive => if (needRef) (p.cxBoxed, true) else (p.cxName, false) - case MString => ("Platform::String", true) - case MDate => ("Windows::Foundation::DateTime", true) - case MBinary => ("Platform::Array", true) - case MOptional => throw new AssertionError("optional should have been special cased") - case MList => ("Windows::Foundation::Collections::IVector", true) - case MSet => ("Windows::Foundation::Collections::IMap", true) //no set in C++/Cx FOr now this shit is broken until I can figure out how to make something map onto itself. - case MMap => ("Windows::Foundation::Collections::IMap", true) - case d: MDef => d.defType match { - case DEnum => (idCx.ty(d.name), true) - case DRecord => (idCx.ty(d.name), true) - case DInterface => - val ext = d.body.asInstanceOf[Interface].ext - if (ext.cpp && !ext.cx) - (idCx.ty(d.name), true) - else - (s"I${withNs(namespace, idCx.ty(d.name))}", true) - } - case e: MExtern => e.body match { - case i: Interface => if(i.ext.cx) (s"I${e.cx.typename}", true) else (e.cx.typename, true) - case _ => if(needRef) (e.cx.boxed, true) else (e.cx.typename, e.cx.reference) - } - case p: MParam => throw new AssertionError("Parameter should not happen at Cx top level") - } - base +// // Return value: (Type_Name, Is_Class_Or_Not) +// def toCxType(ty: TypeRef, namespace: Option[String] = None): (String, Boolean) = toCxType(ty.resolved, namespace, false) +// def toCxType(ty: TypeRef, namespace: Option[String], needRef: Boolean): (String, Boolean) = toCxType(ty.resolved, namespace, needRef) +// def toCxType(tm: MExpr, namespace: Option[String]): (String, Boolean) = toCxType(tm, namespace, false) +// def toCxType(tm: MExpr, namespace: Option[String], needRef: Boolean): (String, Boolean) = { +// def f(tm: MExpr, needRef: Boolean): (String, Boolean) = { +// def base(tm: MExpr, needRef: Boolean): (String, Boolean) = { +// tm.base match { +// case MOptional => +// // We use "nil" for the empty optional. +// assert(tm.args.size == 1) +// val arg = tm.args.head +// arg.base match { +// case MOptional => throw new AssertionError("nested optional?") +// case m => f(arg, true) +// } +// case o => +// val base = o match { +// case p: MPrimitive => if (needRef) (p.cxBoxed, true) else (p.cxName, false) +// case MString => ("Platform::String", true) +// case MDate => ("Windows::Foundation::DateTime", true) +// case MBinary => ("Platform::Array", true) +// case MOptional => throw new AssertionError("optional should have been special cased") +// case MList => ("Windows::Foundation::Collections::IVector", true) +// case MSet => ("Windows::Foundation::Collections::IMap", true) //no set in C++/Cx FOr now this shit is broken until I can figure out how to make something map onto itself. +// case MMap => ("Windows::Foundation::Collections::IMap", true) +// case d: MDef => d.defType match { +// case DEnum => (idCx.ty(d.name), true) +// case DRecord => (idCx.ty(d.name), true) +// case DInterface => +// val ext = d.body.asInstanceOf[Interface].ext +// if (ext.cpp && !ext.cx) +// (idCx.ty(d.name), true) +// else +// (s"I${withNs(namespace, idCx.ty(d.name))}", true) +// } +// case e: MExtern => e.body match { +// case i: Interface => if (i.ext.cx) (s"I${e.cx.typename}", true) else (e.cx.typename, true) +// case _ => if (needRef) (e.cx.boxed, true) else (e.cx.typename, e.cx.reference) +// } +// case p: MParam => throw new AssertionError("Parameter should not happen at Cx top level") +// } +// base +// } +// } +// def expr(tm: MExpr, needRef: Boolean): (String, Boolean) = { +// val args = if (tm.args.isEmpty) "" else tm.args.map(expr).mkString("<", ", ", ">") +// base(tm, needRef) + args +// } +// expr(tm, needRef) +// } +// f(tm, needRef) +// } + + def toCxType(ty: TypeRef, namespace: Option[String] = None): (String, Boolean) = toCxType(ty.resolved, namespace, false) + def toCxType(ty: TypeRef, namespace: Option[String], needRef: Boolean): (String, Boolean) = toCxType(ty.resolved, namespace, needRef) + def toCxType(tm: MExpr, namespace: Option[String]): (String, Boolean) = toCxType(tm, namespace, false) + def toCxType(tm: MExpr, namespace: Option[String], needRef: Boolean): (String, Boolean) = { + def base(m: MExpr, namespace: Option[String], needRef: Boolean): (String, Boolean) = m.base match { + case p: MPrimitive => (p.cName, false) + case MString => ("Platform::String", true) + case MDate => ("Windows::Foundation::DateTime", true) + case MBinary => ("Platform::Array", true) + case MOptional => // We use "nullptr" for the empty optional. + assert(tm.args.size == 1) + val arg = tm.args.head + arg.base match { + case MOptional => throw new AssertionError("nested optional?") + case p: MPrimitive => (p.cxBoxed, true) + case m => base(arg, namespace, true) + } + case MList => ("Windows::Foundation::Collections::IVector", true) + case MSet => ("Windows::Foundation::Collections::IMap", true) + case MMap => ("Windows::Foundation::Collections::IMap", true) + case d: MDef => + d.defType match { + case DEnum => (withNs(namespace, idCx.enumType(d.name)), true) + case DRecord => (withNs(namespace, idCx.ty(d.name)), true) + case DInterface => + val ext = d.body.asInstanceOf[Interface].ext + if (ext.cpp && !ext.cx) + (idCx.ty(d.name), true) + else + (s"I${withNs(namespace, idCx.ty(d.name))}", true) + } + case e: MExtern => e.body match { + case i: Interface => if (i.ext.cx) (s"I${e.cx.typename}", true) else (e.cx.typename, true) + case _ => (e.cpp.typename, needRef) + } + case p: MParam => (idCx.typeParam(p.name), needRef) + } + def exprWithReference(tm: MExpr, namespace: Option[String], needRef:Boolean): String = { + val (arg, ref) = expr(tm, namespace, needRef) + if(ref) s"$arg^" else arg + } + def expr(tm: MExpr, namespace: Option[String], needRef: Boolean): (String, Boolean) = { + + val args = tm.base match { + case MOptional => "" + case MMap => if (tm.args.size == 1) (tm.args :+ tm.args(-1)).map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") else tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") + case d => if (tm.args.isEmpty) "" else tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") } + val (ret, ref) = base(tm, namespace, needRef) + (ret + args, ref) } - f(tm, needRef) + expr(tm, namespace, needRef) } // this can be used in c++ generation to know whether a const& should be applied to the parameter or not From 6d9d0945528ca45ad1665b799edf08341b23d1b6 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 24 Jul 2015 14:16:55 -0700 Subject: [PATCH 35/52] Holy crap, WTF --- src/source/CxCppGenerator.scala | 6 +++--- src/source/CxCppMarshal.scala | 2 +- src/source/CxMarshal.scala | 10 ++++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index e1a5e319c..58ab2ec85 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -201,7 +201,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { i.consts.map(c => { refs.find(c.ty) }) - + refs.cxcpp = refs.hx.clone() refs.cxcpp.add("#include \"CxWrapperCache.h\"") val self = cxcppMarshal.typename(ident, i) @@ -246,8 +246,8 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { for (m <- i.methods) { w.wl writeDoc(w, m.doc) - val ret = cppMarshal.returnType(m.ret) - val params = m.params.map(p => cppMarshal.paramType(p.ty) + " " + idCpp.local(p.ident)) + val ret = cppMarshal.fqReturnType(m.ret) + val params = m.params.map(p => cppMarshal.fqParamType(p.ty) + " " + idCpp.local(p.ident)) if (m.static) { w.wl(s"static $ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")} override") } else { diff --git a/src/source/CxCppMarshal.scala b/src/source/CxCppMarshal.scala index 56fb158bb..04f4dc138 100644 --- a/src/source/CxCppMarshal.scala +++ b/src/source/CxCppMarshal.scala @@ -101,7 +101,7 @@ class CxCppMarshal(spec: Spec) extends Marshal(spec) { } } - def helperClass(name: String) = s"${idCpp.ty(name)}::CxProxy" + def helperClass(name: String) = s"${idCx.ty(name)}::CxProxy" private def helperClass(tm: MExpr): String = helperName(tm) + helperTemplates(tm) def headerName(ident: String): String = idCx.ty(ident) + "_convert" diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index e22ec9925..e5357b7a0 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -51,7 +51,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { private def ownName(tm: MExpr): String = tm.base match { case d: MDef => d.defType match { - case DEnum => withNs(Some("djinni"), s"Enum<${fqTypename(tm)}, ${fqTypename(tm)}>") +// case DEnum => withNs(Some("djinni"), s"Enum<${fqTypename(tm)}, ${fqTypename(tm)}>") case _ => withNs(Some(spec.cxcppNamespace), ownClass(d.name)) } case o => withNs(Some("djinni"), o match { @@ -99,6 +99,12 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { private def helperName(tm: MExpr): String = tm.base match { case d: MDef => d.defType match { case DEnum => withNs(Some("djinni"), s"Enum<${cppMarshal.fqTypename(tm)}, ${fqTypename(tm)}>") + case DInterface => + val ext = d.body.asInstanceOf[Interface].ext + if (ext.cpp && !ext.cx) + withNs(Some(spec.cxcppNamespace), helperClass(d.name)) + else + s"I${withNs(Some(spec.cxcppNamespace), helperClass(d.name))}" case _ => withNs(Some(spec.cxcppNamespace), helperClass(d.name)) } case o => withNs(Some("djinni"), o match { @@ -262,7 +268,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { def toCxType(tm: MExpr, namespace: Option[String]): (String, Boolean) = toCxType(tm, namespace, false) def toCxType(tm: MExpr, namespace: Option[String], needRef: Boolean): (String, Boolean) = { def base(m: MExpr, namespace: Option[String], needRef: Boolean): (String, Boolean) = m.base match { - case p: MPrimitive => (p.cName, false) + case p: MPrimitive => (p.cxName, false) case MString => ("Platform::String", true) case MDate => ("Windows::Foundation::DateTime", true) case MBinary => ("Platform::Array", true) From 9d1fc194582483e5db6987c71df545e345417435 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Mon, 27 Jul 2015 14:06:55 -0700 Subject: [PATCH 36/52] Making stuff work. General bug fixes. Esp around use of "^" and Lists and Maps --- src/source/CxCppGenerator.scala | 68 +++++---------- src/source/CxGenerator.scala | 21 +---- src/source/CxMarshal.scala | 11 +-- src/source/generator.scala | 2 +- support-lib/cx/Marshal.h | 147 ++++++++++++++++---------------- 5 files changed, 103 insertions(+), 146 deletions(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index 58ab2ec85..f0bb28b3a 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -52,38 +52,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { } override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) { - val refs = new CxCppRefs(ident.name) - val self = cxcppMarshal.typename(ident, e) - - if (spec.cppEnumHashWorkaround) { - refs.hx.add("#include ") // needed for std::hash - } - - writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { - w.w(s"enum class $self : int").bracedSemi { - for (o <- e.options) { - writeDoc(w, o.doc) - w.wl(idCpp.enum(o.ident.name) + ",") - } - } - }, - w => { - // std::hash specialization has to go *outside* of the wrapNs - if (spec.cppEnumHashWorkaround) { - val fqSelf = cxcppMarshal.fqTypename(ident, e) - w.wl - wrapNamespace(w, "std", - (w: IndentWriter) => { - w.wl("template <>") - w.w(s"struct hash<$fqSelf>").bracedSemi { - w.w(s"size_t operator()($fqSelf type) const").braced { - w.wl("return std::hash()(static_cast(type));") - } - } - } - ) - } - }) + //nothing required? } def generateHxConstants(w: IndentWriter, consts: Seq[Const]) = { @@ -162,17 +131,17 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.wl(s"struct $self") w.bracedSemi { w.wl(s"using CppType = $cppSelf;") - w.wl(s"using CxType = $cxSelf;"); + w.wl(s"using CxType = $cxSelf^;"); w.wl w.wl(s"using Boxed = $self;") w.wl - w.wl(s"static CppType toCpp(CxType^ cx);") - w.wl(s"static CxType^ fromCpp(const CppType& cpp);") + w.wl(s"static CppType toCpp(CxType cx);") + w.wl(s"static CxType fromCpp(const CppType& cpp);") } }) writeCxCppFile(cxcppMarshal.bodyName(self), origin, refs.cxcpp, w => { - w.wl(s"auto $self::toCpp(CxType^ cx) -> CppType") + w.wl(s"auto $self::toCpp(CxType cx) -> CppType") w.braced { w.wl("assert(cx);") if(r.fields.isEmpty) w.wl("(void)cx; // Suppress warnings in relase builds for empty records") @@ -180,10 +149,10 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.wl(";") } w.wl - w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType^") + w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType") w.braced { if(r.fields.isEmpty) w.wl("(void)cpp; // Suppress warnings in relase builds for empty records") - writeAlignedCall(w, "return ref new CxType(", r.fields, ")", f=> cxcppMarshal.fromCpp(f.ty, "cpp." + idCpp.field(f.ident))) + writeAlignedCall(w, s"return ref new $cxSelf(", r.fields, ")", f=> cxcppMarshal.fromCpp(f.ty, "cpp." + idCpp.field(f.ident))) w.wl(";") } }) @@ -215,12 +184,12 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.bracedSemi { w.wlOutdent("public:") w.wl(s"using CppType = std::shared_ptr<$cppSelf>;") - w.wl(s"using CxType = $cxSelf;"); + w.wl(s"using CxType = $cxSelf^;"); w.wl w.wl(s"using Boxed = $self;") w.wl - w.wl(s"static CppType toCpp(CxType^ cx);") - w.wl(s"static CxType^ fromCpp(const CppType& cpp);") + w.wl(s"static CppType toCpp(CxType cx);") + w.wl(s"static CxType fromCpp(const CppType& cpp);") if (i.ext.cx) { w.wl w.wlOutdent("private:") @@ -238,7 +207,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.wl(s"class $helperClass final : public $cppSelf, public ::djinni::CxWrapperCache::Handle").bracedSemi { w.wlOutdent("public:") w.wl("using Handle::Handle;") - w.wl(s"using CxType = $cxSelf;") + w.wl(s"using CxType = $cxSelf^;") w.wl w.wl("CxProxy(Platform::Object^ cx) : ::djinni::CxWrapperCache::Handle{ cx } {}") w.wl @@ -255,7 +224,8 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.wl(s"$ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag override") } w.braced { - val call = (if (!m.static) "auto r = static_cast(Handle::get())->" else cppSelf + "::") + idCx.method(m.ident) + "(" + val retCall = if(m.ret == None) "" else "auto r = " + val call = retCall + (if (!m.static) s"static_cast(Handle::get())->" else cppSelf + "::") + idCx.method(m.ident) + "(" writeAlignedCall(w, call, m.params, ")", p => cxcppMarshal.fromCpp(p.ty, idCpp.local(p.ident.name))) w.wl(";") m.ret.fold()(r => w.wl(s"return ${cxcppMarshal.toCpp(r, "r")};")) @@ -263,7 +233,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { } } w.wl - w.wl(s"auto $self::toCpp(CxType^ cx) -> CppType") + w.wl(s"auto $self::toCpp(CxType cx) -> CppType") w.braced { w.wl("if (!cx)").braced { w.wl("return nullptr;") @@ -271,24 +241,24 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { w.wl("return ::djinni::CxWrapperCache::getInstance()->get(cx);") } w.wl - w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType^") + w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType") w.braced { w.braced { w.wl("if (!cpp)").braced { w.wl("return nullptr;") } - w.wl("return static_cast(dynamic_cast(*cpp).Handle::get());") + w.wl("return static_cast(dynamic_cast(*cpp).Handle::get());") } } } else { - w.wl(s"auto $self::toCpp(CxType^ cx) -> CppType") + w.wl(s"auto $self::toCpp(CxType cx) -> CppType") w.braced { w.wl("return cx->m_cppRef.get();") } w.wl - w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType^") + w.wl(s"auto $self::fromCpp(const CppType& cpp) -> CxType") w.braced { - w.wl(s"return (CxType^)::djinni::CppWrapperCache<$cppSelf>::getInstance()->get(cpp, [](const std::shared_ptr<$cppSelf>& p)").bracedEnd(");") { + w.wl(s"return (CxType)::djinni::CppWrapperCache<$cppSelf>::getInstance()->get(cpp, [](const std::shared_ptr<$cppSelf>& p)").bracedEnd(");") { w.wl(s"return ref new $cxSelf(p);") } diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index 768d8b62e..a4d5c94e9 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -72,30 +72,13 @@ class CxGenerator(spec: Spec) extends Generator(spec) { val self = cxMarshal.typename(ident, e) writeHxFile(ident, origin, refs.hx, refs.hxFwds, w => { - w.w(s"enum class $self : int").bracedSemi { + w.w(s"public enum class $self").bracedSemi { for (o <- e.options) { writeDoc(w, o.doc) w.wl(idCx.enum(o.ident.name) + ",") } } - }, - w => { - // std::hash specialization has to go *outside* of the wrapNs - if (spec.cppEnumHashWorkaround) { - val fqSelf = cxMarshal.fqTypename(ident, e) - w.wl - wrapNamespace(w, "std", - (w: IndentWriter) => { - w.wl("template <>") - w.w(s"struct hash<$fqSelf>").bracedSemi { - w.w(s"size_t operator()($fqSelf type) const").braced { - w.wl("return std::hash()(static_cast(type));") - } - } - } - ) - } - }) + }) } def generateHxConstants(w: IndentWriter, consts: Seq[Const]) = { diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index e5357b7a0..ca7cbbf10 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -51,8 +51,8 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { private def ownName(tm: MExpr): String = tm.base match { case d: MDef => d.defType match { -// case DEnum => withNs(Some("djinni"), s"Enum<${fqTypename(tm)}, ${fqTypename(tm)}>") - case _ => withNs(Some(spec.cxcppNamespace), ownClass(d.name)) + case DEnum => withNs(Some("djinni"), s"Enum<${fqTypename(tm)}, ${fqTypename(tm)}>") + case _ => withNs(Some(spec.cxcppNamespace), s"${ownClass(d.name)}") } case o => withNs(Some("djinni"), o match { case p: MPrimitive => p.idlName match { @@ -82,7 +82,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { case MOptional => assert(tm.args.size == 1) val argHelperClass = ownClass(tm.args.head) - s"<${spec.cppOptionalTemplate}, $argHelperClass>" + s"<${spec.cppOptionalTemplate}, $argHelperClass>" //TODO THIS IS VERY WRONG! case MList | MSet => assert(tm.args.size == 1) f @@ -176,7 +176,8 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { } case MString | MDate | MBinary | MOptional | MList | MSet | MMap => List() case d: MDef => d.defType match { - case DEnum | DRecord => + case DEnum => List() //no headers to import for enums + case DRecord => //DEnum | DRecord => if (d.name != exclude) { List(ImportRef(q(spec.cxcppIncludePrefix + spec.cxFileIdentStyle(d.name) + "_convert." + spec.cxcppHeaderExt))) } else { @@ -285,7 +286,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { case MMap => ("Windows::Foundation::Collections::IMap", true) case d: MDef => d.defType match { - case DEnum => (withNs(namespace, idCx.enumType(d.name)), true) + case DEnum => (withNs(namespace, idCx.enumType(d.name)), false) case DRecord => (withNs(namespace, idCx.ty(d.name)), true) case DInterface => val ext = d.body.asInstanceOf[Interface].ext diff --git a/src/source/generator.scala b/src/source/generator.scala index fa709004f..550f91156 100644 --- a/src/source/generator.scala +++ b/src/source/generator.scala @@ -132,7 +132,7 @@ package object generatorTools { val javaDefault = JavaIdentStyle(camelUpper, camelUpper, camelLower, camelLower, camelLower, underCaps, underCaps) val cppDefault = CppIdentStyle(camelUpper, camelUpper, camelUpper, underLower, underLower, underLower, underCaps, underCaps) val objcDefault = ObjcIdentStyle(camelUpper, camelUpper, camelLower, camelLower, camelLower, camelUpper, camelUpper) - val cxDefault = CxIdentStyle(camelUpper, camelUpper, camelUpper, camelUpper, camelLower, camelLower, camelUpper, underCaps) + val cxDefault = CxIdentStyle(camelUpper, camelUpper, camelUpper, camelUpper, camelUpper, camelUpper, camelUpper, camelUpper) val styles = Map( "FooBar" -> camelUpper, diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index ee9dea30e..8151c8ddf 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -44,79 +44,79 @@ namespace djinni { static CxType fromCpp(CppType x) { return x; } struct Boxed { - using CxType = Platform::Object; - static CppType toCpp(CxType^ x) { assert(x); return Self::unbox(x); } - static CxType^ fromCpp(CppType x) { return Self::box(x); } + using CxType = Platform::Object^; + static CppType toCpp(CxType x) { assert(x); return Self::unbox(x); } + static CxType fromCpp(CppType x) { return Self::box(x); } }; }; //stupid C++/Cx doesn't have int8_t; we'll pass it up as a uint8_t instead, and pray. class I8 : public Primitive { friend Primitive; - static int16 unbox(Boxed::CxType^ x) { return (uint8)x; } - static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = (uint8)x; return cx; } + static int16 unbox(Boxed::CxType x) { return (uint8)x; } + static Boxed::CxType box(CppType x) { Boxed::CxType cx = (uint8)x; return cx; } }; class I16 : public Primitive { friend Primitive; - static int16 unbox(Boxed::CxType^ x) { return (int16)x; } - static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = x; return cx; } + static int16 unbox(Boxed::CxType x) { return (int16)x; } + static Boxed::CxType box(CppType x) { Boxed::CxType cx = x; return cx; } }; class I32 : public Primitive { friend Primitive; - static int32 unbox(Boxed::CxType^ x) { return (int32)x; } - static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = x; return cx; } + static int32 unbox(Boxed::CxType x) { return (int32)x; } + static Boxed::CxType box(CppType x) { Boxed::CxType cx = x; return cx; } }; class I64 : public Primitive { friend Primitive; - static int64 unbox(Boxed::CxType^ x) { return (int64)x; } - static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = x; return cx; } + static int64 unbox(Boxed::CxType x) { return (int64)x; } + static Boxed::CxType box(CppType x) { Boxed::CxType cx = x; return cx; } }; class F32 : public Primitive { friend Primitive; - static float unbox(Boxed::CxType^ x) { return (float)x; } - static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = x; return cx; } + static float unbox(Boxed::CxType x) { return (float)x; } + static Boxed::CxType box(CppType x) { Boxed::CxType cx = x; return cx; } }; class F64 : public Primitive { friend Primitive; - static double unbox(Boxed::CxType^ x) { return (double)x; } - static Boxed::CxType^ box(CppType x) { Boxed::CxType^ cx = x; return cx; } + static double unbox(Boxed::CxType x) { return (double)x; } + static Boxed::CxType box(CppType x) { Boxed::CxType cx = x; return cx; } }; -// template -// struct Enum { -// using CppType = CppEnum; -// using CxType = CxEnum; -// -// static CppType toCpp(CxType e) { return static_cast(e); } -// static CxType fromCpp(CppType e) { return static_cast(e); } -// -// struct Boxed { -// //yeah, I just don't know about these two lines. So...weird? How _do_ you box an enum? -// static CppType toCpp(CxType x) { return Enum::toCpp(static_cast(static_cast(x))); } -// static CxType fromCpp(CppType x) { return Enum::toCx(static_cast(static_cast(x))); } -// }; -// }; + template + struct Enum { + using CppType = CppEnum; + using CxType = CxEnum; + + static CppType toCpp(CxType e) { return static_cast(e); } + static CxType fromCpp(CppType e) { return static_cast(e); } + + struct Boxed { + //yeah, I just don't know about these two lines. So...weird? How _do_ you box an enum? + static CppType toCpp(CxType x) { return Enum::toCpp(static_cast(static_cast(x))); } + static CxType fromCpp(CppType x) { return Enum::toCx(static_cast(static_cast(x))); } + }; + }; struct String { using CppType = std::string; - using CxType = Platform::String; + using CxType = Platform::String^; using Boxed = String; - static CppType toCpp(CxType^ string) { + static CppType toCpp(CxType string) { assert(string); std::wstring wstring{ string->Data() }; std::wstring_convert, wchar_t> converter; return converter.to_bytes(wstring); } - static CxType^ fromCpp(const CppType& string) { + static CxType fromCpp(const CppType& string) { //TODO this doesn't seem to work. assert(string.size() <= std::numeric_limits::max()); std::wstring_convert> converter; return ref new Platform::String(converter.from_bytes(string).c_str()); @@ -198,26 +198,26 @@ namespace djinni { template class List { using ECppType = typename T::CppType; - using ECxType = typename T::Boxed::CxType; + using ECxType = typename T::CxType; public: using CppType = std::vector; - using CxType = Windows::Foundation::Collections::IVector; + using CxType = Windows::Foundation::Collections::IVector^; using Boxed = List; - static CppType toCpp(CxType^ v) { + static CppType toCpp(CxType v) { assert(v); CppType nv; - for(ECxType^ val : v) + for(ECxType val : v) { nv.push_back(T::Boxed::toCpp(val)); } return nv; } - static CxType^ fromCpp(const CppType& v) { - Platform::Collections::Vector^ nv = ref new Platform::Collections::Vector; + static CxType fromCpp(const CppType& v) { + auto nv = ref new Platform::Collections::Vector; for (ECppType val : v) { nv->Append(T::Boxed::fromCpp(val)); @@ -256,38 +256,41 @@ namespace djinni { // return set; // } // }; - // - // template - // class Map { - // using CppKeyType = typename Key::CppType; - // using CppValueType = typename Value::CppType; - // using CxKeyType = typename Key::Boxed::CxType; - // using CxValueType = typename Value::Boxed::CxType; - // - // public: - // using CppType = std::unordered_map; - // using CxType = NSDictionary*; - // - // using Boxed = Map; - // - // static CppType toCpp(CxType map) { - // assert(map); - // __block auto m = CppType(); - // m.reserve(map.count); - // [map enumerateKeysAndObjectsUsingBlock:^(CxKeyType key, CxValueType obj, BOOL *) { - // m.emplace(Key::Boxed::toCpp(key), Value::Boxed::toCpp(obj)); - // }]; - // return m; - // } - // - // static CxType fromCpp(const CppType& m) { - // assert(m.size() <= std::numeric_limits::max()); - // auto map = [NSMutableDictionary dictionaryWithCapacity:static_cast(m.size())]; - // for(const auto& kvp : m) { - // [map setObject:Value::Boxed::fromCpp(kvp.second) forKey:Key::Boxed::fromCpp(kvp.first)]; - // } - // return map; - // } - // }; - // + + template + class Map { + using CppKeyType = typename Key::CppType; + using CppValueType = typename Value::CppType; + using CxKeyType = typename Key::CxType; + using CxValueType = typename Value::CxType; + + public: + using CppType = std::unordered_map; + using CxType = Windows::Foundation::Collections::IMap^; + + using Boxed = Map; + + static CppType toCpp(CxType map) { + assert(map); + auto m = CppType(); + m.reserve(map.count); + + std::for_each(begin(map), end(map), [](Windows::Foundation::Collections::IKeyValuePair^ pair) + { + m.emplace(Key::toCpp(pair->Key), Value::toCpp(pair->Value)): + }); + + return m; + } + + static CxType fromCpp(const CppType& m) { +// assert(m.size() <= std::numeric_limits::max()); + auto map = ref new Platform::Collections::Map;//[NSMutableDictionary dictionaryWithCapacity:static_cast(m.size())]; +// for(const auto& kvp : m) { +// [map setObject:Value::Boxed::fromCpp(kvp.second) forKey:Key::Boxed::fromCpp(kvp.first)]; +// } + return map; + } + }; + } // namespace djinni From 67be17ad706c81b8e7a41eaba840f8427c3af1fe Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Mon, 27 Jul 2015 14:39:28 -0700 Subject: [PATCH 37/52] Fixing a remaining bug --- support-lib/cx/Marshal.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index 8151c8ddf..acbaff4eb 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -284,11 +284,12 @@ namespace djinni { } static CxType fromCpp(const CppType& m) { -// assert(m.size() <= std::numeric_limits::max()); + //assert(m.size() <= std::numeric_limits::max()); auto map = ref new Platform::Collections::Map;//[NSMutableDictionary dictionaryWithCapacity:static_cast(m.size())]; -// for(const auto& kvp : m) { + for(const auto& kvp : m) { + map->Insert(Value::fromCpp(kvp.first), Key::fromCpp(kvp.second)); // [map setObject:Value::Boxed::fromCpp(kvp.second) forKey:Key::Boxed::fromCpp(kvp.first)]; -// } + } return map; } }; From 1c56b86edb75ac305ec6caef4f5367827b73398e Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Mon, 27 Jul 2015 15:57:22 -0700 Subject: [PATCH 38/52] Add support for Sets in Cx as reflexive maps --- src/source/CxMarshal.scala | 3 +- support-lib/cx/Marshal.h | 68 +++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index ca7cbbf10..2a5fa7f6a 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -309,7 +309,8 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { val args = tm.base match { case MOptional => "" - case MMap => if (tm.args.size == 1) (tm.args :+ tm.args(-1)).map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") else tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") + case MSet => if (tm.args.size == 1) (tm.args :+ tm.args(0)).map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") else tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") + case MMap => tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") case d => if (tm.args.isEmpty) "" else tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") } val (ret, ref) = base(tm, namespace, needRef) diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index acbaff4eb..78158f3df 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -226,36 +226,37 @@ namespace djinni { } //We ought to specialize this for types C++/Cx knows how to convert for us. }; - // - // template - // class Set { - // using ECppType = typename T::CppType; - // using ECxType = typename T::Boxed::CxType; - // - // public: - // using CppType = std::unordered_set; - // using CxType = NSSet*; - // - // using Boxed = Set; - // - // static CppType toCpp(CxType set) { - // assert(set); - // auto s = CppType(); - // for(ECxType value in set) { - // s.insert(T::Boxed::toCpp(value)); - // } - // return s; - // } - // - // static CxType fromCpp(const CppType& s) { - // assert(s.size() <= std::numeric_limits::max()); - // auto set = [NSMutableSet setWithCapacity:static_cast(s.size())]; - // for(const auto& value : s) { - // [set addObject:T::Boxed::fromCpp(value)]; - // } - // return set; - // } - // }; + + template + class Set { + using ECppType = typename T::CppType; + using ECxType = typename T::CxType; + + public: + using CppType = std::unordered_set; + using CxType = Windows::Foundation::Collections::IMap^; //no sets. Seriously. So we'll just map objects to themselves + + using Boxed = Set; + + static CppType toCpp(CxType set) { + assert(set); + auto s = CppType(); + std::for_each(begin(set), end(set), [&s](Windows::Foundation::Collections::IKeyValuePair^ pair) + { + s.insert(T::toCpp(pair->Key)); + }); + + return s; + } + + static CxType fromCpp(const CppType& s) { + auto set = ref new Platform::Collections::Map; + for (const auto& val : s) { + set->Insert(T::fromCpp(val), T::fromCpp(val)); + } + return set; + } + }; template class Map { @@ -275,9 +276,9 @@ namespace djinni { auto m = CppType(); m.reserve(map.count); - std::for_each(begin(map), end(map), [](Windows::Foundation::Collections::IKeyValuePair^ pair) + std::for_each(begin(map), end(map), [&m](Windows::Foundation::Collections::IKeyValuePair^ pair) { - m.emplace(Key::toCpp(pair->Key), Value::toCpp(pair->Value)): + m.emplace(Key::toCpp(pair->Key), Value::toCpp(pair->Value)); }); return m; @@ -285,10 +286,9 @@ namespace djinni { static CxType fromCpp(const CppType& m) { //assert(m.size() <= std::numeric_limits::max()); - auto map = ref new Platform::Collections::Map;//[NSMutableDictionary dictionaryWithCapacity:static_cast(m.size())]; + auto map = ref new Platform::Collections::Map; for(const auto& kvp : m) { map->Insert(Value::fromCpp(kvp.first), Key::fromCpp(kvp.second)); -// [map setObject:Value::Boxed::fromCpp(kvp.second) forKey:Key::Boxed::fromCpp(kvp.first)]; } return map; } From e9f4b6ae9b583d291ef03510ad52ec1047b72a01 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Mon, 3 Aug 2015 12:17:07 -0700 Subject: [PATCH 39/52] Leave off final commas --- src/source/CppGenerator.scala | 2 +- src/source/CxGenerator.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/source/CppGenerator.scala b/src/source/CppGenerator.scala index d7feba133..ef63bacd2 100644 --- a/src/source/CppGenerator.scala +++ b/src/source/CppGenerator.scala @@ -61,7 +61,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) { w.w(s"enum class $self : int").bracedSemi { for (o <- e.options) { writeDoc(w, o.doc) - w.wl(idCpp.enum(o.ident.name) + ",") + w.wl(idCpp.enum(o.ident.name) + (if(o == e.options.last) "" else ",")) } } }, diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index a4d5c94e9..98a6af99c 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -75,7 +75,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { w.w(s"public enum class $self").bracedSemi { for (o <- e.options) { writeDoc(w, o.doc) - w.wl(idCx.enum(o.ident.name) + ",") + w.wl(idCx.enum(o.ident.name) + (if(o == e.options.last) "" else ",")) } } }) From 39ff60258166e79561ee15952d29e224fd85595f Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Tue, 22 Sep 2015 16:14:13 -0700 Subject: [PATCH 40/52] Stupid awful typo. --- src/source/CxCppGenerator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/CxCppGenerator.scala b/src/source/CxCppGenerator.scala index f0bb28b3a..660437e50 100644 --- a/src/source/CxCppGenerator.scala +++ b/src/source/CxCppGenerator.scala @@ -218,7 +218,7 @@ class CxCppGenerator(spec: Spec) extends Generator(spec) { val ret = cppMarshal.fqReturnType(m.ret) val params = m.params.map(p => cppMarshal.fqParamType(p.ty) + " " + idCpp.local(p.ident)) if (m.static) { - w.wl(s"static $ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")} override") + w.wl(s"static $ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")}") } else { val constFlag = if (m.const) " const" else "" w.wl(s"$ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")}$constFlag override") From 26decdd90f5061233e0cff9ea82cb5f3b2aa8938 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Mon, 3 Aug 2015 13:26:08 -0700 Subject: [PATCH 41/52] Needed include for IInspectable --- support-lib/cx/CxWrapperCache.h | 1 + 1 file changed, 1 insertion(+) diff --git a/support-lib/cx/CxWrapperCache.h b/support-lib/cx/CxWrapperCache.h index 49dfe4bd0..d2a954456 100755 --- a/support-lib/cx/CxWrapperCache.h +++ b/support-lib/cx/CxWrapperCache.h @@ -19,6 +19,7 @@ #include #include #include +#include namespace djinni { From ceae6965ee43e5f63c5a60452d76edc2421f02bc Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Mon, 3 Aug 2015 14:18:01 -0700 Subject: [PATCH 42/52] This doesn't quite fix the problem of optional lists, but we're getting closer. Why are the literals doubled? --- src/source/CxMarshal.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index 2a5fa7f6a..f93a7a30a 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -308,7 +308,14 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { def expr(tm: MExpr, namespace: Option[String], needRef: Boolean): (String, Boolean) = { val args = tm.base match { - case MOptional => "" + case MOptional => + assert(tm.args.size == 1) + val arg = tm.args.head + arg.base match { + case MOptional => throw new AssertionError("nested optional?") + case p: MPrimitive => (p.cxBoxed, true) + case m => exprWithReference(arg, namespace, needRef) + } case MSet => if (tm.args.size == 1) (tm.args :+ tm.args(0)).map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") else tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") case MMap => tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") case d => if (tm.args.isEmpty) "" else tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") From 9851437056f7a0eff096a75cc8aff78ccef936a5 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Mon, 3 Aug 2015 14:28:10 -0700 Subject: [PATCH 43/52] Typo --- support-lib/cx/Marshal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index 78158f3df..00f5a8d21 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -191,7 +191,7 @@ namespace djinni { } static CxType fromCpp(const CppType& opt) { - return opt ? T::Boxed::fromCpp(*opt) : nil; + return opt ? T::Boxed::fromCpp(*opt) : nullptr; } }; From 8aad860244086362062dad86416de8949c8fa55b Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Tue, 4 Aug 2015 07:38:54 -0700 Subject: [PATCH 44/52] Optional types are generated correctly in Cx now. --- src/.idea/libraries/SBT__org_yaml_snakeyaml_1_15_jar.xml | 9 +++++++++ src/source/CxMarshal.scala | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 src/.idea/libraries/SBT__org_yaml_snakeyaml_1_15_jar.xml diff --git a/src/.idea/libraries/SBT__org_yaml_snakeyaml_1_15_jar.xml b/src/.idea/libraries/SBT__org_yaml_snakeyaml_1_15_jar.xml new file mode 100644 index 000000000..f02b1d962 --- /dev/null +++ b/src/.idea/libraries/SBT__org_yaml_snakeyaml_1_15_jar.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index f93a7a30a..68e8dabf5 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -279,7 +279,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { arg.base match { case MOptional => throw new AssertionError("nested optional?") case p: MPrimitive => (p.cxBoxed, true) - case m => base(arg, namespace, true) + case m => expr(arg, namespace, true) } case MList => ("Windows::Foundation::Collections::IVector", true) case MSet => ("Windows::Foundation::Collections::IMap", true) @@ -313,15 +313,15 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { val arg = tm.args.head arg.base match { case MOptional => throw new AssertionError("nested optional?") - case p: MPrimitive => (p.cxBoxed, true) - case m => exprWithReference(arg, namespace, needRef) +// case p: MPrimitive => "" + case m => "" //if (tm.args.isEmpty) "" else tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") //(tm.args[0].typename, true) } case MSet => if (tm.args.size == 1) (tm.args :+ tm.args(0)).map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") else tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") case MMap => tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") case d => if (tm.args.isEmpty) "" else tm.args.map(arg => exprWithReference(arg, namespace, needRef)).mkString("<", ", ", ">") } val (ret, ref) = base(tm, namespace, needRef) - (ret + args, ref) + (ret+ args, ref) } expr(tm, namespace, needRef) } From 18a82d5d9fb99314bbb9882395e498418882fe2b Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Wed, 5 Aug 2015 16:45:28 -0700 Subject: [PATCH 45/52] Typo. Wow. --- support-lib/cx/Marshal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index 00f5a8d21..35826c6ab 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -187,7 +187,7 @@ namespace djinni { using Boxed = Optional; static CppType toCpp(CxType cx) { - return obj ? CppType(T::Boxed::toCpp(cx)) : CppType(); + return cx ? CppType(T::Boxed::toCpp(cx)) : CppType(); } static CxType fromCpp(const CppType& opt) { From 167f9d0aa96cb3ef00019bf2f8da153ed2e04308 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Wed, 5 Aug 2015 23:36:51 -0700 Subject: [PATCH 46/52] Correct header paths, please! --- src/source/CxGenerator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index 98a6af99c..04acca1af 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -243,7 +243,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { m.ret.foreach(refs.findConvert) }) refs.cx.add("#include \"Marshal.h\"") - refs.cx.add("#include \""+ cxcppMarshal.headerName(ident.name) + "." + spec.cxcppHeaderExt + "\"") + refs.cx.add("#include \""+ spec.cxcppIncludePrefix + cxcppMarshal.headerName(ident.name) + "." + spec.cxcppHeaderExt + "\"") val self = cxMarshal.typename(ident, i) val cppSelf = cppMarshal.fqTypename(ident, i) From e377735111faa720a67f38d8398e3f1228b2b8fa Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Thu, 24 Sep 2015 11:10:44 -0700 Subject: [PATCH 47/52] Fixed namespace issue on enum declarations under the Cx covers. --- src/source/CxMarshal.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/CxMarshal.scala b/src/source/CxMarshal.scala index 68e8dabf5..07d8be763 100644 --- a/src/source/CxMarshal.scala +++ b/src/source/CxMarshal.scala @@ -51,7 +51,7 @@ class CxMarshal(spec: Spec) extends Marshal(spec) { private def ownName(tm: MExpr): String = tm.base match { case d: MDef => d.defType match { - case DEnum => withNs(Some("djinni"), s"Enum<${fqTypename(tm)}, ${fqTypename(tm)}>") + case DEnum => withNs(Some("djinni"), s"Enum<${cppMarshal.fqTypename(tm)}, ${fqTypename(tm)}>") case _ => withNs(Some(spec.cxcppNamespace), s"${ownClass(d.name)}") } case o => withNs(Some("djinni"), o match { From a7a0304642ba475b3775eb9ecf88ad1cfaaa100f Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Thu, 1 Oct 2015 14:45:01 -0700 Subject: [PATCH 48/52] Proper handling of public static const members --- src/source/CxGenerator.scala | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index 04acca1af..bc0eb76b5 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -85,7 +85,21 @@ class CxGenerator(spec: Spec) extends Generator(spec) { for (c <- consts) { w.wl writeDoc(w, c.doc) - w.wl(s"static const ${cxMarshal.fieldType(c.ty)} ${idCx.const(c.ident)};") + w.wl(s"static property ${cxMarshal.fieldType(c.ty)} ${idCx.const(c.ident)};") + w.braced { + w.wl(s"${cxMarshal.fieldType(c.ty)} get()") + w.braced { + w.wl(s"return ${idCx.const(c.ident)}_;") + } + } + } + } + + def generateHxPrivateConstants(w: IndentWriter, consts: Seq[Const]) = { + for (c <- consts) { + w.wl + writeDoc(w, c.doc) + w.wl(s"static ${cxMarshal.fieldType(c.ty)} ${idCx.const(c.ident)}_;") } } @@ -286,6 +300,8 @@ class CxGenerator(spec: Spec) extends Generator(spec) { } } //private members + w.wlOutdent("private:") + generateHxPrivateConstants(w, i.consts) w.wlOutdent("internal:") //construct from a cpp ref w.wl(s"$self(const std::shared_ptr<$cppSelf>& cppRef);") From 8ad2da20589d8e8920b8d7eed6abecf904a20a36 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 2 Oct 2015 15:33:59 -0700 Subject: [PATCH 49/52] Semicolon out of place. --- src/source/CxGenerator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index bc0eb76b5..66a7a935f 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -85,7 +85,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { for (c <- consts) { w.wl writeDoc(w, c.doc) - w.wl(s"static property ${cxMarshal.fieldType(c.ty)} ${idCx.const(c.ident)};") + w.wl(s"static property ${cxMarshal.fieldType(c.ty)} ${idCx.const(c.ident)}") w.braced { w.wl(s"${cxMarshal.fieldType(c.ty)} get()") w.braced { From 2320bb44e1064eae04e7e33763dcce3a09b5a016 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 2 Oct 2015 15:37:11 -0700 Subject: [PATCH 50/52] OH yes, needed to generate the private member too --- src/source/CxGenerator.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index 66a7a935f..e32b790aa 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -185,6 +185,9 @@ class CxGenerator(spec: Spec) extends Generator(spec) { w.wl(s"int32 CompareTo($actualSelf^ rhs);") } + w.wlOutdent("private:") + generateHxPrivateConstants(w, r.consts) + } } From 0c9222a23835718cfb3fe45d1adc7415d222ca8a Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Fri, 2 Oct 2015 15:51:23 -0700 Subject: [PATCH 51/52] Initialize constants the right way. --- src/source/CxGenerator.scala | 62 ++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/src/source/CxGenerator.scala b/src/source/CxGenerator.scala index e32b790aa..184645548 100644 --- a/src/source/CxGenerator.scala +++ b/src/source/CxGenerator.scala @@ -96,14 +96,6 @@ class CxGenerator(spec: Spec) extends Generator(spec) { } def generateHxPrivateConstants(w: IndentWriter, consts: Seq[Const]) = { - for (c <- consts) { - w.wl - writeDoc(w, c.doc) - w.wl(s"static ${cxMarshal.fieldType(c.ty)} ${idCx.const(c.ident)}_;") - } - } - - def generateCxConstants(w: IndentWriter, consts: Seq[Const], selfName: String) = { def writeCxConst(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match { case l: Long => w.w(l.toString) case d: Double if cxMarshal.fieldType(ty) == "float" => w.w(d.toString + "f") @@ -111,7 +103,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { case b: Boolean => w.w(if (b) "true" else "false") case s: String => w.w(s) case e: EnumValue => w.w(cxMarshal.typename(ty) + "::" + idCx.enum(e.ty.name + "_" + e.name)) - case v: ConstRef => w.w(selfName + "::" + idCx.const(v)) + case v: ConstRef => w.w(idCx.const(v)) case z: Map[_, _] => { // Value is record val recordMdef = ty.resolved.base.asInstanceOf[MDef] val record = recordMdef.body.asInstanceOf[Record] @@ -130,15 +122,51 @@ class CxGenerator(spec: Spec) extends Generator(spec) { } } - val skipFirst = SkipFirst() for (c <- consts) { - skipFirst{ w.wl } - w.w(s"${cxMarshal.fieldType(c.ty)} const $selfName::${idCx.const(c.ident)} = ") + w.wl + writeDoc(w, c.doc) + w.w(s"static const ${cxMarshal.fieldType(c.ty)} ${idCx.const(c.ident)}_ = ") writeCxConst(w, c.ty, c.value) w.wl(";") } } +// def generateCxConstants(w: IndentWriter, consts: Seq[Const], selfName: String) = { +// def writeCxConst(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match { +// case l: Long => w.w(l.toString) +// case d: Double if cxMarshal.fieldType(ty) == "float" => w.w(d.toString + "f") +// case d: Double => w.w(d.toString) +// case b: Boolean => w.w(if (b) "true" else "false") +// case s: String => w.w(s) +// case e: EnumValue => w.w(cxMarshal.typename(ty) + "::" + idCx.enum(e.ty.name + "_" + e.name)) +// case v: ConstRef => w.w(selfName + "::" + idCx.const(v)) +// case z: Map[_, _] => { // Value is record +// val recordMdef = ty.resolved.base.asInstanceOf[MDef] +// val record = recordMdef.body.asInstanceOf[Record] +// val vMap = z.asInstanceOf[Map[String, Any]] +// w.wl(cxMarshal.typename(ty) + "(") +// w.increase() +// // Use exact sequence +// val skipFirst = SkipFirst() +// for (f <- record.fields) { +// skipFirst {w.wl(",")} +// writeCxConst(w, f.ty, vMap.apply(f.ident.name)) +// w.w(" /* " + idCx.field(f.ident) + " */ ") +// } +// w.w(")") +// w.decrease() +// } +// } +// +// val skipFirst = SkipFirst() +// for (c <- consts) { +// skipFirst{ w.wl } +// w.w(s"${cxMarshal.fieldType(c.ty)} const $selfName::${idCx.const(c.ident)} = ") +// writeCxConst(w, c.ty, c.value) +// w.wl(";") +// } +// } + override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) { val refs = new CxRefs(ident.name) r.fields.foreach(f => refs.find(f.ty)) @@ -195,7 +223,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { if (r.consts.nonEmpty || r.derivingTypes.nonEmpty) { writeCxFile(cxName, origin, refs.cx, w => { - generateCxConstants(w, r.consts, actualSelf) +// generateCxConstants(w, r.consts, actualSelf) if (r.derivingTypes.contains(DerivingType.Eq)) { w.wl @@ -270,7 +298,7 @@ class CxGenerator(spec: Spec) extends Generator(spec) { writeCxTypeParams(w, typeParams) if (i.ext.cx) w.wl(s"public interface class $self").bracedSemi { // Constants -// generateHxConstants(w, i.consts) //TODO no can do! Not gonna happen. Nuuh. We can make this a property with no setter and agetter that reaches into C++ land tho + generateHxConstants(w, i.consts) // Methods for (m <- i.methods) { w.wl @@ -315,9 +343,9 @@ class CxGenerator(spec: Spec) extends Generator(spec) { // Cx only generated in need of Constants writeCxFile(ident, origin, refs.cx, w => { if (! i.ext.cx) { - if (i.consts.nonEmpty) { - generateCxConstants(w, i.consts, self) - } +// if (i.consts.nonEmpty) { +// generateCxConstants(w, i.consts, self) +// } //constructor w.wl(s"$self::$self(const std::shared_ptr<$cppSelf>& cppRef)") w.braced { From 2f2be44d3a8831f3195528ee9e64948bf71d6e94 Mon Sep 17 00:00:00 2001 From: Don Goodman-Wilson Date: Wed, 14 Oct 2015 15:23:14 -0700 Subject: [PATCH 52/52] In Cpp/Cx, empty Platform::String^ is null. Who knew? --- support-lib/cx/Marshal.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/support-lib/cx/Marshal.h b/support-lib/cx/Marshal.h index 35826c6ab..d5d3beaec 100755 --- a/support-lib/cx/Marshal.h +++ b/support-lib/cx/Marshal.h @@ -110,10 +110,14 @@ namespace djinni { using Boxed = String; static CppType toCpp(CxType string) { - assert(string); - std::wstring wstring{ string->Data() }; - std::wstring_convert, wchar_t> converter; - return converter.to_bytes(wstring); + //Empty strings are null. + if(string) + { + std::wstring wstring{ string->Data() }; + std::wstring_convert, wchar_t> converter; + return converter.to_bytes(wstring); + } + return {""}; } static CxType fromCpp(const CppType& string) {