Commit 708d6218 authored by j4cbo's avatar j4cbo

Merge pull request #95 from mknejp/feature/extern-types-2

Awesome. Thanks for all the rebasing!
parents 6058bd09 b704a7c3
...@@ -245,6 +245,141 @@ interface/record prefixed. Example: ...@@ -245,6 +245,141 @@ interface/record prefixed. Example:
will be `RecordWithConst::CONST_VALUE` in C++, `RecordWithConst.CONST_VALUE` in Java, and will be `RecordWithConst::CONST_VALUE` in C++, `RecordWithConst.CONST_VALUE` in Java, and
`RecordWithConstConstValue` in Objective-C. `RecordWithConstConstValue` in Objective-C.
## Modularization and Library Support
When generating the interface for your project and wish to make it available to other users
in all of C++/Objective-C/Java you can tell Djinni to generate a special YAML file as part
of the code generation process. This file then contains all the information Djinni requires
to include your types in a different project. Instructing Djinni to create these YAML files
is controlled by the follwoing arguments:
- `--yaml-out`: The output folder for YAML files (Generator disabled if unspecified).
- `--yaml-out-file`: If specified all types are merged into a single YAML file instead of generating one file per type (relative to `--yaml-out`).
- `--yaml-prefix`: The prefix to add to type names stored in YAML files (default: `""`).
Such a YAML file looks as follows:
```yml
---
name: mylib_record1
typedef: 'record +c deriving(eq, ord)'
params: []
prefix: 'mylib'
cpp:
typename: '::mylib::Record1'
header: '"MyLib/Record1.hpp"'
byValue: false
objc:
typename: 'MLBRecord1'
header: '"MLB/MLBRecord1.h"'
boxed: 'MLBRecord1'
pointer: true
hash: '%s.hash'
objcpp:
translator: '::mylib::djinni::objc::Record1'
header: '"mylib/djinni/objc/Record1.hpp"'
java:
typename: 'com.example.mylib.Record1'
boxed: 'com.example.mylib.Record1'
reference: true
generic: true
hash: '%s.hashCode()'
jni:
translator: '::mylib::djinni::jni::Record1'
header: '"Duration-jni.hpp"'
typename: jobject
typeSignature: 'Lcom/example/mylib/Record1;'
---
name: mylib_interface1
typedef: 'interface +j +o'
(...)
---
name: mylib_enum1
typedef: 'enum'
(...)
```
Each document in the YAML file describes one extern type.
A full documentation of all fields is available in `example/example.yaml`. You can also check
the files `test-suite/djinni/date.yaml` and `test-suite/djinni/duration.yaml` for some
real working examples of what you can do with it.
To use a library type in your project simply include it in your IDL file and refer to it using
its name identifier:
```
@extern "mylib.yaml"
client_interface = interface +c {
foo(): mylib_record1;
}
```
These files can be created by hand as long as you follow the required format. This allows you
to support types not generated by Djinni. See `test-suite/djinni/duration.yaml` and the
accompanying translators in `test-suite/handwritten-src/cpp/Duration-objc.hpp` and
`test-suite/handwritten-src/cpp/Duration-jni.hpp` for an advanced example. Handwritten
translators implement the following concept:
```cpp
// For C++ <-> Objective-C
struct Record1
{
using CppType = ::mylib::Record1;
using ObjcType = MLBRecord1*;
static CppType toCpp(ObjcType o) { return /* your magic here */; }
static ObjcType fromCpp(CppType c) { return /* your magic here */; }
// Option 1: use this if no boxing is required
using Boxed = Record1;
// Option 2: or this if you do need dedicated boxing behavior
struct Boxed
{
using ObjcType = MLBRecord1Special*;
static CppType toCpp(ObjcType o) { return /* your magic here */; }
static ObjcType fromCpp(CppType c) { return /* your magic here */; }
}
};
```
```cpp
// For C++ <-> JNI
#include "djinni_support.hpp"
struct Record1
{
using CppType = ::mylib::Record1;
using JniType = jobject;
static CppType toCpp(JniType j) { return /* your magic here */; }
// The return type *must* be LocalRef<T> if T is not a primitive!
static ::djinni::LocalRef<jobject> JniType fromCpp(CppType c) { return /* your magic here */; }
using Boxed = Record1;
};
```
For `interface` classes the `CppType` alias is expected to be a `std::shared_ptr<T>`.
Be sure to put the translators into representative and distinct namespaces.
If your type is generic the translator takes the same number of template parameters.
At usage each is instantiated with the translators of the respective type argument.
```cpp
template<class A, class B>
struct Record1
{
using CppType = ::mylib::Record1<typename A::CppType, typename B::CppType>;
using ObjcType = MLBRecord1*;
static CppType toCpp(ObjcType o)
{
// Use A::toCpp() and B::toCpp() if necessary
return /* your magic here */;
}
static ObjcType fromCpp(CppType c)
{
// Use A::fromCpp() and B::fromCpp() if necessary
return /* your magic here */;
}
using Boxed = Record1;
};
```
## Miscellaneous ## Miscellaneous
### Record constructors / initializers ### Record constructors / initializers
Djinni does not permit custom constructors for records or interfaces, since there would be Djinni does not permit custom constructors for records or interfaces, since there would be
......
# This is an example YAML file for being imported in other projects.
# It holds all necessary information for Djinni to integrate foreign types into the generated code.
# All fields are mandatory.
---
# The name to refer to this type in other .djinni files.
# It must be unique in the entire Djinni run, so you should pick a unique prefix for your framework/library.
name: mylib_record1
# Specifies what kind of type this is.
# Supports the same syntax as is used to declare types in .djinni files.
# Examples: 'interface +c', 'record deriving(eq, or)', 'enum', 'interface +j +o'
# This determines how Djinni integrates this type into function parameters, fields or return types and operators.
typedef: 'record +c deriving(eq, ord)'
# The (potentially empty) list of required type parameters.
params: [type_param1, type_param2]
# This string is stripped from the value specified under "name" to ensure Djinni is referencing the correct typename in code.
# May be an empty string if you don't have a prefix (bad!)
prefix: 'mylib'
cpp:
# The name of this type in C++ (without template arguments). Should be fully qualified.
typename: '::mylib::Record1'
# The header required in C++ to use your type. Must include "" or <>.
header: '"MyLib/Record1.hpp"'
# Only used for "record" types: determines whether it should be passed by-value in C++.
# If this is false it is always passed as const&
byValue: false
objc:
# The name of this type in Objective-C.
typename: 'MLBRecord1'
# The header required in Objective-C to use your type.
header: '"MLB/MLBRecord1.h"'
# Only used for "record" types: determines the type used when boxing the record is required.
# Should not contain the pointer asterisk "*", protocols are not supported.
# This fiels is the same as "typename" most of the time as records are typically NSObjects and require no special boxing.
# However, some may not, for example NSTimeInterval is boxed to NSNumber.
boxed: 'MLBRecord1'
# Specifies whether the unboxed type is a pointer.
pointer: true
# If the type is a "record" and has "eq" deriving then this string must not be empty.
# It declares a well-formed expression with a single "%s" format placeholder replaced with the variable for which the hash code is needed
hash: '%s.hash'
objcpp:
# The fully qualified name of the class containing the toCpp/fromCpp methods.
translator: '::mylib::djinni::objc::Record1'
# Where to find the translator class.
header: '"mylib/djinni/objc/Record1.hpp"'
java:
# The name of the (fully qualified) Java type to be used.
typename: 'com.example.mylib.Record1'
# Only used for "record" types: determines the type used when boxing the record is required.
# This field is the same as "typename" most of the time as records are typically Objects and require no special boxing.
# However maybe your record has a dedicated boxed type and this field allows you to control that.
boxed: 'com.example.mylib.Record1'
# If this is true "typename" is an Object reference (and not a builtin).
reference: true
# Controls whether the type parameters as specified in "params" are forwarded as generics to Java.
# This is useful when templates are only used in C++ (e.g. std::chrono::duration<rep, period> versus java.time.Duration)
# This should be true by default (even if your type has no parameters) and only set to false if required
generic: true
# If the type is a "record" and has "eq" deriving then this string must not be empty.
# It declares a well-formed expression with a single "%s" format placeholder replaced with the variable for which the hash code is needed
hash: '%s.hashCode()'
jni:
# The fully qualified name of the class containing the toCpp/fromCpp methods.
translator: '::mylib::djinni::jni::Record1'
# Where to find the translator class.
header: '"mylib/djinni/jni/Record1.hpp"'
# The type used for representations in JNI (jobject, jint, jbyteArray, etc)
typename: jobject
# The mangled type signature of your type to be found by JNI.
# See the JNI docs for its format
typeSignature: 'Lcom/example/mylib/Record1;'
...@@ -2,7 +2,10 @@ import com.typesafe.sbt.SbtStartScript ...@@ -2,7 +2,10 @@ import com.typesafe.sbt.SbtStartScript
scalaVersion := "2.11.0" scalaVersion := "2.11.0"
libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.1" libraryDependencies ++= Seq(
"org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.1",
"org.yaml" % "snakeyaml" % "1.15"
)
scalaSource in Compile := baseDirectory.value / "source" scalaSource in Compile := baseDirectory.value / "source"
......
...@@ -47,7 +47,7 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { ...@@ -47,7 +47,7 @@ class CppMarshal(spec: Spec) extends Marshal(spec) {
case d: MDef => d.defType match { case d: MDef => d.defType match {
case DEnum | DRecord => case DEnum | DRecord =>
if (d.name != exclude) { if (d.name != exclude) {
List(ImportRef(q(spec.cppIncludePrefix + spec.cppFileIdentStyle(d.name) + "." + spec.cppHeaderExt))) List(ImportRef(include(d.name)))
} else { } else {
List() List()
} }
...@@ -58,8 +58,16 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { ...@@ -58,8 +58,16 @@ class CppMarshal(spec: Spec) extends Marshal(spec) {
List(ImportRef("<memory>")) List(ImportRef("<memory>"))
} }
} }
case e: MExtern => e.defType match {
// Do not forward declare extern types, they might be in arbitrary namespaces.
// This isn't a problem as extern types cannot cause dependency cycles with types being generated here
case DInterface => List(ImportRef("<memory>"), ImportRef(e.cpp.header))
case _ => List(ImportRef(e.cpp.header))
}
case p: MParam => List() case p: MParam => List()
} }
def include(ident: String): String = q(spec.cppIncludePrefix + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt)
private def toCppType(ty: TypeRef, namespace: Option[String] = None): String = toCppType(ty.resolved, namespace) private def toCppType(ty: TypeRef, namespace: Option[String] = None): String = toCppType(ty.resolved, namespace)
private def toCppType(tm: MExpr, namespace: Option[String]): String = { private def toCppType(tm: MExpr, namespace: Option[String]): String = {
...@@ -78,6 +86,10 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { ...@@ -78,6 +86,10 @@ class CppMarshal(spec: Spec) extends Marshal(spec) {
case DRecord => withNs(namespace, idCpp.ty(d.name)) case DRecord => withNs(namespace, idCpp.ty(d.name))
case DInterface => s"std::shared_ptr<${withNs(namespace, idCpp.ty(d.name))}>" case DInterface => s"std::shared_ptr<${withNs(namespace, idCpp.ty(d.name))}>"
} }
case e: MExtern => e.defType match {
case DInterface => s"std::shared_ptr<${e.cpp.typename}>"
case _ => e.cpp.typename
}
case p: MParam => idCpp.typeParam(p.name) case p: MParam => idCpp.typeParam(p.name)
} }
def expr(tm: MExpr): String = { def expr(tm: MExpr): String = {
...@@ -87,21 +99,32 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { ...@@ -87,21 +99,32 @@ class CppMarshal(spec: Spec) extends Marshal(spec) {
expr(tm) expr(tm)
} }
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.cpp.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
}
// this can be used in c++ generation to know whether a const& should be applied to the parameter or not // this can be used in c++ generation to know whether a const& should be applied to the parameter or not
private def toCppParamType(tm: MExpr, namespace: Option[String] = None): String = { private def toCppParamType(tm: MExpr, namespace: Option[String] = None): String = {
val cppType = toCppType(tm, namespace) val cppType = toCppType(tm, namespace)
val refType = "const " + cppType + " &" val refType = "const " + cppType + " &"
val valueType = cppType val valueType = cppType
if(byValue(tm)) valueType else refType
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)
} }
} }
...@@ -8,7 +8,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { ...@@ -8,7 +8,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
// For JNI typename() is always fully qualified and describes the mangled Java type to be used in field/method signatures // For JNI typename() is always fully qualified and describes the mangled Java type to be used in field/method signatures
override def typename(tm: MExpr): String = javaTypeSignature(tm) override def typename(tm: MExpr): String = javaTypeSignature(tm)
def typename(name: String, ty: TypeDef): String = throw new AssertionError("not applicable") def typename(name: String, ty: TypeDef) = s"L${undecoratedTypename(name, ty)};"
override def fqTypename(tm: MExpr): String = typename(tm) override def fqTypename(tm: MExpr): String = typename(tm)
def fqTypename(name: String, ty: TypeDef): String = typename(name, ty) def fqTypename(name: String, ty: TypeDef): String = typename(name, ty)
...@@ -35,10 +35,13 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { ...@@ -35,10 +35,13 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
def references(m: Meta, exclude: String = ""): Seq[SymbolReference] = m match { def references(m: Meta, exclude: String = ""): Seq[SymbolReference] = m match {
case o: MOpaque => List(ImportRef(q(spec.jniBaseLibIncludePrefix + "Marshal.hpp"))) case o: MOpaque => List(ImportRef(q(spec.jniBaseLibIncludePrefix + "Marshal.hpp")))
case d: MDef => List(ImportRef(q(spec.jniIncludePrefix + spec.jniFileIdentStyle(d.name) + "." + spec.cppHeaderExt))) case d: MDef => List(ImportRef(include(d.name)))
case e: MExtern => List(ImportRef(e.jni.header))
case _ => List() case _ => List()
} }
def include(ident: String) = q(spec.jniIncludePrefix + spec.jniFileIdentStyle(ident) + "." + spec.cppHeaderExt)
def toJniType(ty: TypeRef): String = toJniType(ty.resolved, false) def toJniType(ty: TypeRef): String = toJniType(ty.resolved, false)
def toJniType(m: MExpr, needRef: Boolean): String = m.base match { def toJniType(m: MExpr, needRef: Boolean): String = m.base match {
case p: MPrimitive => if (needRef) "jobject" else p.jniName case p: MPrimitive => if (needRef) "jobject" else p.jniName
...@@ -46,6 +49,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { ...@@ -46,6 +49,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
case MOptional => toJniType(m.args.head, true) case MOptional => toJniType(m.args.head, true)
case MBinary => "jbyteArray" case MBinary => "jbyteArray"
case tp: MParam => helperClass(tp.name) + "::JniType" case tp: MParam => helperClass(tp.name) + "::JniType"
case e: MExtern => helperClass(m) + (if(needRef) "::Boxed" else "") + "::JniType"
case _ => "jobject" case _ => "jobject"
} }
...@@ -70,6 +74,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { ...@@ -70,6 +74,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
case MSet => "Ljava/util/HashSet;" case MSet => "Ljava/util/HashSet;"
case MMap => "Ljava/util/HashMap;" case MMap => "Ljava/util/HashMap;"
} }
case e: MExtern => e.jni.typeSignature
case MParam(_) => "Ljava/lang/Object;" case MParam(_) => "Ljava/lang/Object;"
case d: MDef => s"L${undecoratedTypename(d.name, d.body)};" case d: MDef => s"L${undecoratedTypename(d.name, d.body)};"
} }
...@@ -78,8 +83,9 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { ...@@ -78,8 +83,9 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
params.map(f => typename(f.ty)).mkString("(", "", ")") + ret.fold("V")(typename) params.map(f => typename(f.ty)).mkString("(", "", ")") + ret.fold("V")(typename)
} }
private def helperName(tm: MExpr): String = tm.base match { def helperName(tm: MExpr): String = tm.base match {
case d: MDef => withNs(Some(spec.jniNamespace), helperClass(d.name)) case d: MDef => withNs(Some(spec.jniNamespace), helperClass(d.name))
case e: MExtern => e.jni.translator
case o => withNs(Some("djinni"), o match { case o => withNs(Some("djinni"), o match {
case p: MPrimitive => p.idlName match { case p: MPrimitive => p.idlName match {
case "i8" => "I8" case "i8" => "I8"
...@@ -98,6 +104,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { ...@@ -98,6 +104,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
case MSet => "Set" case MSet => "Set"
case MMap => "Map" case MMap => "Map"
case d: MDef => throw new AssertionError("unreachable") case d: MDef => throw new AssertionError("unreachable")
case e: MExtern => throw new AssertionError("unreachable")
case p: MParam => throw new AssertionError("not applicable") case p: MParam => throw new AssertionError("not applicable")
}) })
} }
......
...@@ -285,6 +285,15 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { ...@@ -285,6 +285,15 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
case DEnum => w.w(s"this.${idJava.field(f.ident)} == other.${idJava.field(f.ident)}") case DEnum => w.w(s"this.${idJava.field(f.ident)} == other.${idJava.field(f.ident)}")
case _ => throw new AssertionError("Unreachable") case _ => throw new AssertionError("Unreachable")
} }
case e: MExtern => e.defType match {
case DRecord => if(e.java.reference) {
w.w(s"this.${idJava.field(f.ident)}.equals(other.${idJava.field(f.ident)})")
} else {
w.w(s"this.${idJava.field(f.ident)} == other.${idJava.field(f.ident)}")
}
case DEnum => w.w(s"this.${idJava.field(f.ident)} == other.${idJava.field(f.ident)}")
case _ => throw new AssertionError("Unreachable")
}
case _ => throw new AssertionError("Unreachable") case _ => throw new AssertionError("Unreachable")
} }
} }
...@@ -317,6 +326,11 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { ...@@ -317,6 +326,11 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
case "boolean" => s"(${idJava.field(f.ident)} ? 1 : 0)" case "boolean" => s"(${idJava.field(f.ident)} ? 1 : 0)"
case _ => throw new AssertionError("Unreachable") case _ => throw new AssertionError("Unreachable")
} }
case e: MExtern => e.defType match {
case DRecord => "(" + e.java.hash.format(idJava.field(f.ident)) + ")"
case DEnum => s"${idJava.field(f.ident)}.hashCode()"
case _ => throw new AssertionError("Unreachable")
}
case _ => throw new AssertionError("Unreachable") case _ => throw new AssertionError("Unreachable")
} }
w.wl(s"hashCode = hashCode * $multiplier + $fieldHashCode;") w.wl(s"hashCode = hashCode * $multiplier + $fieldHashCode;")
...@@ -327,6 +341,18 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { ...@@ -327,6 +341,18 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
} }
if (r.derivingTypes.contains(DerivingType.Ord)) { if (r.derivingTypes.contains(DerivingType.Ord)) {
def primitiveCompare(ident: Ident) {
w.wl(s"if (this.${idJava.field(ident)} < other.${idJava.field(ident)}) {").nested {
w.wl(s"tempResult = -1;")
}
w.wl(s"} else if (this.${idJava.field(ident)} > other.${idJava.field(ident)}) {").nested {
w.wl(s"tempResult = 1;")
}
w.wl(s"} else {").nested {
w.wl(s"tempResult = 0;")
}
w.wl("}")
}
w.wl w.wl
w.wl("@Override") w.wl("@Override")
val nonnullAnnotation = javaNonnullAnnotation.map(_ + " ").getOrElse("") val nonnullAnnotation = javaNonnullAnnotation.map(_ + " ").getOrElse("")
...@@ -335,22 +361,17 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { ...@@ -335,22 +361,17 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
for (f <- r.fields) { for (f <- r.fields) {
f.ty.resolved.base match { f.ty.resolved.base match {
case MString => w.wl(s"tempResult = this.${idJava.field(f.ident)}.compareTo(other.${idJava.field(f.ident)});") case MString => w.wl(s"tempResult = this.${idJava.field(f.ident)}.compareTo(other.${idJava.field(f.ident)});")
case t: MPrimitive => case t: MPrimitive => primitiveCompare(f.ident)
w.wl(s"if (this.${idJava.field(f.ident)} < other.${idJava.field(f.ident)}) {").nested {
w.wl(s"tempResult = -1;")
}
w.wl(s"} else if (this.${idJava.field(f.ident)} > other.${idJava.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 df: MDef => df.defType match {
case DRecord => w.wl(s"tempResult = this.${idJava.field(f.ident)}.compareTo(other.${idJava.field(f.ident)});") case DRecord => w.wl(s"tempResult = this.${idJava.field(f.ident)}.compareTo(other.${idJava.field(f.ident)});")
case DEnum => w.w(s"tempResult = this.${idJava.field(f.ident)}.compareTo(other.${idJava.field(f.ident)});") case DEnum => w.w(s"tempResult = this.${idJava.field(f.ident)}.compareTo(other.${idJava.field(f.ident)});")
case _ => throw new AssertionError("Unreachable") case _ => throw new AssertionError("Unreachable")
} }
case e: MExtern => e.defType match {
case DRecord => if(e.java.reference) w.wl(s"tempResult = this.${idJava.field(f.ident)}.compareTo(other.${idJava.field(f.ident)});") else primitiveCompare(f.ident)
case DEnum => w.w(s"tempResult = this.${idJava.field(f.ident)}.compareTo(other.${idJava.field(f.ident)});")
case _ => throw new AssertionError("Unreachable")
}
case _ => throw new AssertionError("Unreachable") case _ => throw new AssertionError("Unreachable")
} }
w.w("if (tempResult != 0)").braced { w.w("if (tempResult != 0)").braced {
......
...@@ -44,17 +44,28 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) { ...@@ -44,17 +44,28 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) {
ty.resolved.base match { ty.resolved.base match {
case MOptional => javaNullableAnnotation case MOptional => javaNullableAnnotation
case p: MPrimitive => None case p: MPrimitive => None
case m: MDef => case m: MDef => m.defType match {
m.defType match {
case DInterface => javaNullableAnnotation case DInterface => javaNullableAnnotation
case DEnum => javaNonnullAnnotation case DEnum => javaNonnullAnnotation
case DRecord => javaNonnullAnnotation case DRecord => javaNonnullAnnotation
} }
case e: MExtern => e.defType match {
case DInterface => javaNullableAnnotation
case DRecord => if(e.java.reference) javaNonnullAnnotation else None
case DEnum => javaNonnullAnnotation
}
case _ => javaNonnullAnnotation case _ => javaNonnullAnnotation
} }
} }
def isReference(td: TypeDecl) = td.body match {
case i: Interface => true
case r: Record => true
case e: Enum => true
}
private def toJavaType(tm: MExpr, packageName: Option[String]): String = { private def toJavaType(tm: MExpr, packageName: Option[String]): String = {
def args(tm: MExpr) = if (tm.args.isEmpty) "" else tm.args.map(f(_, true)).mkString("<", ", ", ">")
def f(tm: MExpr, needRef: Boolean): String = { def f(tm: MExpr, needRef: Boolean): String = {
tm.base match { tm.base match {
case MOptional => case MOptional =>
...@@ -66,8 +77,8 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) { ...@@ -66,8 +77,8 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) {
case MOptional => throw new AssertionError("nested optional?") case MOptional => throw new AssertionError("nested optional?")
case m => f(arg, true) case m => f(arg, true)
} }
case e: MExtern => (if(needRef) e.java.boxed else e.java.typename) + (if(e.java.generic) args(tm) else "")
case o => case o =>
val args = if (tm.args.isEmpty) "" else tm.args.map(f(_, true)).mkString("<", ", ", ">")
val base = o match { val base = o match {
case p: MPrimitive => if (needRef) p.jBoxed else p.jName case p: MPrimitive => if (needRef) p.jBoxed else p.jName
case MString => "String" case MString => "String"
...@@ -78,9 +89,10 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) { ...@@ -78,9 +89,10 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) {
case MSet => "HashSet" case MSet => "HashSet"
case MMap => "HashMap" case MMap => "HashMap"
case d: MDef => withPackage(packageName, idJava.ty(d.name)) case d: MDef => withPackage(packageName, idJava.ty(d.name))
case e: MExtern => throw new AssertionError("unreachable")
case p: MParam => idJava.typeParam(p.name) case p: MParam => idJava.typeParam(p.name)
} }
base + args base + args(tm)
} }
} }
f(tm, false) f(tm, false)
......
...@@ -68,6 +68,9 @@ object Main { ...@@ -68,6 +68,9 @@ object Main {
var inFileListPath: Option[File] = None var inFileListPath: Option[File] = None
var outFileListPath: Option[File] = None var outFileListPath: Option[File] = None
var skipGeneration: Boolean = false var skipGeneration: Boolean = false
var yamlOutFolder: Option[File] = None
var yamlOutFile: Option[String] = None
var yamlPrefix: String = ""
val argParser = new scopt.OptionParser[Unit]("djinni") { val argParser = new scopt.OptionParser[Unit]("djinni") {
...@@ -153,6 +156,14 @@ object Main { ...@@ -153,6 +156,14 @@ object Main {
.text("The namespace name to use for generated Objective-C++ classes.") .text("The namespace name to use for generated Objective-C++ classes.")
opt[String]("objc-base-lib-include-prefix").valueName("...").foreach(x => objcBaseLibIncludePrefix = x) 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.") .text("The Objective-C++ base library's include path, relative to the Objective-C++ classes.")
note("")
opt[File]("yaml-out").valueName("<out-folder>").foreach(x => yamlOutFolder = Some(x))
.text("The output folder for YAML files (Generator disabled if unspecified).")
opt[String]("yaml-out-file").valueName("<out-file>").foreach(x => yamlOutFile = Some(x))
.text("If specified all types are merged into a single YAML file instead of generating one file per type (relative to --yaml-out).")
opt[String]("yaml-prefix").valueName("<pre>").foreach(yamlPrefix = _)
.text("The prefix to add to type names stored in YAML files (default: \"\").")
note("")
opt[File]("list-in-files").valueName("<list-in-files>").foreach(x => inFileListPath = Some(x)) opt[File]("list-in-files").valueName("<list-in-files>").foreach(x => inFileListPath = Some(x))
.text("Optional file in which to write the list of input files parsed.") .text("Optional file in which to write the list of input files parsed.")
opt[File]("list-out-files").valueName("<list-out-files>").foreach(x => outFileListPath = Some(x)) opt[File]("list-out-files").valueName("<list-out-files>").foreach(x => outFileListPath = Some(x))
...@@ -282,7 +293,10 @@ object Main { ...@@ -282,7 +293,10 @@ object Main {
objcppNamespace, objcppNamespace,
objcBaseLibIncludePrefix, objcBaseLibIncludePrefix,
outFileListWriter, outFileListWriter,
skipGeneration) skipGeneration,
yamlOutFolder,
yamlOutFile,
yamlPrefix)
try { try {
......
...@@ -285,6 +285,15 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) { ...@@ -285,6 +285,15 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
case DEnum => w.w(s"self.${idObjc.field(f.ident)} == typedOther.${idObjc.field(f.ident)}") case DEnum => w.w(s"self.${idObjc.field(f.ident)} == typedOther.${idObjc.field(f.ident)}")
case _ => throw new AssertionError("Unreachable") case _ => throw new AssertionError("Unreachable")
} }
case e: MExtern => e.defType match {
case DRecord => if(e.objc.pointer) {
w.w(s"[self.${idObjc.field(f.ident)} isEqual:typedOther.${idObjc.field(f.ident)}]")
} else {
w.w(s"self.${idObjc.field(f.ident)} == typedOther.${idObjc.field(f.ident)}")
}
case DEnum => w.w(s"self.${idObjc.field(f.ident)} == typedOther.${idObjc.field(f.ident)}")
case _ => throw new AssertionError("Unreachable")
}
case _ => throw new AssertionError("Unreachable") case _ => throw new AssertionError("Unreachable")
} }
} }
...@@ -311,6 +320,11 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) { ...@@ -311,6 +320,11 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
case DEnum => w.w(s"(NSUInteger)self.${idObjc.field(f.ident)}") case DEnum => w.w(s"(NSUInteger)self.${idObjc.field(f.ident)}")
case _ => w.w(s"self.${idObjc.field(f.ident)}.hash") case _ => w.w(s"self.${idObjc.field(f.ident)}.hash")
} }
case e: MExtern => e.defType match {
case DEnum => w.w(s"(NSUInteger)self.${idObjc.field(f.ident)}")
case DRecord => w.w("(" + e.objc.hash.format("self." + idObjc.field(f.ident)) + ")")
case _ => throw new AssertionError("Unreachable")
}
case _ => w.w(s"self.${idObjc.field(f.ident)}.hash") case _ => w.w(s"self.${idObjc.field(f.ident)}.hash")
} }
} }
...@@ -345,6 +359,11 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) { ...@@ -345,6 +359,11 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
case DEnum => generatePrimitiveOrder(f.ident, w) case DEnum => generatePrimitiveOrder(f.ident, w)
case _ => throw new AssertionError("Unreachable") case _ => throw new AssertionError("Unreachable")
} }
case e: MExtern => e.defType match {
case DRecord => if(e.objc.pointer) w.wl(s"tempResult = [self.${idObjc.field(f.ident)} compare:other.${idObjc.field(f.ident)}];") else generatePrimitiveOrder(f.ident, w)
case DEnum => generatePrimitiveOrder(f.ident, w)
case _ => throw new AssertionError("Unreachable")
}
case _ => throw new AssertionError("Unreachable") case _ => throw new AssertionError("Unreachable")
} }
w.w("if (tempResult != NSOrderedSame)").braced { w.w("if (tempResult != NSOrderedSame)").braced {
...@@ -374,6 +393,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) { ...@@ -374,6 +393,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
}) })
} }
// TODO: this should be in ObjcMarshal
// Return value: (Type_Name, Is_Class_Or_Not) // Return value: (Type_Name, Is_Class_Or_Not)
def toObjcType(ty: TypeRef): (String, Boolean) = toObjcType(ty.resolved, false) def toObjcType(ty: TypeRef): (String, Boolean) = toObjcType(ty.resolved, false)
def toObjcType(ty: TypeRef, needRef: Boolean): (String, Boolean) = toObjcType(ty.resolved, needRef) def toObjcType(ty: TypeRef, needRef: Boolean): (String, Boolean) = toObjcType(ty.resolved, needRef)
...@@ -406,6 +426,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) { ...@@ -406,6 +426,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
val ext = d.body.asInstanceOf[Interface].ext val ext = d.body.asInstanceOf[Interface].ext
if (ext.cpp) (s"${idObjc.ty(d.name)}*", false) else (s"id<${idObjc.ty(d.name)}>", false) if (ext.cpp) (s"${idObjc.ty(d.name)}*", false) else (s"id<${idObjc.ty(d.name)}>", false)
} }
case e: MExtern => if(needRef) (e.objc.boxed, true) else (e.objc.typename, e.objc.pointer)
case p: MParam => throw new AssertionError("Parameter should not happen at Obj-C top level") case p: MParam => throw new AssertionError("Parameter should not happen at Obj-C top level")
} }
base base
...@@ -414,6 +435,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) { ...@@ -414,6 +435,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
f(tm, needRef) f(tm, needRef)
} }
// TODO: this should be in ObjcMarshal
def toObjcTypeDef(ty: TypeRef): String = toObjcTypeDef(ty.resolved, false) def toObjcTypeDef(ty: TypeRef): String = toObjcTypeDef(ty.resolved, false)
def toObjcTypeDef(ty: TypeRef, needRef: Boolean): String = toObjcTypeDef(ty.resolved, needRef) def toObjcTypeDef(ty: TypeRef, needRef: Boolean): String = toObjcTypeDef(ty.resolved, needRef)
def toObjcTypeDef(tm: MExpr): String = toObjcTypeDef(tm, false) def toObjcTypeDef(tm: MExpr): String = toObjcTypeDef(tm, false)
......
...@@ -16,15 +16,22 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { ...@@ -16,15 +16,22 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) {
def fqTypename(name: String, ty: TypeDef): String = typename(name, ty) def fqTypename(name: String, ty: TypeDef): String = typename(name, ty)
def nullability(tm: MExpr): Option[String] = { def nullability(tm: MExpr): Option[String] = {
val nonnull = Some("nonnull")
val nullable = Some("nullable")
tm.base match { tm.base match {
case MOptional => Some("nullable") case MOptional => nullable
case MPrimitive(_,_,_,_,_,_,_,_) => None case MPrimitive(_,_,_,_,_,_,_,_) => None
case d: MDef => d.defType match { case d: MDef => d.defType match {
case DEnum => None case DEnum => None
case DInterface => Some("nullable") case DInterface => nullable
case DRecord => Some("nonnull") case DRecord => nonnull
} }
case _ => Some("nonnull") case e: MExtern => e.defType match {
case DEnum => None
case DInterface => nullable
case DRecord => if(e.objc.pointer) nonnull else None
}
case _ => nonnull
} }
} }
...@@ -47,7 +54,7 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { ...@@ -47,7 +54,7 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) {
List(ImportRef("<Foundation/Foundation.h>")) List(ImportRef("<Foundation/Foundation.h>"))
case d: MDef => d.defType match { case d: MDef => d.defType match {
case DEnum => case DEnum =>
List(ImportRef(q(spec.objcIncludePrefix + headerName(d.name)))) List(ImportRef(include(d.name)))
case DInterface => case DInterface =>
val ext = d.body.asInstanceOf[Interface].ext val ext = d.body.asInstanceOf[Interface].ext
if (ext.cpp && !ext.objc) { if (ext.cpp && !ext.objc) {
...@@ -61,10 +68,24 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { ...@@ -61,10 +68,24 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) {
val prefix = if (r.ext.objc) "../" else "" val prefix = if (r.ext.objc) "../" else ""
List(ImportRef(q(spec.objcIncludePrefix + prefix + headerName(d.name)))) List(ImportRef(q(spec.objcIncludePrefix + prefix + headerName(d.name))))
} }
case e: MExtern => List(ImportRef(e.objc.header))
case p: MParam => List() case p: MParam => List()
} }
def headerName(ident: String): String = idObjc.ty(ident) + "." + spec.objcHeaderExt def headerName(ident: String) = idObjc.ty(ident) + "." + spec.objcHeaderExt
def include(ident: String) = q(spec.objcIncludePrefix + headerName(ident))
def isPointer(td: TypeDecl) = td.body match {
case i: Interface => true
case r: Record => true
case e: Enum => false
}
def boxedTypename(td: TypeDecl) = td.body match {
case i: Interface => typename(td.ident, i)
case r: Record => typename(td.ident, r)
case e: Enum => "NSNumber"
}
// Return value: (Type_Name, Is_Class_Or_Not) // Return value: (Type_Name, Is_Class_Or_Not)
def toObjcType(ty: TypeRef): (String, Boolean) = toObjcType(ty.resolved, false) def toObjcType(ty: TypeRef): (String, Boolean) = toObjcType(ty.resolved, false)
...@@ -101,6 +122,10 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) { ...@@ -101,6 +122,10 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) {
else else
(s"id<${idObjc.ty(d.name)}>", false) (s"id<${idObjc.ty(d.name)}>", false)
} }
case e: MExtern => e.body match {
case i: Interface => if(i.ext.objc) (s"id<${e.objc.typename}>", false) else (e.objc.typename, true)
case _ => if(needRef) (e.objc.boxed, true) else (e.objc.typename, e.objc.pointer)
}
case p: MParam => throw new AssertionError("Parameter should not happen at Obj-C top level") case p: MParam => throw new AssertionError("Parameter should not happen at Obj-C top level")
} }
base base
......
...@@ -37,25 +37,35 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) { ...@@ -37,25 +37,35 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) {
case DEnum => case DEnum =>
List(ImportRef(q(spec.objcBaseLibIncludePrefix + "DJIMarshal+Private.h"))) List(ImportRef(q(spec.objcBaseLibIncludePrefix + "DJIMarshal+Private.h")))
case DInterface => case DInterface =>
List(ImportRef(q(spec.objcppIncludePrefix + privateHeaderName(d.name)))) List(ImportRef(include(m)))
case DRecord => case DRecord =>
val r = d.body.asInstanceOf[Record] val r = d.body.asInstanceOf[Record]
val objcName = d.name + (if (r.ext.objc) "_base" else "") val objcName = d.name + (if (r.ext.objc) "_base" else "")
List(ImportRef(q(spec.objcppIncludePrefix + privateHeaderName(objcName)))) List(ImportRef(q(spec.objcppIncludePrefix + privateHeaderName(objcName))))
} }
case e: MExtern => List(ImportRef(e.objcpp.header))
case p: MParam => List() case p: MParam => List()
} }
def include(m: Meta) = m match {
case d: MDef => d.defType match {
case DEnum => q(spec.objcBaseLibIncludePrefix + "DJIMarshal+Private.h")
case _ => q(spec.objcppIncludePrefix + privateHeaderName(d.name))
}
case _ => throw new AssertionError("not applicable")
}
def helperClass(name: String) = idCpp.ty(name) def helperClass(name: String) = idCpp.ty(name)
private def helperClass(tm: MExpr): String = helperName(tm) + helperTemplates(tm) private def helperClass(tm: MExpr): String = helperName(tm) + helperTemplates(tm)
def privateHeaderName(ident: String): String = idObjc.ty(ident) + "+Private." + spec.objcHeaderExt def privateHeaderName(ident: String): String = idObjc.ty(ident) + "+Private." + spec.objcHeaderExt
private def helperName(tm: MExpr): String = tm.base match { def helperName(tm: MExpr): String = tm.base match {
case d: MDef => d.defType match { case d: MDef => d.defType match {
case DEnum => withNs(Some("djinni"), s"Enum<${cppMarshal.fqTypename(tm)}, ${objcMarshal.fqTypename(tm)}>") case DEnum => withNs(Some("djinni"), s"Enum<${cppMarshal.fqTypename(tm)}, ${objcMarshal.fqTypename(tm)}>")
case _ => withNs(Some(spec.objcppNamespace), helperClass(d.name)) case _ => withNs(Some(spec.objcppNamespace), helperClass(d.name))
} }
case e: MExtern => e.objcpp.translator
case o => withNs(Some("djinni"), o match { case o => withNs(Some("djinni"), o match {
case p: MPrimitive => p.idlName match { case p: MPrimitive => p.idlName match {
case "i8" => "I8" case "i8" => "I8"
...@@ -74,6 +84,7 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) { ...@@ -74,6 +84,7 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) {
case MSet => "Set" case MSet => "Set"
case MMap => "Map" case MMap => "Map"
case d: MDef => throw new AssertionError("unreachable") case d: MDef => throw new AssertionError("unreachable")
case e: MExtern => throw new AssertionError("unreachable")
case p: MParam => throw new AssertionError("not applicable") case p: MParam => throw new AssertionError("not applicable")
}) })
} }
......
package djinni
import djinni.ast._
import djinni.ast.Record.DerivingType.DerivingType
import djinni.generatorTools._
import djinni.meta._
import djinni.writer.IndentWriter
import java.util.{Map => JMap}
import scala.collection.JavaConversions._
import scala.collection.mutable
class YamlGenerator(spec: Spec) extends Generator(spec) {
val cppMarshal = new CppMarshal(spec)
val objcMarshal = new ObjcMarshal(spec)
val objcppMarshal = new ObjcppMarshal(spec)
val javaMarshal = new JavaMarshal(spec)
val jniMarshal = new JNIMarshal(spec)
case class QuotedString(str: String) // For anything that migt require escaping
private def writeYamlFile(name: String, origin: String, f: IndentWriter => Unit): Unit = {
createFile(spec.yamlOutFolder.get, name, out => new IndentWriter(out, " "), w => {
w.wl("# AUTOGENERATED FILE - DO NOT MODIFY!")
w.wl("# This file generated by Djinni from " + origin)
f(w)
})
}
private def writeYamlFile(tds: Seq[InternTypeDecl]): Unit = {
val origins = tds.map(_.origin).distinct.sorted.mkString(", ")
writeYamlFile(spec.yamlOutFile.get, origins, w => {
// Writing with SnakeYAML creates loads of cluttering and unnecessary tags, so write manually.
// We're not doing anything complicated anyway and it's good to have human readable output.
for(td <- tds) {
w.wl("---")
write(w, td)
}
})
}
private def writeYamlFile(ident: String, origin: String, td: InternTypeDecl): Unit =
writeYamlFile(spec.yamlPrefix + ident + ".yaml", origin, w => {
write(w, td)
})
private def write(w: IndentWriter, td: TypeDecl) {
write(w, preamble(td))
w.wl("cpp:").nested { write(w, cpp(td)) }
w.wl("objc:").nested { write(w, objc(td)) }
w.wl("objcpp:").nested { write(w, objcpp(td)) }
w.wl("java:").nested { write(w, java(td)) }
w.wl("jni:").nested { write(w, jni(td)) }
}
private def write(w: IndentWriter, m: Map[String, Any]) {
for((k, v) <- m) {
w.w(k + ": ")
v match {
case s: String => write(w, s)
case s: QuotedString => write(w, s)
case m: Map[_, _] => w.wl.nested { write(w, m.asInstanceOf[Map[String, Any]]) }
case s: Seq[_] => write(w, s)
case b: Boolean => write(w, b)
case _ => throw new AssertionError("unexpected map value")
}
}
}
private def write(w: IndentWriter, s: Seq[Any]) {
// The only arrays we have are small enough to use flow notation
w.wl(s.mkString("[", ",", "]"))
}
private def write(w: IndentWriter, b: Boolean) {
w.wl(if(b) "true" else "false")
}
private def write(w: IndentWriter, s: String) {
if(s.isEmpty) w.wl(q("")) else w.wl(s)
}
private def write(w: IndentWriter, s: QuotedString) {
if(s.str.isEmpty) w.wl(q("")) else w.wl("'" + s.str.replaceAllLiterally("'", "''") + "'")
}
private def preamble(td: TypeDecl) = Map[String, Any](
"name" -> (spec.yamlPrefix + td.ident.name),
"typedef" -> QuotedString(typeDef(td)),
"params" -> td.params.collect { case p: TypeParam => p.ident.name },
"prefix" -> spec.yamlPrefix
)
private def typeDef(td: TypeDecl) = {
def ext(e: Ext): String = (if(e.cpp) " +c" else "") + (if(e.objc) " +o" else "") + (if(e.java) " +j" else "")
def deriving(r: Record) = {
if(r.derivingTypes.isEmpty) {
""
} else {
r.derivingTypes.collect {
case Record.DerivingType.Eq => "eq"
case Record.DerivingType.Ord => "ord"
}.mkString(" deriving(", ", ", ")")
}
}
td.body match {
case i: Interface => "interface" + ext(i.ext)
case r: Record => "record" + ext(r.ext) + deriving(r)
case e: Enum => "enum"
}
}
private def cpp(td: TypeDecl) = Map[String, Any](
"typename" -> QuotedString(cppMarshal.fqTypename(td.ident, td.body)),
"header" -> QuotedString(cppMarshal.include(td.ident)),
"byValue" -> cppMarshal.byValue(td)
)
private def objc(td: TypeDecl) = Map[String, Any](
"typename" -> QuotedString(objcMarshal.fqTypename(td.ident, td.body)),
"header" -> QuotedString(objcMarshal.include(td.ident)),
"boxed" -> QuotedString(objcMarshal.boxedTypename(td)),
"pointer" -> objcMarshal.isPointer(td),
"hash" -> QuotedString("%s.hash")
)
private def objcpp(td: TypeDecl) = Map[String, Any](
"translator" -> QuotedString(objcppMarshal.helperName(mexpr(td))),
"header" -> QuotedString(objcppMarshal.include(meta(td)))
)
private def java(td: TypeDecl) = Map[String, Any](
"typename" -> QuotedString(javaMarshal.fqTypename(td.ident, td.body)),
"boxed" -> QuotedString(javaMarshal.fqTypename(td.ident, td.body)),
"reference" -> javaMarshal.isReference(td),
"generic" -> true,
"hash" -> QuotedString("%s.hashCode()")
)
private def jni(td: TypeDecl) = Map[String, Any](
"translator" -> QuotedString(jniMarshal.helperName(mexpr(td))),
"header" -> QuotedString(jniMarshal.include(td.ident)),
"typename" -> jniMarshal.fqParamType(mexpr(td)),
"typeSignature" -> QuotedString(jniMarshal.fqTypename(td.ident, td.body))
)
// 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())
private def meta(td: TypeDecl) = {
val defType = td.body match {
case i: Interface => DInterface
case r: Record => DRecord
case e: Enum => DEnum
}
MDef(td.ident, 0, defType, td.body)
}
override def generate(idl: Seq[TypeDecl]) {
val internOnly = idl.collect { case itd: InternTypeDecl => itd }.sortWith(_.ident.name < _.ident.name)
if(spec.yamlOutFile.isDefined) {
writeYamlFile(internOnly)
} else {
for(td <- internOnly) {
writeYamlFile(td.ident, td.origin, td)
}
}
}
override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) {
// unused
}
override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) {
// unused
}
override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) {
// unused
}
}
object YamlGenerator {
def metaFromYaml(td: ExternTypeDecl) = MExtern(
td.ident.name.stripPrefix(td.properties("prefix").toString), // Make sure the generator uses this type with its original name for all intents and purposes
td.params.size,
defType(td),
td.body,
MExtern.Cpp(
nested(td, "cpp")("typename").toString,
nested(td, "cpp")("header").toString,
nested(td, "cpp")("byValue").asInstanceOf[Boolean]),
MExtern.Objc(
nested(td, "objc")("typename").toString,
nested(td, "objc")("header").toString,
nested(td, "objc")("boxed").toString,
nested(td, "objc")("pointer").asInstanceOf[Boolean],
nested(td, "objc")("hash").toString),
MExtern.Objcpp(
nested(td, "objcpp")("translator").toString,
nested(td, "objcpp")("header").toString),
MExtern.Java(
nested(td, "java")("typename").toString,
nested(td, "java")("boxed").toString,
nested(td, "java")("reference").asInstanceOf[Boolean],
nested(td, "java")("generic").asInstanceOf[Boolean],
nested(td, "java")("hash").toString),
MExtern.Jni(
nested(td, "jni")("translator").toString,
nested(td, "jni")("header").toString,
nested(td, "jni")("typename").toString,
nested(td, "jni")("typeSignature").toString)
)
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]())
}
private def defType(td: ExternTypeDecl) = td.body match {
case i: Interface => DInterface
case r: Record => DRecord
case e: Enum => DEnum
}
}
...@@ -21,7 +21,13 @@ import djinni.ast.Record.DerivingType.DerivingType ...@@ -21,7 +21,13 @@ import djinni.ast.Record.DerivingType.DerivingType
import djinni.meta.MExpr import djinni.meta.MExpr
import djinni.syntax.Loc import djinni.syntax.Loc
case class IdlFile(imports: Seq[File], typeDecls: Seq[TypeDecl]) case class IdlFile(imports: Seq[FileRef], typeDecls: Seq[TypeDecl])
abstract sealed class FileRef {
val file: File
}
case class IdlFileRef(override val file: File) extends FileRef
case class ExternFileRef(override val file: File) extends FileRef
case class Ident(name: String, file: File, loc: Loc) case class Ident(name: String, file: File, loc: Loc)
class ConstRef(ident: Ident) extends Ident(ident.name, ident.file, ident.loc) class ConstRef(ident: Ident) extends Ident(ident.name, ident.file, ident.loc)
...@@ -31,7 +37,14 @@ case class TypeParam(ident: Ident) ...@@ -31,7 +37,14 @@ case class TypeParam(ident: Ident)
case class Doc(lines: Seq[String]) case class Doc(lines: Seq[String])
case class TypeDecl(ident: Ident, params: Seq[TypeParam], body: TypeDef, doc: Doc, origin: String) sealed abstract class TypeDecl {
val ident: Ident
val params: Seq[TypeParam]
val body: TypeDef
val origin: String
}
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) {
def any(): Boolean = { def any(): Boolean = {
......
...@@ -67,7 +67,10 @@ package object generatorTools { ...@@ -67,7 +67,10 @@ package object generatorTools {
objcppNamespace: String, objcppNamespace: String,
objcBaseLibIncludePrefix: String, objcBaseLibIncludePrefix: String,
outFileListWriter: Option[Writer], outFileListWriter: Option[Writer],
skipGeneration: Boolean) skipGeneration: Boolean,
yamlOutFolder: Option[File],
yamlOutFile: Option[String],
yamlPrefix: String)
def preComma(s: String) = { def preComma(s: String) = {
if (s.isEmpty) s else ", " + s if (s.isEmpty) s else ", " + s
...@@ -188,6 +191,12 @@ package object generatorTools { ...@@ -188,6 +191,12 @@ package object generatorTools {
} }
new ObjcppGenerator(spec).generate(idl) new ObjcppGenerator(spec).generate(idl)
} }
if (spec.yamlOutFolder.isDefined) {
if (!spec.skipGeneration) {
createFolder("YAML", spec.yamlOutFolder.get)
new YamlGenerator(spec).generate(idl)
}
}
None None
} }
catch { catch {
...@@ -204,7 +213,7 @@ abstract class Generator(spec: Spec) ...@@ -204,7 +213,7 @@ abstract class Generator(spec: Spec)
{ {
protected val writtenFiles = mutable.HashMap[String,String]() protected val writtenFiles = mutable.HashMap[String,String]()
protected def createFile(folder: File, fileName: String, f: IndentWriter => Unit) { protected def createFile(folder: File, fileName: String, makeWriter: OutputStreamWriter => IndentWriter, f: IndentWriter => Unit): Unit = {
if (spec.outFileListWriter.isDefined) { if (spec.outFileListWriter.isDefined) {
spec.outFileListWriter.get.write(new File(folder, fileName).getPath + "\n") spec.outFileListWriter.get.write(new File(folder, fileName).getPath + "\n")
} }
...@@ -224,17 +233,19 @@ abstract class Generator(spec: Spec) ...@@ -224,17 +233,19 @@ abstract class Generator(spec: Spec)
case _ => case _ =>
} }
val fout = new FileOutputStream(file) val fout = new FileOutputStream(file)
try { try {
val out = new OutputStreamWriter(fout, "UTF-8") val out = new OutputStreamWriter(fout, "UTF-8")
f(new IndentWriter(out)) f(makeWriter(out))
out.flush() out.flush()
} }
finally { finally {
fout.close() fout.close()
} }
} }
protected def createFile(folder: File, fileName: String, f: IndentWriter => Unit): Unit = createFile(folder, fileName, out => new IndentWriter(out), f)
implicit def identToString(ident: Ident): String = ident.name implicit def identToString(ident: Ident): String = ident.name
val idCpp = spec.cppIdentStyle val idCpp = spec.cppIdentStyle
val idJava = spec.javaIdentStyle val idJava = spec.javaIdentStyle
...@@ -298,14 +309,12 @@ abstract class Generator(spec: Spec) ...@@ -298,14 +309,12 @@ abstract class Generator(spec: Spec)
} }
def generate(idl: Seq[TypeDecl]) { def generate(idl: Seq[TypeDecl]) {
for (td <- idl) { for (td <- idl.collect { case itd: InternTypeDecl => itd }) td.body match {
td.body match { case e: Enum =>
case e: Enum => assert(td.params.isEmpty)
assert(td.params.isEmpty) generateEnum(td.origin, td.ident, td.doc, e)
generateEnum(td.origin, td.ident, td.doc, e) case r: Record => generateRecord(td.origin, td.ident, td.doc, td.params, r)
case r: Record => generateRecord(td.origin, td.ident, td.doc, td.params, r) case i: Interface => generateInterface(td.origin, td.ident, td.doc, td.params, i)
case i: Interface => generateInterface(td.origin, td.ident, td.doc, td.params, i)
}
} }
} }
......
...@@ -30,6 +30,43 @@ abstract sealed class Meta ...@@ -30,6 +30,43 @@ abstract sealed class Meta
case class MParam(name: String) extends Meta { val numParams = 0 } 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 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
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 <a/b/c>.
// All typenames are fully qualified in their respective language.
// TODO: names of enum values and record fields as written in code for use in constants (do not use IdentStyle)
case class Cpp(
typename: String,
header: String,
byValue: Boolean // Whether to pass struct by value in C++ (e.g. std::chrono::duration). Only used for "record" types.
)
case class Objc(
typename: String,
header: String,
boxed: String, // Fully qualified Objective-C typename, must be an object. Only used for "record" types.
pointer: Boolean, // True to construct pointer types and make it eligible for "nonnull" qualifier. Only used for "record" types.
hash: String // A well-formed expression to get the hash value. Must be a format string with a single "%s" placeholder. Only used for "record" types with "eq" deriving when needed.
)
case class Objcpp(
translator: String, // C++ typename containing toCpp/fromCpp methods
header: String // Where to find the translator class
)
case class Java(
typename: String,
boxed: String, // Java typename used if boxing is required, must be an object.
reference: Boolean, // True if the unboxed type is an object reference and qualifies for any kind of "nonnull" annotation in Java. Only used for "record" types.
generic: Boolean, // Set to false to exclude type arguments from the Java class. This is should be true by default. Useful if template arguments are only used in C++.
hash: String // A well-formed expression to get the hash value. Must be a format string with a single "%s" placeholder. Only used for "record" types types with "eq" deriving when needed.
)
case class Jni(
translator: String, // C++ typename containing toCpp/fromCpp methods
header: String, // Where to find the translator class
typename: String, // The JNI type to use (e.g. jobject, jstring)
typeSignature: String // The mangled Java type signature (e.g. "Ljava/lang/String;")
)
}
abstract sealed class MOpaque extends Meta { val idlName: String } abstract sealed class MOpaque extends Meta { val idlName: String }
abstract sealed class DefType abstract sealed class DefType
......
...@@ -22,6 +22,9 @@ import djinni.ast.Interface.Method ...@@ -22,6 +22,9 @@ import djinni.ast.Interface.Method
import djinni.ast.Record.DerivingType.DerivingType import djinni.ast.Record.DerivingType.DerivingType
import djinni.syntax._ import djinni.syntax._
import djinni.ast._ import djinni.ast._
import java.util.{Map => JMap}
import org.yaml.snakeyaml.Yaml
import scala.collection.JavaConversions._
import scala.collection.mutable import scala.collection.mutable
import scala.util.parsing.combinator.RegexParsers import scala.util.parsing.combinator.RegexParsers
import scala.util.parsing.input.{Position, Positional} import scala.util.parsing.input.{Position, Positional}
...@@ -36,16 +39,22 @@ private object IdlParser extends RegexParsers { ...@@ -36,16 +39,22 @@ private object IdlParser extends RegexParsers {
def idlFile(origin: String): Parser[IdlFile] = rep(importFile) ~ rep(typeDecl(origin)) ^^ { case imp~types => IdlFile(imp, types) } def idlFile(origin: String): Parser[IdlFile] = rep(importFile) ~ rep(typeDecl(origin)) ^^ { case imp~types => IdlFile(imp, types) }
def importFile: Parser[File] = "@import \"" ~> filePath <~ "\"" ^^ { def importFile: Parser[FileRef] = ("@" ~> directive) ~ ("\"" ~> filePath <~ "\"") ^^ {
x => { case "import" ~ x =>
val newPath = fileStack.top.getParent() + "/" + x val newPath = fileStack.top.getParent() + "/" + x
new File(newPath) new IdlFileRef(new File(newPath))
} case "extern" ~ x =>
val newPath = fileStack.top.getParent() + "/" + x
new ExternFileRef(new File(newPath))
} }
def filePath = "[^\"]*".r def filePath = "[^\"]*".r
def directive = importDirective | externDirective
def importDirective = "import".r
def externDirective = "extern".r
def typeDecl(origin: String): Parser[TypeDecl] = doc ~ ident ~ typeList(ident ^^ TypeParam) ~ "=" ~ typeDef ^^ { def typeDecl(origin: String): Parser[TypeDecl] = doc ~ ident ~ typeList(ident ^^ TypeParam) ~ "=" ~ typeDef ^^ {
case doc~ident~typeParams~_~body => TypeDecl(ident, typeParams, body, doc, origin) case doc~ident~typeParams~_~body => InternTypeDecl(ident, typeParams, body, doc, origin)
} }
def ext(default: Ext) = (rep1("+" ~> ident) >> checkExts) | success(default) def ext(default: Ext) = (rep1("+" ~> ident) >> checkExts) | success(default)
...@@ -78,7 +87,8 @@ private object IdlParser extends RegexParsers { ...@@ -78,7 +87,8 @@ private object IdlParser extends RegexParsers {
def typeDef: Parser[TypeDef] = record | enum | interface def typeDef: Parser[TypeDef] = record | enum | interface
def record: Parser[Record] = "record" ~> extRecord ~ bracesList(field | const) ~ opt(deriving) ^^ { def recordHeader = "record" ~> extRecord
def record: Parser[Record] = recordHeader ~ bracesList(field | const) ~ opt(deriving) ^^ {
case ext~items~deriving => { case ext~items~deriving => {
val fields = items collect {case f: Field => f} val fields = items collect {case f: Field => f}
val consts = items collect {case c: Const => c} val consts = items collect {case c: Const => c}
...@@ -97,12 +107,14 @@ private object IdlParser extends RegexParsers { ...@@ -97,12 +107,14 @@ private object IdlParser extends RegexParsers {
}).toSet }).toSet
} }
def enum: Parser[Enum] = "enum" ~> bracesList(enumOption) ^^ Enum.apply def enumHeader = "enum".r
def enum: Parser[Enum] = enumHeader ~> bracesList(enumOption) ^^ Enum.apply
def enumOption: Parser[Enum.Option] = doc ~ ident ^^ { def enumOption: Parser[Enum.Option] = doc ~ ident ^^ {
case doc~ident => Enum.Option(ident, doc) case doc~ident => Enum.Option(ident, doc)
} }
def interface: Parser[Interface] = "interface" ~> extInterface ~ bracesList(method | const) ^^ { def interfaceHeader = "interface" ~> extInterface
def interface: Parser[Interface] = interfaceHeader ~ bracesList(method | const) ^^ {
case ext~items => { case ext~items => {
val methods = items collect {case m: Method => m} val methods = items collect {case m: Method => m}
val consts = items collect {case c: Const => c} val consts = items collect {case c: Const => c}
...@@ -110,6 +122,11 @@ private object IdlParser extends RegexParsers { ...@@ -110,6 +122,11 @@ private object IdlParser extends RegexParsers {
} }
} }
def externTypeDecl: Parser[TypeDef] = externEnum | externInterface | externRecord
def externEnum: Parser[Enum] = enumHeader ^^ { case _ => Enum(List()) }
def externRecord: Parser[Record] = recordHeader ~ opt(deriving) ^^ { case ext~deriving => Record(ext, List(), List(), deriving.getOrElse(Set[DerivingType]())) }
def externInterface: Parser[Interface] = interfaceHeader ^^ { case ext => Interface(ext, List(), List()) }
def staticLabel: Parser[Boolean] = ("static ".r | "".r) ^^ { def staticLabel: Parser[Boolean] = ("static ".r | "".r) ^^ {
case "static " => true case "static " => true
case "" => false case "" => false
...@@ -206,6 +223,44 @@ def parse(origin: String, in: java.io.Reader): Either[Error,IdlFile] = { ...@@ -206,6 +223,44 @@ def parse(origin: String, in: java.io.Reader): Either[Error,IdlFile] = {
} }
} }
def parseExtern(origin: String, in: java.io.Reader): Either[Error, Seq[TypeDecl]] = {
val yaml = new Yaml();
val tds = mutable.MutableList[TypeDecl]()
for(properties <- yaml.loadAll(in).collect { case doc: JMap[_, _] => doc.collect { case (k: String, v: Any) => (k, v) } }) {
val name = properties("name").toString
val ident = Ident(name, fileStack.top, Loc(fileStack.top, 1, 1))
val params = properties.get("params").fold(Seq[TypeParam]())(_.asInstanceOf[java.util.ArrayList[String]].collect { case s: String => TypeParam(Ident(s.asInstanceOf[String], fileStack.top, Loc(fileStack.top, 1, 1))) })
IdlParser.parseAll(IdlParser.externTypeDecl, properties("typedef").toString) match {
case IdlParser.Success(ty: TypeDef, _) =>
tds += ExternTypeDecl(ident, params, ty, properties.toMap, origin)
case IdlParser.NoSuccess(msg, input) =>
return Left(Error(Loc(fileStack.top, 1, 1), "'typedef' has an unrecognized value"))
}
}
Right(tds)
}
def parseExternFile(externFile: File, inFileListWriter: Option[Writer]) : Seq[TypeDecl] = {
if (inFileListWriter.isDefined) {
inFileListWriter.get.write(externFile + "\n")
}
visitedFiles.add(externFile)
fileStack.push(externFile)
val fin = new FileInputStream(externFile)
try {
parseExtern(externFile.getName, new InputStreamReader(fin, "UTF-8")) match {
case Right(x) => x
case Left(err) => throw err.toException
}
}
finally {
fin.close()
fileStack.pop()
}
}
def parseFile(idlFile: File, inFileListWriter: Option[Writer]): Seq[TypeDecl] = { def parseFile(idlFile: File, inFileListWriter: Option[Writer]): Seq[TypeDecl] = {
if (inFileListWriter.isDefined) { if (inFileListWriter.isDefined) {
inFileListWriter.get.write(idlFile + "\n") inFileListWriter.get.write(idlFile + "\n")
...@@ -222,11 +277,16 @@ def parseFile(idlFile: File, inFileListWriter: Option[Writer]): Seq[TypeDecl] = ...@@ -222,11 +277,16 @@ def parseFile(idlFile: File, inFileListWriter: Option[Writer]): Seq[TypeDecl] =
case Right(idl) => { case Right(idl) => {
var types = idl.typeDecls var types = idl.typeDecls
idl.imports.foreach(x => { idl.imports.foreach(x => {
if (fileStack.contains(x)) { if (fileStack.contains(x.file)) {
throw new AssertionError("Circular import detected!") throw new AssertionError("Circular import detected!")
} }
if (!visitedFiles.contains(x)) { if (!visitedFiles.contains(x.file)) {
types = parseFile(x, inFileListWriter) ++ types x match {
case IdlFileRef(file) =>
types = parseFile(file, inFileListWriter) ++ types
case ExternFileRef(file) =>
types = parseExternFile(file, inFileListWriter) ++ types
}
} }
}) })
types types
......
...@@ -42,7 +42,7 @@ def resolve(metas: Scope, idl: Seq[TypeDecl]): Option[Error] = { ...@@ -42,7 +42,7 @@ def resolve(metas: Scope, idl: Seq[TypeDecl]): Option[Error] = {
for (typeDecl <- idl) { for (typeDecl <- idl) {
topLevelDupeChecker.check(typeDecl.ident) topLevelDupeChecker.check(typeDecl.ident)
val defType = typeDecl.body match { def defType = typeDecl.body match {
case e: Enum => case e: Enum =>
if (!typeDecl.params.isEmpty) { if (!typeDecl.params.isEmpty) {
throw Error(typeDecl.ident.loc, "enums can't have type parameters").toException throw Error(typeDecl.ident.loc, "enums can't have type parameters").toException
...@@ -51,8 +51,10 @@ def resolve(metas: Scope, idl: Seq[TypeDecl]): Option[Error] = { ...@@ -51,8 +51,10 @@ def resolve(metas: Scope, idl: Seq[TypeDecl]): Option[Error] = {
case r: Record => DRecord case r: Record => DRecord
case i: Interface => DInterface case i: Interface => DInterface
} }
val mdef = MDef(typeDecl.ident.name, typeDecl.params.length, defType, typeDecl.body) topScope = topScope.updated(typeDecl.ident.name, typeDecl match {
topScope = topScope.updated(typeDecl.ident.name, mdef) case td: InternTypeDecl => MDef(typeDecl.ident.name, typeDecl.params.length, defType, typeDecl.body)
case td: ExternTypeDecl => YamlGenerator.metaFromYaml(td)
})
} }
// Resolve everything // Resolve everything
...@@ -201,6 +203,7 @@ private def constTypeCheck(ty: MExpr, value: Any, resolvedConsts: Seq[Const]) { ...@@ -201,6 +203,7 @@ private def constTypeCheck(ty: MExpr, value: Any, resolvedConsts: Seq[Const]) {
throw new AssertionError(s"Const type mismatch: enum ${d.name} does not have option ${opt.name}") throw new AssertionError(s"Const type mismatch: enum ${d.name} does not have option ${opt.name}")
} }
} }
case e: MExtern => throw new AssertionError("Extern type not allowed for constant")
case _ => throw new AssertionError("Const type cannot be resolved") case _ => throw new AssertionError("Const type cannot be resolved")
} }
} }
...@@ -241,6 +244,15 @@ private def resolveRecord(scope: Scope, r: Record) { ...@@ -241,6 +244,15 @@ private def resolveRecord(scope: Scope, r: Record) {
throw new Error(f.ident.loc, s"Some deriving required is not implemented in record ${f.ident.name}").toException throw new Error(f.ident.loc, s"Some deriving required is not implemented in record ${f.ident.name}").toException
case DEnum => case DEnum =>
} }
case e: MExtern => e.defType match {
case DInterface =>
throw new Error(f.ident.loc, "Interface reference cannot live in a record").toException
case DRecord =>
val record = e.body.asInstanceOf[Record]
if (!r.derivingTypes.subsetOf(record.derivingTypes))
throw new Error(f.ident.loc, s"Some deriving required is not implemented in record ${f.ident.name}").toException
case DEnum =>
}
case _ => throw new AssertionError("Type cannot be resolved") case _ => throw new AssertionError("Type cannot be resolved")
} }
} }
......
...@@ -11,3 +11,4 @@ ...@@ -11,3 +11,4 @@
@import "primtypes.djinni" @import "primtypes.djinni"
@import "constants.djinni" @import "constants.djinni"
@import "date.djinni" @import "date.djinni"
@import "duration.djinni"
@extern "date.yaml"
date_record = record { date_record = record {
created_at: date; created_at: extern_date;
} } deriving(eq, ord)
map_date_record = record { map_date_record = record {
dates_by_id: map<string, date>; dates_by_id: map<string, extern_date>;
} }
# This is an example YAML file mimicking the builtin "date" type as external type
---
name: extern_date
typedef: 'record deriving(eq, ord)'
params: []
prefix: ''
cpp:
typename: 'std::chrono::system_clock::time_point'
header: '<chrono>'
byValue: true
objc:
typename: 'NSDate'
header: '<Foundation/Foundation.h>'
boxed: 'NSDate'
pointer: true
hash: '(NSUInteger)%s.timeIntervalSinceReferenceDate'
objcpp:
translator: '::djinni::Date'
header: '"DJIMarshal+Private.h"'
java:
typename: 'java.util.Date'
boxed: 'java.util.Date'
reference: true
generic: true
hash: '%s.hashCode()'
jni:
translator: '::djinni::Date'
header: '"Marshal.hpp"'
typename: jobject
typeSignature: 'Ljava/util/Date;'
@extern "duration.yaml"
test_duration = interface +c {
static hoursString(dt: duration<i32, h>): string;
static minutesString(dt: duration<i32, min>): string;
static secondsString(dt: duration<i32, s>): string;
static millisString(dt: duration<i32, ms>): string;
static microsString(dt: duration<i32, us>): string;
static nanosString(dt: duration<i32, ns>): string;
static hours(count: i32): duration<i32, h>;
static minutes(count: i32): duration<i32, min>;
static seconds(count: i32): duration<i32, s>;
static millis(count: i32): duration<i32, ms>;
static micros(count: i32): duration<i32, us>;
static nanos(count: i32): duration<i32, ns>;
static hoursf(count: f64): duration<f64, h>;
static minutesf(count: f64): duration<f64, min>;
static secondsf(count: f64): duration<f64, s>;
static millisf(count: f64): duration<f64, ms>;
static microsf(count: f64): duration<f64, us>;
static nanosf(count: f64): duration<f64, ns>;
static box(count: i64): optional<duration<i64, s>>;
static unbox(dt: optional<duration<i64, s>>): i64;
}
record_with_duration_and_derivings = record {
dt: duration<f64, ns>;
} deriving(eq, ord)
# This is an example YAML file providing an interface to a chrono::duration based type with customizable representation and period mapped to NSTimeInterval and java.time.Duration.
# Example usage:
# duration<i32, s> maps to std::chrono::duration<int32_t, std::ratio<1>>
# duration<f64, min> maps to std::chrono::duration<double, std::ratio<60>>
---
name: duration
typedef: 'record deriving(eq, ord)'
params: [rep, period]
prefix: ''
cpp:
typename: 'std::chrono::duration'
header: '<chrono>'
byValue: true
objc:
typename: 'NSTimeInterval'
header: '<Foundation/Foundation.h>'
boxed: 'NSNumber'
pointer: false
hash: '(NSUInteger)%s'
objcpp:
translator: '::djinni::Duration'
header: '"Duration-objc.hpp"'
java:
typename: 'java.time.Duration'
boxed: 'java.time.Duration'
reference: true
generic: false
hash: '%s.hashCode()'
jni:
translator: '::djinni::Duration'
header: '"Duration-jni.hpp"'
typename: jobject
typeSignature: 'Ljava/time/Duration;'
---
name: h
typedef: 'record'
params: []
prefix: ''
cpp:
typename: 'std::ratio<3600>'
header: '<chrono>'
byValue: true
objc:
typename: 'NSTimeInterval'
header: '<Foundation/Foundation.h>'
boxed: 'NSNumber'
pointer: false
hash: ''
objcpp:
translator: '::djinni::Duration_h'
header: '"Duration-objc.hpp"'
java:
typename: 'java.time.Duration'
boxed: 'java.time.Duration'
reference: true
generic: false
hash: ''
jni:
translator: '::djinni::Duration_h'
header: '"Duration-jni.hpp"'
typename: jobject
typeSignature: 'Ljava/time/Duration;'
---
name: min
typedef: 'record'
params: []
prefix: ''
cpp:
typename: 'std::ratio<60>'
header: '<chrono>'
byValue: true
objc:
typename: 'NSTimeInterval'
header: '<Foundation/Foundation.h>'
boxed: 'NSNumber'
pointer: false
hash: ''
objcpp:
translator: '::djinni::Duration_min'
header: '"Duration-objc.hpp"'
java:
typename: 'java.time.Duration'
boxed: 'java.time.Duration'
reference: true
generic: false
hash: ''
jni:
translator: '::djinni::Duration_min'
header: '"Duration-jni.hpp"'
typename: jobject
typeSignature: 'Ljava/time/Duration;'
---
name: s
typedef: 'record'
params: []
prefix: ''
cpp:
typename: 'std::ratio<1>'
header: '<chrono>'
byValue: true
objc:
typename: 'NSTimeInterval'
header: '<Foundation/Foundation.h>'
boxed: 'NSNumber'
pointer: false
hash: ''
objcpp:
translator: '::djinni::Duration_s'
header: '"Duration-objc.hpp"'
java:
typename: 'java.time.Duration'
boxed: 'java.time.Duration'
reference: true
generic: false
hash: ''
jni:
translator: '::djinni::Duration_s'
header: '"Duration-jni.hpp"'
typename: jobject
typeSignature: 'Ljava/time/Duration;'
---
name: ms
typedef: 'record'
params: []
prefix: ''
cpp:
typename: 'std::milli'
header: '<chrono>'
byValue: true
objc:
typename: 'NSTimeInterval'
header: '<Foundation/Foundation.h>'
boxed: 'NSNumber'
pointer: false
hash: ''
objcpp:
translator: '::djinni::Duration_ms'
header: '"Duration-objc.hpp"'
java:
typename: 'java.time.Duration'
boxed: 'java.time.Duration'
reference: true
generic: false
hash: ''
jni:
translator: '::djinni::Duration_ms'
header: '"Duration-jni.hpp"'
typename: jobject
typeSignature: 'Ljava/time/Duration;'
---
name: us
typedef: 'record'
params: []
prefix: ''
cpp:
typename: 'std::micro'
header: '<chrono>'
byValue: true
objc:
typename: 'NSTimeInterval'
header: '<Foundation/Foundation.h>'
boxed: 'NSNumber'
pointer: false
hash: ''
objcpp:
translator: '::djinni::Duration_us'
header: '"Duration-objc.hpp"'
java:
typename: 'java.time.Duration'
boxed: 'java.time.Duration'
reference: true
generic: false
hash: ''
jni:
translator: '::djinni::Duration_us'
header: '"Duration-jni.hpp"'
typename: jobject
typeSignature: 'Ljava/time/Duration;'
---
name: ns
typedef: 'record'
params: []
prefix: ''
cpp:
typename: 'std::nano'
header: '<chrono>'
byValue: true
objc:
typename: 'NSTimeInterval'
header: '<Foundation/Foundation.h>'
boxed: 'NSNumber'
pointer: false
hash: ''
objcpp:
translator: '::djinni::Duration_ns'
header: '"Duration-objc.hpp"'
java:
typename: 'java.time.Duration'
boxed: 'java.time.Duration'
reference: true
generic: false
hash: ''
jni:
translator: '::djinni::Duration_ns'
header: '"Duration-jni.hpp"'
typename: jobject
typeSignature: 'Ljava/time/Duration;'
@extern "yaml-test.yaml"
# This file tests YAML dumped by Djinni can be parsed back in
extern_record_with_derivings = record
{
member: test_record_with_derivings;
e: test_color;
} deriving(eq, ord)
extern_interface_1 = interface +c
{
foo(i: test_client_interface): test_client_returned_record;
}
extern_interface_2 = interface +j +o
{
foo(i: test_test_helpers): extern_record_with_derivings;
}
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from date.djinni
#include "date_record.hpp" // my header
bool operator==(const DateRecord& lhs, const DateRecord& rhs) {
return lhs.created_at == rhs.created_at;
}
bool operator!=(const DateRecord& lhs, const DateRecord& rhs) {
return !(lhs == rhs);
}
bool operator<(const DateRecord& lhs, const DateRecord& rhs) {
if (lhs.created_at < rhs.created_at) {
return true;
}
if (rhs.created_at < lhs.created_at) {
return false;
}
return false;
}
bool operator>(const DateRecord& lhs, const DateRecord& rhs) {
return rhs < lhs;
}
bool operator<=(const DateRecord& lhs, const DateRecord& rhs) {
return !(rhs < lhs);
}
bool operator>=(const DateRecord& lhs, const DateRecord& rhs) {
return !(lhs < rhs);
}
...@@ -9,6 +9,15 @@ ...@@ -9,6 +9,15 @@
struct DateRecord final { struct DateRecord final {
std::chrono::system_clock::time_point created_at; std::chrono::system_clock::time_point created_at;
friend bool operator==(const DateRecord& lhs, const DateRecord& rhs);
friend bool operator!=(const DateRecord& lhs, const DateRecord& rhs);
friend bool operator<(const DateRecord& lhs, const DateRecord& rhs);
friend bool operator>(const DateRecord& lhs, const DateRecord& rhs);
friend bool operator<=(const DateRecord& lhs, const DateRecord& rhs);
friend bool operator>=(const DateRecord& lhs, const DateRecord& rhs);
DateRecord(std::chrono::system_clock::time_point created_at) DateRecord(std::chrono::system_clock::time_point created_at)
: created_at(std::move(created_at)) : created_at(std::move(created_at))
{} {}
......
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#pragma once
#include "client_interface.hpp"
#include "client_returned_record.hpp"
#include <memory>
class ExternInterface1 {
public:
virtual ~ExternInterface1() {}
virtual ::ClientReturnedRecord foo(const std::shared_ptr<::ClientInterface> & i) = 0;
};
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#pragma once
#include "extern_record_with_derivings.hpp"
#include "test_helpers.hpp"
#include <memory>
class ExternInterface2 {
public:
virtual ~ExternInterface2() {}
virtual ExternRecordWithDerivings foo(const std::shared_ptr<::TestHelpers> & i) = 0;
};
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#include "extern_record_with_derivings.hpp" // my header
bool operator==(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs) {
return lhs.member == rhs.member &&
lhs.e == rhs.e;
}
bool operator!=(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs) {
return !(lhs == rhs);
}
bool operator<(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs) {
if (lhs.member < rhs.member) {
return true;
}
if (rhs.member < lhs.member) {
return false;
}
if (lhs.e < rhs.e) {
return true;
}
if (rhs.e < lhs.e) {
return false;
}
return false;
}
bool operator>(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs) {
return rhs < lhs;
}
bool operator<=(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs) {
return !(rhs < lhs);
}
bool operator>=(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs) {
return !(lhs < rhs);
}
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#pragma once
#include "color.hpp"
#include "record_with_derivings.hpp"
#include <utility>
/** This file tests YAML dumped by Djinni can be parsed back in */
struct ExternRecordWithDerivings final {
::RecordWithDerivings member;
::color e;
friend bool operator==(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs);
friend bool operator!=(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs);
friend bool operator<(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs);
friend bool operator>(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs);
friend bool operator<=(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs);
friend bool operator>=(const ExternRecordWithDerivings& lhs, const ExternRecordWithDerivings& rhs);
ExternRecordWithDerivings(::RecordWithDerivings member,
::color e)
: member(std::move(member))
, e(std::move(e))
{}
};
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#include "record_with_duration_and_derivings.hpp" // my header
bool operator==(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs) {
return lhs.dt == rhs.dt;
}
bool operator!=(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs) {
return !(lhs == rhs);
}
bool operator<(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs) {
if (lhs.dt < rhs.dt) {
return true;
}
if (rhs.dt < lhs.dt) {
return false;
}
return false;
}
bool operator>(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs) {
return rhs < lhs;
}
bool operator<=(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs) {
return !(rhs < lhs);
}
bool operator>=(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs) {
return !(lhs < rhs);
}
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#pragma once
#include <chrono>
#include <utility>
struct RecordWithDurationAndDerivings final {
std::chrono::duration<double, std::nano> dt;
friend bool operator==(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs);
friend bool operator!=(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs);
friend bool operator<(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs);
friend bool operator>(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs);
friend bool operator<=(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs);
friend bool operator>=(const RecordWithDurationAndDerivings& lhs, const RecordWithDurationAndDerivings& rhs);
RecordWithDurationAndDerivings(std::chrono::duration<double, std::nano> dt)
: dt(std::move(dt))
{}
};
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#pragma once
#include <chrono>
#include <cstdint>
#include <experimental/optional>
#include <string>
class TestDuration {
public:
virtual ~TestDuration() {}
static std::string hoursString(std::chrono::duration<int32_t, std::ratio<3600>> dt);
static std::string minutesString(std::chrono::duration<int32_t, std::ratio<60>> dt);
static std::string secondsString(std::chrono::duration<int32_t, std::ratio<1>> dt);
static std::string millisString(std::chrono::duration<int32_t, std::milli> dt);
static std::string microsString(std::chrono::duration<int32_t, std::micro> dt);
static std::string nanosString(std::chrono::duration<int32_t, std::nano> dt);
static std::chrono::duration<int32_t, std::ratio<3600>> hours(int32_t count);
static std::chrono::duration<int32_t, std::ratio<60>> minutes(int32_t count);
static std::chrono::duration<int32_t, std::ratio<1>> seconds(int32_t count);
static std::chrono::duration<int32_t, std::milli> millis(int32_t count);
static std::chrono::duration<int32_t, std::micro> micros(int32_t count);
static std::chrono::duration<int32_t, std::nano> nanos(int32_t count);
static std::chrono::duration<double, std::ratio<3600>> hoursf(double count);
static std::chrono::duration<double, std::ratio<60>> minutesf(double count);
static std::chrono::duration<double, std::ratio<1>> secondsf(double count);
static std::chrono::duration<double, std::milli> millisf(double count);
static std::chrono::duration<double, std::micro> microsf(double count);
static std::chrono::duration<double, std::nano> nanosf(double count);
static std::experimental::optional<std::chrono::duration<int64_t, std::ratio<1>>> box(int64_t count);
static int64_t unbox(std::experimental::optional<std::chrono::duration<int64_t, std::ratio<1>>> dt);
};
...@@ -12,3 +12,6 @@ djinni/test.djinni ...@@ -12,3 +12,6 @@ djinni/test.djinni
djinni/primtypes.djinni djinni/primtypes.djinni
djinni/constants.djinni djinni/constants.djinni
djinni/date.djinni djinni/date.djinni
djinni/date.yaml
djinni/duration.djinni
djinni/duration.yaml
...@@ -3,22 +3,48 @@ ...@@ -3,22 +3,48 @@
package com.dropbox.djinni.test; package com.dropbox.djinni.test;
import java.util.Date;
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public final class DateRecord { public final class DateRecord implements Comparable<DateRecord> {
/*package*/ final Date mCreatedAt; /*package*/ final java.util.Date mCreatedAt;
public DateRecord( public DateRecord(
@Nonnull Date createdAt) { @Nonnull java.util.Date createdAt) {
this.mCreatedAt = createdAt; this.mCreatedAt = createdAt;
} }
@Nonnull @Nonnull
public Date getCreatedAt() { public java.util.Date getCreatedAt() {
return mCreatedAt; return mCreatedAt;
} }
@Override
public boolean equals(@CheckForNull Object obj) {
if (!(obj instanceof DateRecord)) {
return false;
}
DateRecord other = (DateRecord) obj;
return this.mCreatedAt.equals(other.mCreatedAt);
}
@Override
public int hashCode() {
// Pick an arbitrary non-zero starting value
int hashCode = 17;
hashCode = hashCode * 31 + (mCreatedAt.hashCode());
return hashCode;
}
@Override
public int compareTo(@Nonnull DateRecord other) {
int tempResult;
tempResult = this.mCreatedAt.compareTo(other.mCreatedAt);
if (tempResult != 0) {
return tempResult;
}
return 0;
}
} }
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
package com.dropbox.djinni.test;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class ExternInterface1 {
public abstract com.dropbox.djinni.test.ClientReturnedRecord foo(com.dropbox.djinni.test.ClientInterface i);
public static final class CppProxy extends ExternInterface1
{
private final long nativeRef;
private final AtomicBoolean destroyed = new AtomicBoolean(false);
private CppProxy(long nativeRef)
{
if (nativeRef == 0) throw new RuntimeException("nativeRef is zero");
this.nativeRef = nativeRef;
}
private native void nativeDestroy(long nativeRef);
public void destroy()
{
boolean destroyed = this.destroyed.getAndSet(true);
if (!destroyed) nativeDestroy(this.nativeRef);
}
protected void finalize() throws java.lang.Throwable
{
destroy();
super.finalize();
}
@Override
public com.dropbox.djinni.test.ClientReturnedRecord foo(com.dropbox.djinni.test.ClientInterface i)
{
assert !this.destroyed.get() : "trying to use a destroyed object";
return native_foo(this.nativeRef, i);
}
private native com.dropbox.djinni.test.ClientReturnedRecord native_foo(long _nativeRef, com.dropbox.djinni.test.ClientInterface i);
}
}
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
package com.dropbox.djinni.test;
public abstract class ExternInterface2 {
public abstract ExternRecordWithDerivings foo(com.dropbox.djinni.test.TestHelpers i);
}
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
package com.dropbox.djinni.test;
/** This file tests YAML dumped by Djinni can be parsed back in */
public final class ExternRecordWithDerivings implements Comparable<ExternRecordWithDerivings> {
/*package*/ final com.dropbox.djinni.test.RecordWithDerivings mMember;
/*package*/ final com.dropbox.djinni.test.Color mE;
public ExternRecordWithDerivings(
com.dropbox.djinni.test.RecordWithDerivings member,
com.dropbox.djinni.test.Color e) {
this.mMember = member;
this.mE = e;
}
public com.dropbox.djinni.test.RecordWithDerivings getMember() {
return mMember;
}
public com.dropbox.djinni.test.Color getE() {
return mE;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ExternRecordWithDerivings)) {
return false;
}
ExternRecordWithDerivings other = (ExternRecordWithDerivings) obj;
return this.mMember.equals(other.mMember) &&
this.mE == other.mE;
}
@Override
public int hashCode() {
// Pick an arbitrary non-zero starting value
int hashCode = 17;
hashCode = hashCode * 31 + (mMember.hashCode());
hashCode = hashCode * 31 + mE.hashCode();
return hashCode;
}
@Override
public int compareTo(ExternRecordWithDerivings other) {
int tempResult;
tempResult = this.mMember.compareTo(other.mMember);
if (tempResult != 0) {
return tempResult;
}
tempResult = this.mE.compareTo(other.mE);if (tempResult != 0) {
return tempResult;
}
return 0;
}
}
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
package com.dropbox.djinni.test; package com.dropbox.djinni.test;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
...@@ -11,15 +10,15 @@ import javax.annotation.Nonnull; ...@@ -11,15 +10,15 @@ import javax.annotation.Nonnull;
public final class MapDateRecord { public final class MapDateRecord {
/*package*/ final HashMap<String, Date> mDatesById; /*package*/ final HashMap<String, java.util.Date> mDatesById;
public MapDateRecord( public MapDateRecord(
@Nonnull HashMap<String, Date> datesById) { @Nonnull HashMap<String, java.util.Date> datesById) {
this.mDatesById = datesById; this.mDatesById = datesById;
} }
@Nonnull @Nonnull
public HashMap<String, Date> getDatesById() { public HashMap<String, java.util.Date> getDatesById() {
return mDatesById; return mDatesById;
} }
} }
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
package com.dropbox.djinni.test;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
public final class RecordWithDurationAndDerivings implements Comparable<RecordWithDurationAndDerivings> {
/*package*/ final java.time.Duration mDt;
public RecordWithDurationAndDerivings(
@Nonnull java.time.Duration dt) {
this.mDt = dt;
}
@Nonnull
public java.time.Duration getDt() {
return mDt;
}
@Override
public boolean equals(@CheckForNull Object obj) {
if (!(obj instanceof RecordWithDurationAndDerivings)) {
return false;
}
RecordWithDurationAndDerivings other = (RecordWithDurationAndDerivings) obj;
return this.mDt.equals(other.mDt);
}
@Override
public int hashCode() {
// Pick an arbitrary non-zero starting value
int hashCode = 17;
hashCode = hashCode * 31 + (mDt.hashCode());
return hashCode;
}
@Override
public int compareTo(@Nonnull RecordWithDurationAndDerivings other) {
int tempResult;
tempResult = this.mDt.compareTo(other.mDt);
if (tempResult != 0) {
return tempResult;
}
return 0;
}
}
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
package com.dropbox.djinni.test;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
public abstract class TestDuration {
@Nonnull
public static native String hoursString(@Nonnull java.time.Duration dt);
@Nonnull
public static native String minutesString(@Nonnull java.time.Duration dt);
@Nonnull
public static native String secondsString(@Nonnull java.time.Duration dt);
@Nonnull
public static native String millisString(@Nonnull java.time.Duration dt);
@Nonnull
public static native String microsString(@Nonnull java.time.Duration dt);
@Nonnull
public static native String nanosString(@Nonnull java.time.Duration dt);
@Nonnull
public static native java.time.Duration hours(int count);
@Nonnull
public static native java.time.Duration minutes(int count);
@Nonnull
public static native java.time.Duration seconds(int count);
@Nonnull
public static native java.time.Duration millis(int count);
@Nonnull
public static native java.time.Duration micros(int count);
@Nonnull
public static native java.time.Duration nanos(int count);
@Nonnull
public static native java.time.Duration hoursf(double count);
@Nonnull
public static native java.time.Duration minutesf(double count);
@Nonnull
public static native java.time.Duration secondsf(double count);
@Nonnull
public static native java.time.Duration millisf(double count);
@Nonnull
public static native java.time.Duration microsf(double count);
@Nonnull
public static native java.time.Duration nanosf(double count);
@CheckForNull
public static native java.time.Duration box(long count);
public static native long unbox(@CheckForNull java.time.Duration dt);
public static final class CppProxy extends TestDuration
{
private final long nativeRef;
private final AtomicBoolean destroyed = new AtomicBoolean(false);
private CppProxy(long nativeRef)
{
if (nativeRef == 0) throw new RuntimeException("nativeRef is zero");
this.nativeRef = nativeRef;
}
private native void nativeDestroy(long nativeRef);
public void destroy()
{
boolean destroyed = this.destroyed.getAndSet(true);
if (!destroyed) nativeDestroy(this.nativeRef);
}
protected void finalize() throws java.lang.Throwable
{
destroy();
super.finalize();
}
}
}
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#include "NativeExternInterface1.hpp" // my header
#include "NativeClientInterface.hpp"
#include "NativeClientReturnedRecord.hpp"
namespace djinni_generated {
NativeExternInterface1::NativeExternInterface1() : ::djinni::JniInterface<::ExternInterface1, NativeExternInterface1>("com/dropbox/djinni/test/ExternInterface1$CppProxy") {}
NativeExternInterface1::~NativeExternInterface1() = default;
CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_ExternInterface1_00024CppProxy_nativeDestroy(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
{
try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
delete reinterpret_cast<djinni::CppProxyHandle<::ExternInterface1>*>(nativeRef);
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
}
CJNIEXPORT ::djinni_generated::NativeClientReturnedRecord::JniType JNICALL Java_com_dropbox_djinni_test_ExternInterface1_00024CppProxy_native_1foo(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, ::djinni_generated::NativeClientInterface::JniType j_i)
{
try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
const auto& ref = ::djinni::CppProxyHandle<::ExternInterface1>::get(nativeRef);
auto r = ref->foo(::djinni_generated::NativeClientInterface::toCpp(jniEnv, j_i));
return ::djinni::release(::djinni_generated::NativeClientReturnedRecord::fromCpp(jniEnv, r));
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)
}
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#pragma once
#include "djinni_support.hpp"
#include "extern_interface_1.hpp"
namespace djinni_generated {
class NativeExternInterface1 final : ::djinni::JniInterface<::ExternInterface1, NativeExternInterface1> {
public:
using CppType = std::shared_ptr<::ExternInterface1>;
using JniType = jobject;
using Boxed = NativeExternInterface1;
~NativeExternInterface1();
static CppType toCpp(JNIEnv* jniEnv, JniType j) { return ::djinni::JniClass<NativeExternInterface1>::get()._fromJava(jniEnv, j); }
static ::djinni::LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) { return {jniEnv, ::djinni::JniClass<NativeExternInterface1>::get()._toJava(jniEnv, c)}; }
private:
NativeExternInterface1();
friend ::djinni::JniClass<NativeExternInterface1>;
friend ::djinni::JniInterface<::ExternInterface1, NativeExternInterface1>;
};
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#include "NativeExternInterface2.hpp" // my header
#include "NativeExternRecordWithDerivings.hpp"
#include "NativeTestHelpers.hpp"
namespace djinni_generated {
NativeExternInterface2::NativeExternInterface2() : ::djinni::JniInterface<::ExternInterface2, NativeExternInterface2>() {}
NativeExternInterface2::~NativeExternInterface2() = default;
NativeExternInterface2::JavaProxy::JavaProxy(JniType j) : JavaProxyCacheEntry(j) { }
NativeExternInterface2::JavaProxy::~JavaProxy() = default;
::ExternRecordWithDerivings NativeExternInterface2::JavaProxy::foo(const std::shared_ptr<::TestHelpers> & c_i) {
auto jniEnv = ::djinni::jniGetThreadEnv();
::djinni::JniLocalScope jscope(jniEnv, 10);
const auto& data = ::djinni::JniClass<::djinni_generated::NativeExternInterface2>::get();
auto jret = jniEnv->CallObjectMethod(getGlobalRef(), data.method_foo,
::djinni::get(::djinni_generated::NativeTestHelpers::fromCpp(jniEnv, c_i)));
::djinni::jniExceptionCheck(jniEnv);
return ::djinni_generated::NativeExternRecordWithDerivings::toCpp(jniEnv, jret);
}
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#pragma once
#include "djinni_support.hpp"
#include "extern_interface_2.hpp"
namespace djinni_generated {
class NativeExternInterface2 final : ::djinni::JniInterface<::ExternInterface2, NativeExternInterface2> {
public:
using CppType = std::shared_ptr<::ExternInterface2>;
using JniType = jobject;
using Boxed = NativeExternInterface2;
~NativeExternInterface2();
static CppType toCpp(JNIEnv* jniEnv, JniType j) { return ::djinni::JniClass<NativeExternInterface2>::get()._fromJava(jniEnv, j); }
static ::djinni::LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) { return {jniEnv, ::djinni::JniClass<NativeExternInterface2>::get()._toJava(jniEnv, c)}; }
private:
NativeExternInterface2();
friend ::djinni::JniClass<NativeExternInterface2>;
friend ::djinni::JniInterface<::ExternInterface2, NativeExternInterface2>;
class JavaProxy final : ::djinni::JavaProxyCacheEntry, public ::ExternInterface2
{
public:
JavaProxy(JniType j);
~JavaProxy();
::ExternRecordWithDerivings foo(const std::shared_ptr<::TestHelpers> & i) override;
private:
using ::djinni::JavaProxyCacheEntry::getGlobalRef;
friend ::djinni::JniInterface<::ExternInterface2, ::djinni_generated::NativeExternInterface2>;
friend ::djinni::JavaProxyCache<JavaProxy>;
};
const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/ExternInterface2") };
const jmethodID method_foo { ::djinni::jniGetMethodID(clazz.get(), "foo", "(Lcom/dropbox/djinni/test/TestHelpers;)Lcom/dropbox/djinni/test/ExternRecordWithDerivings;") };
};
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#include "NativeExternRecordWithDerivings.hpp" // my header
#include "NativeColor.hpp"
#include "NativeRecordWithDerivings.hpp"
namespace djinni_generated {
NativeExternRecordWithDerivings::NativeExternRecordWithDerivings() = default;
NativeExternRecordWithDerivings::~NativeExternRecordWithDerivings() = default;
auto NativeExternRecordWithDerivings::fromCpp(JNIEnv* jniEnv, const CppType& c) -> ::djinni::LocalRef<JniType> {
const auto& data = ::djinni::JniClass<NativeExternRecordWithDerivings>::get();
auto r = ::djinni::LocalRef<JniType>{jniEnv->NewObject(data.clazz.get(), data.jconstructor,
::djinni::get(::djinni_generated::NativeRecordWithDerivings::fromCpp(jniEnv, c.member)),
::djinni::get(::djinni_generated::NativeColor::fromCpp(jniEnv, c.e)))};
::djinni::jniExceptionCheck(jniEnv);
return r;
}
auto NativeExternRecordWithDerivings::toCpp(JNIEnv* jniEnv, JniType j) -> CppType {
::djinni::JniLocalScope jscope(jniEnv, 3);
assert(j != nullptr);
const auto& data = ::djinni::JniClass<NativeExternRecordWithDerivings>::get();
return {::djinni_generated::NativeRecordWithDerivings::toCpp(jniEnv, jniEnv->GetObjectField(j, data.field_mMember)),
::djinni_generated::NativeColor::toCpp(jniEnv, jniEnv->GetObjectField(j, data.field_mE))};
}
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#pragma once
#include "djinni_support.hpp"
#include "extern_record_with_derivings.hpp"
namespace djinni_generated {
class NativeExternRecordWithDerivings final {
public:
using CppType = ::ExternRecordWithDerivings;
using JniType = jobject;
using Boxed = NativeExternRecordWithDerivings;
~NativeExternRecordWithDerivings();
static CppType toCpp(JNIEnv* jniEnv, JniType j);
static ::djinni::LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c);
private:
NativeExternRecordWithDerivings();
friend ::djinni::JniClass<NativeExternRecordWithDerivings>;
const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/ExternRecordWithDerivings") };
const jmethodID jconstructor { ::djinni::jniGetMethodID(clazz.get(), "<init>", "(Lcom/dropbox/djinni/test/RecordWithDerivings;Lcom/dropbox/djinni/test/Color;)V") };
const jfieldID field_mMember { ::djinni::jniGetFieldID(clazz.get(), "mMember", "Lcom/dropbox/djinni/test/RecordWithDerivings;") };
const jfieldID field_mE { ::djinni::jniGetFieldID(clazz.get(), "mE", "Lcom/dropbox/djinni/test/Color;") };
};
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#include "NativeRecordWithDurationAndDerivings.hpp" // my header
#include "Duration-jni.hpp"
#include "Marshal.hpp"
namespace djinni_generated {
NativeRecordWithDurationAndDerivings::NativeRecordWithDurationAndDerivings() = default;
NativeRecordWithDurationAndDerivings::~NativeRecordWithDurationAndDerivings() = default;
auto NativeRecordWithDurationAndDerivings::fromCpp(JNIEnv* jniEnv, const CppType& c) -> ::djinni::LocalRef<JniType> {
const auto& data = ::djinni::JniClass<NativeRecordWithDurationAndDerivings>::get();
auto r = ::djinni::LocalRef<JniType>{jniEnv->NewObject(data.clazz.get(), data.jconstructor,
::djinni::get(::djinni::Duration<::djinni::F64, ::djinni::Duration_ns>::fromCpp(jniEnv, c.dt)))};
::djinni::jniExceptionCheck(jniEnv);
return r;
}
auto NativeRecordWithDurationAndDerivings::toCpp(JNIEnv* jniEnv, JniType j) -> CppType {
::djinni::JniLocalScope jscope(jniEnv, 2);
assert(j != nullptr);
const auto& data = ::djinni::JniClass<NativeRecordWithDurationAndDerivings>::get();
return {::djinni::Duration<::djinni::F64, ::djinni::Duration_ns>::toCpp(jniEnv, jniEnv->GetObjectField(j, data.field_mDt))};
}
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#pragma once
#include "djinni_support.hpp"
#include "record_with_duration_and_derivings.hpp"
namespace djinni_generated {
class NativeRecordWithDurationAndDerivings final {
public:
using CppType = ::RecordWithDurationAndDerivings;
using JniType = jobject;
using Boxed = NativeRecordWithDurationAndDerivings;
~NativeRecordWithDurationAndDerivings();
static CppType toCpp(JNIEnv* jniEnv, JniType j);
static ::djinni::LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c);
private:
NativeRecordWithDurationAndDerivings();
friend ::djinni::JniClass<NativeRecordWithDurationAndDerivings>;
const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/RecordWithDurationAndDerivings") };
const jmethodID jconstructor { ::djinni::jniGetMethodID(clazz.get(), "<init>", "(Ljava/time/Duration;)V") };
const jfieldID field_mDt { ::djinni::jniGetFieldID(clazz.get(), "mDt", "Ljava/time/Duration;") };
};
} // namespace djinni_generated
This diff is collapsed.
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#pragma once
#include "djinni_support.hpp"
#include "test_duration.hpp"
namespace djinni_generated {
class NativeTestDuration final : ::djinni::JniInterface<::TestDuration, NativeTestDuration> {
public:
using CppType = std::shared_ptr<::TestDuration>;
using JniType = jobject;
using Boxed = NativeTestDuration;
~NativeTestDuration();
static CppType toCpp(JNIEnv* jniEnv, JniType j) { return ::djinni::JniClass<NativeTestDuration>::get()._fromJava(jniEnv, j); }
static ::djinni::LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) { return {jniEnv, ::djinni::JniClass<NativeTestDuration>::get()._toJava(jniEnv, c)}; }
private:
NativeTestDuration();
friend ::djinni::JniClass<NativeTestDuration>;
friend ::djinni::JniInterface<::TestDuration, NativeTestDuration>;
};
} // namespace djinni_generated
...@@ -8,4 +8,6 @@ ...@@ -8,4 +8,6 @@
@property (nonatomic, readonly, nonnull) NSDate * createdAt; @property (nonatomic, readonly, nonnull) NSDate * createdAt;
- (NSComparisonResult)compare:(nonnull DBDateRecord *)other;
@end @end
...@@ -14,4 +14,29 @@ ...@@ -14,4 +14,29 @@
return self; return self;
} }
- (BOOL)isEqual:(id)other
{
if (![other isKindOfClass:[DBDateRecord class]]) {
return NO;
}
DBDateRecord *typedOther = (DBDateRecord *)other;
return [self.createdAt isEqual:typedOther.createdAt];
}
- (NSUInteger)hash
{
return NSStringFromClass([self class]).hash ^
((NSUInteger)self.createdAt.timeIntervalSinceReferenceDate);
}
- (NSComparisonResult)compare:(DBDateRecord *)other
{
NSComparisonResult tempResult;
tempResult = [self.createdAt compare:other.createdAt];
if (tempResult != NSOrderedSame) {
return tempResult;
}
return NSOrderedSame;
}
@end @end
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#include "extern_interface_1.hpp"
#include <memory>
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
@class DBExternInterface1;
namespace djinni_generated {
class ExternInterface1
{
public:
using CppType = std::shared_ptr<::ExternInterface1>;
using ObjcType = DBExternInterface1*;
using Boxed = ExternInterface1;
static CppType toCpp(ObjcType objc);
static ObjcType fromCpp(const CppType& cpp);
private:
class ObjcProxy;
};
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#import "DBExternInterface1+Private.h"
#import "DBExternInterface1.h"
#import "DBClientInterface+Private.h"
#import "DBClientReturnedRecord+Private.h"
#import "DJICppWrapperCache+Private.h"
#import "DJIError.h"
#include <exception>
#include <utility>
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
@interface DBExternInterface1 ()
@property (nonatomic, readonly) ::djinni::DbxCppWrapperCache<::ExternInterface1>::Handle cppRef;
- (id)initWithCpp:(const std::shared_ptr<::ExternInterface1>&)cppRef;
@end
@implementation DBExternInterface1
- (id)initWithCpp:(const std::shared_ptr<::ExternInterface1>&)cppRef
{
if (self = [super init]) {
_cppRef.assign(cppRef);
}
return self;
}
- (nonnull DBClientReturnedRecord *)foo:(nullable id<DBClientInterface>)i {
try {
auto r = _cppRef.get()->foo(::djinni_generated::ClientInterface::toCpp(i));
return ::djinni_generated::ClientReturnedRecord::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
@end
namespace djinni_generated {
auto ExternInterface1::toCpp(ObjcType objc) -> CppType
{
if (!objc) {
return nullptr;
}
return objc.cppRef.get();
}
auto ExternInterface1::fromCpp(const CppType& cpp) -> ObjcType
{
if (!cpp) {
return nil;
}
return ::djinni::DbxCppWrapperCache<::ExternInterface1>::getInstance()->get(cpp, [] (const CppType& p) {
return [[DBExternInterface1 alloc] initWithCpp:p];
});
}
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#import "DBClientInterface.h"
#import "DBClientReturnedRecord.h"
#import <Foundation/Foundation.h>
@interface DBExternInterface1 : NSObject
- (nonnull DBClientReturnedRecord *)foo:(nullable id<DBClientInterface>)i;
@end
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#include "extern_interface_2.hpp"
#include <memory>
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
@protocol DBExternInterface2;
namespace djinni_generated {
class ExternInterface2
{
public:
using CppType = std::shared_ptr<::ExternInterface2>;
using ObjcType = id<DBExternInterface2>;
using Boxed = ExternInterface2;
static CppType toCpp(ObjcType objc);
static ObjcType fromCpp(const CppType& cpp);
private:
class ObjcProxy;
};
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#import "DBExternInterface2+Private.h"
#import "DBExternInterface2.h"
#import "DBExternRecordWithDerivings+Private.h"
#import "DBTestHelpers+Private.h"
#import "DJIObjcWrapperCache+Private.h"
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
namespace djinni_generated {
class ExternInterface2::ObjcProxy final
: public ::ExternInterface2
, public ::djinni::DbxObjcWrapperCache<ObjcProxy>::Handle
{
public:
using Handle::Handle;
::ExternRecordWithDerivings foo(const std::shared_ptr<::TestHelpers> & c_i) override
{
@autoreleasepool {
auto r = [(ObjcType)Handle::get() foo:(::djinni_generated::TestHelpers::fromCpp(c_i))];
return ::djinni_generated::ExternRecordWithDerivings::toCpp(r);
}
}
};
} // namespace djinni_generated
namespace djinni_generated {
auto ExternInterface2::toCpp(ObjcType objc) -> CppType
{
if (!objc) {
return nullptr;
}
return ::djinni::DbxObjcWrapperCache<ObjcProxy>::getInstance()->get(objc);
}
auto ExternInterface2::fromCpp(const CppType& cpp) -> ObjcType
{
if (!cpp) {
return nil;
}
return dynamic_cast<ObjcProxy&>(*cpp).Handle::get();
}
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#import "DBExternRecordWithDerivings.h"
#import "DBTestHelpers.h"
#import <Foundation/Foundation.h>
@protocol DBExternInterface2
- (nonnull DBExternRecordWithDerivings *)foo:(nullable DBTestHelpers *)i;
@end
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#import "DBExternRecordWithDerivings.h"
#include "extern_record_with_derivings.hpp"
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
@class DBExternRecordWithDerivings;
namespace djinni_generated {
struct ExternRecordWithDerivings
{
using CppType = ::ExternRecordWithDerivings;
using ObjcType = DBExternRecordWithDerivings*;
using Boxed = ExternRecordWithDerivings;
static CppType toCpp(ObjcType objc);
static ObjcType fromCpp(const CppType& cpp);
};
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#import "DBExternRecordWithDerivings+Private.h"
#import "DBRecordWithDerivings+Private.h"
#import "DJIMarshal+Private.h"
#include <cassert>
namespace djinni_generated {
auto ExternRecordWithDerivings::toCpp(ObjcType obj) -> CppType
{
assert(obj);
return {::djinni_generated::RecordWithDerivings::toCpp(obj.member),
::djinni::Enum<::color, DBColor>::toCpp(obj.e)};
}
auto ExternRecordWithDerivings::fromCpp(const CppType& cpp) -> ObjcType
{
return [[DBExternRecordWithDerivings alloc] initWithMember:(::djinni_generated::RecordWithDerivings::fromCpp(cpp.member))
e:(::djinni::Enum<::color, DBColor>::fromCpp(cpp.e))];
}
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#import "DBColor.h"
#import "DBRecordWithDerivings.h"
#import <Foundation/Foundation.h>
/** This file tests YAML dumped by Djinni can be parsed back in */
@interface DBExternRecordWithDerivings : NSObject
- (nonnull id)initWithMember:(nonnull DBRecordWithDerivings *)member
e:(DBColor)e;
@property (nonatomic, readonly, nonnull) DBRecordWithDerivings * member;
@property (nonatomic, readonly) DBColor e;
- (NSComparisonResult)compare:(nonnull DBExternRecordWithDerivings *)other;
@end
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from yaml-test.djinni
#import "DBExternRecordWithDerivings.h"
@implementation DBExternRecordWithDerivings
- (id)initWithMember:(nonnull DBRecordWithDerivings *)member
e:(DBColor)e
{
if (self = [super init]) {
_member = member;
_e = e;
}
return self;
}
- (BOOL)isEqual:(id)other
{
if (![other isKindOfClass:[DBExternRecordWithDerivings class]]) {
return NO;
}
DBExternRecordWithDerivings *typedOther = (DBExternRecordWithDerivings *)other;
return [self.member isEqual:typedOther.member] &&
self.e == typedOther.e;
}
- (NSUInteger)hash
{
return NSStringFromClass([self class]).hash ^
(self.member.hash) ^
(NSUInteger)self.e;
}
- (NSComparisonResult)compare:(DBExternRecordWithDerivings *)other
{
NSComparisonResult tempResult;
tempResult = [self.member compare:other.member];
if (tempResult != NSOrderedSame) {
return tempResult;
}
if (self.e < other.e) {
tempResult = NSOrderedAscending;
} else if (self.e > other.e) {
tempResult = NSOrderedDescending;
} else {
tempResult = NSOrderedSame;
}
if (tempResult != NSOrderedSame) {
return tempResult;
}
return NSOrderedSame;
}
@end
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#import "DBRecordWithDurationAndDerivings.h"
#include "record_with_duration_and_derivings.hpp"
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
@class DBRecordWithDurationAndDerivings;
namespace djinni_generated {
struct RecordWithDurationAndDerivings
{
using CppType = ::RecordWithDurationAndDerivings;
using ObjcType = DBRecordWithDurationAndDerivings*;
using Boxed = RecordWithDurationAndDerivings;
static CppType toCpp(ObjcType objc);
static ObjcType fromCpp(const CppType& cpp);
};
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#import "DBRecordWithDurationAndDerivings+Private.h"
#import "DJIMarshal+Private.h"
#import "Duration-objc.hpp"
#include <cassert>
namespace djinni_generated {
auto RecordWithDurationAndDerivings::toCpp(ObjcType obj) -> CppType
{
assert(obj);
return {::djinni::Duration<::djinni::F64, ::djinni::Duration_ns>::toCpp(obj.dt)};
}
auto RecordWithDurationAndDerivings::fromCpp(const CppType& cpp) -> ObjcType
{
return [[DBRecordWithDurationAndDerivings alloc] initWithDt:(::djinni::Duration<::djinni::F64, ::djinni::Duration_ns>::fromCpp(cpp.dt))];
}
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#import <Foundation/Foundation.h>
@interface DBRecordWithDurationAndDerivings : NSObject
- (nonnull id)initWithDt:(NSTimeInterval)dt;
@property (nonatomic, readonly) NSTimeInterval dt;
- (NSComparisonResult)compare:(nonnull DBRecordWithDurationAndDerivings *)other;
@end
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#import "DBRecordWithDurationAndDerivings.h"
@implementation DBRecordWithDurationAndDerivings
- (id)initWithDt:(NSTimeInterval)dt
{
if (self = [super init]) {
_dt = dt;
}
return self;
}
- (BOOL)isEqual:(id)other
{
if (![other isKindOfClass:[DBRecordWithDurationAndDerivings class]]) {
return NO;
}
DBRecordWithDurationAndDerivings *typedOther = (DBRecordWithDurationAndDerivings *)other;
return self.dt == typedOther.dt;
}
- (NSUInteger)hash
{
return NSStringFromClass([self class]).hash ^
((NSUInteger)self.dt);
}
- (NSComparisonResult)compare:(DBRecordWithDurationAndDerivings *)other
{
NSComparisonResult tempResult;
if (self.dt < other.dt) {
tempResult = NSOrderedAscending;
} else if (self.dt > other.dt) {
tempResult = NSOrderedDescending;
} else {
tempResult = NSOrderedSame;
}
if (tempResult != NSOrderedSame) {
return tempResult;
}
return NSOrderedSame;
}
@end
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#include "test_duration.hpp"
#include <memory>
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
@class DBTestDuration;
namespace djinni_generated {
class TestDuration
{
public:
using CppType = std::shared_ptr<::TestDuration>;
using ObjcType = DBTestDuration*;
using Boxed = TestDuration;
static CppType toCpp(ObjcType objc);
static ObjcType fromCpp(const CppType& cpp);
private:
class ObjcProxy;
};
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#import "DBTestDuration+Private.h"
#import "DBTestDuration.h"
#import "DJICppWrapperCache+Private.h"
#import "DJIError.h"
#import "DJIMarshal+Private.h"
#import "Duration-objc.hpp"
#include <exception>
#include <utility>
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
@interface DBTestDuration ()
@property (nonatomic, readonly) ::djinni::DbxCppWrapperCache<::TestDuration>::Handle cppRef;
- (id)initWithCpp:(const std::shared_ptr<::TestDuration>&)cppRef;
@end
@implementation DBTestDuration
- (id)initWithCpp:(const std::shared_ptr<::TestDuration>&)cppRef
{
if (self = [super init]) {
_cppRef.assign(cppRef);
}
return self;
}
+ (nonnull NSString *)hoursString:(NSTimeInterval)dt {
try {
auto r = ::TestDuration::hoursString(::djinni::Duration<::djinni::I32, ::djinni::Duration_h>::toCpp(dt));
return ::djinni::String::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (nonnull NSString *)minutesString:(NSTimeInterval)dt {
try {
auto r = ::TestDuration::minutesString(::djinni::Duration<::djinni::I32, ::djinni::Duration_min>::toCpp(dt));
return ::djinni::String::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (nonnull NSString *)secondsString:(NSTimeInterval)dt {
try {
auto r = ::TestDuration::secondsString(::djinni::Duration<::djinni::I32, ::djinni::Duration_s>::toCpp(dt));
return ::djinni::String::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (nonnull NSString *)millisString:(NSTimeInterval)dt {
try {
auto r = ::TestDuration::millisString(::djinni::Duration<::djinni::I32, ::djinni::Duration_ms>::toCpp(dt));
return ::djinni::String::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (nonnull NSString *)microsString:(NSTimeInterval)dt {
try {
auto r = ::TestDuration::microsString(::djinni::Duration<::djinni::I32, ::djinni::Duration_us>::toCpp(dt));
return ::djinni::String::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (nonnull NSString *)nanosString:(NSTimeInterval)dt {
try {
auto r = ::TestDuration::nanosString(::djinni::Duration<::djinni::I32, ::djinni::Duration_ns>::toCpp(dt));
return ::djinni::String::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)hours:(int32_t)count {
try {
auto r = ::TestDuration::hours(::djinni::I32::toCpp(count));
return ::djinni::Duration<::djinni::I32, ::djinni::Duration_h>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)minutes:(int32_t)count {
try {
auto r = ::TestDuration::minutes(::djinni::I32::toCpp(count));
return ::djinni::Duration<::djinni::I32, ::djinni::Duration_min>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)seconds:(int32_t)count {
try {
auto r = ::TestDuration::seconds(::djinni::I32::toCpp(count));
return ::djinni::Duration<::djinni::I32, ::djinni::Duration_s>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)millis:(int32_t)count {
try {
auto r = ::TestDuration::millis(::djinni::I32::toCpp(count));
return ::djinni::Duration<::djinni::I32, ::djinni::Duration_ms>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)micros:(int32_t)count {
try {
auto r = ::TestDuration::micros(::djinni::I32::toCpp(count));
return ::djinni::Duration<::djinni::I32, ::djinni::Duration_us>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)nanos:(int32_t)count {
try {
auto r = ::TestDuration::nanos(::djinni::I32::toCpp(count));
return ::djinni::Duration<::djinni::I32, ::djinni::Duration_ns>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)hoursf:(double)count {
try {
auto r = ::TestDuration::hoursf(::djinni::F64::toCpp(count));
return ::djinni::Duration<::djinni::F64, ::djinni::Duration_h>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)minutesf:(double)count {
try {
auto r = ::TestDuration::minutesf(::djinni::F64::toCpp(count));
return ::djinni::Duration<::djinni::F64, ::djinni::Duration_min>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)secondsf:(double)count {
try {
auto r = ::TestDuration::secondsf(::djinni::F64::toCpp(count));
return ::djinni::Duration<::djinni::F64, ::djinni::Duration_s>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)millisf:(double)count {
try {
auto r = ::TestDuration::millisf(::djinni::F64::toCpp(count));
return ::djinni::Duration<::djinni::F64, ::djinni::Duration_ms>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)microsf:(double)count {
try {
auto r = ::TestDuration::microsf(::djinni::F64::toCpp(count));
return ::djinni::Duration<::djinni::F64, ::djinni::Duration_us>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSTimeInterval)nanosf:(double)count {
try {
auto r = ::TestDuration::nanosf(::djinni::F64::toCpp(count));
return ::djinni::Duration<::djinni::F64, ::djinni::Duration_ns>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (nullable NSNumber *)box:(int64_t)count {
try {
auto r = ::TestDuration::box(::djinni::I64::toCpp(count));
return ::djinni::Optional<std::experimental::optional, ::djinni::Duration<::djinni::I64, ::djinni::Duration_s>>::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (int64_t)unbox:(nullable NSNumber *)dt {
try {
auto r = ::TestDuration::unbox(::djinni::Optional<std::experimental::optional, ::djinni::Duration<::djinni::I64, ::djinni::Duration_s>>::toCpp(dt));
return ::djinni::I64::fromCpp(r);
} DJINNI_TRANSLATE_EXCEPTIONS()
}
@end
namespace djinni_generated {
auto TestDuration::toCpp(ObjcType objc) -> CppType
{
if (!objc) {
return nullptr;
}
return objc.cppRef.get();
}
auto TestDuration::fromCpp(const CppType& cpp) -> ObjcType
{
if (!cpp) {
return nil;
}
return ::djinni::DbxCppWrapperCache<::TestDuration>::getInstance()->get(cpp, [] (const CppType& p) {
return [[DBTestDuration alloc] initWithCpp:p];
});
}
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from duration.djinni
#import <Foundation/Foundation.h>
@interface DBTestDuration : NSObject
+ (nonnull NSString *)hoursString:(NSTimeInterval)dt;
+ (nonnull NSString *)minutesString:(NSTimeInterval)dt;
+ (nonnull NSString *)secondsString:(NSTimeInterval)dt;
+ (nonnull NSString *)millisString:(NSTimeInterval)dt;
+ (nonnull NSString *)microsString:(NSTimeInterval)dt;
+ (nonnull NSString *)nanosString:(NSTimeInterval)dt;
+ (NSTimeInterval)hours:(int32_t)count;
+ (NSTimeInterval)minutes:(int32_t)count;
+ (NSTimeInterval)seconds:(int32_t)count;
+ (NSTimeInterval)millis:(int32_t)count;
+ (NSTimeInterval)micros:(int32_t)count;
+ (NSTimeInterval)nanos:(int32_t)count;
+ (NSTimeInterval)hoursf:(double)count;
+ (NSTimeInterval)minutesf:(double)count;
+ (NSTimeInterval)secondsf:(double)count;
+ (NSTimeInterval)millisf:(double)count;
+ (NSTimeInterval)microsf:(double)count;
+ (NSTimeInterval)nanosf:(double)count;
+ (nullable NSNumber *)box:(int64_t)count;
+ (int64_t)unbox:(nullable NSNumber *)dt;
@end
djinni-output-temp/cpp/test_duration.hpp
djinni-output-temp/cpp/record_with_duration_and_derivings.hpp
djinni-output-temp/cpp/record_with_duration_and_derivings.cpp
djinni-output-temp/cpp/date_record.hpp djinni-output-temp/cpp/date_record.hpp
djinni-output-temp/cpp/date_record.cpp
djinni-output-temp/cpp/map_date_record.hpp djinni-output-temp/cpp/map_date_record.hpp
djinni-output-temp/cpp/constants.hpp djinni-output-temp/cpp/constants.hpp
djinni-output-temp/cpp/constants.cpp djinni-output-temp/cpp/constants.cpp
...@@ -21,6 +25,8 @@ djinni-output-temp/cpp/record_with_derivings.cpp ...@@ -21,6 +25,8 @@ djinni-output-temp/cpp/record_with_derivings.cpp
djinni-output-temp/cpp/record_with_nested_derivings.hpp djinni-output-temp/cpp/record_with_nested_derivings.hpp
djinni-output-temp/cpp/record_with_nested_derivings.cpp djinni-output-temp/cpp/record_with_nested_derivings.cpp
djinni-output-temp/cpp/set_record.hpp djinni-output-temp/cpp/set_record.hpp
djinni-output-temp/java/TestDuration.java
djinni-output-temp/java/RecordWithDurationAndDerivings.java
djinni-output-temp/java/DateRecord.java djinni-output-temp/java/DateRecord.java
djinni-output-temp/java/MapDateRecord.java djinni-output-temp/java/MapDateRecord.java
djinni-output-temp/java/Constants.java djinni-output-temp/java/Constants.java
...@@ -39,6 +45,10 @@ djinni-output-temp/java/NestedCollection.java ...@@ -39,6 +45,10 @@ djinni-output-temp/java/NestedCollection.java
djinni-output-temp/java/RecordWithDerivings.java djinni-output-temp/java/RecordWithDerivings.java
djinni-output-temp/java/RecordWithNestedDerivings.java djinni-output-temp/java/RecordWithNestedDerivings.java
djinni-output-temp/java/SetRecord.java djinni-output-temp/java/SetRecord.java
djinni-output-temp/jni/NativeTestDuration.hpp
djinni-output-temp/jni/NativeTestDuration.cpp
djinni-output-temp/jni/NativeRecordWithDurationAndDerivings.hpp
djinni-output-temp/jni/NativeRecordWithDurationAndDerivings.cpp
djinni-output-temp/jni/NativeDateRecord.hpp djinni-output-temp/jni/NativeDateRecord.hpp
djinni-output-temp/jni/NativeDateRecord.cpp djinni-output-temp/jni/NativeDateRecord.cpp
djinni-output-temp/jni/NativeMapDateRecord.hpp djinni-output-temp/jni/NativeMapDateRecord.hpp
...@@ -74,6 +84,9 @@ djinni-output-temp/jni/NativeRecordWithNestedDerivings.hpp ...@@ -74,6 +84,9 @@ djinni-output-temp/jni/NativeRecordWithNestedDerivings.hpp
djinni-output-temp/jni/NativeRecordWithNestedDerivings.cpp djinni-output-temp/jni/NativeRecordWithNestedDerivings.cpp
djinni-output-temp/jni/NativeSetRecord.hpp djinni-output-temp/jni/NativeSetRecord.hpp
djinni-output-temp/jni/NativeSetRecord.cpp djinni-output-temp/jni/NativeSetRecord.cpp
djinni-output-temp/objc/DBTestDuration.h
djinni-output-temp/objc/DBRecordWithDurationAndDerivings.h
djinni-output-temp/objc/DBRecordWithDurationAndDerivings.mm
djinni-output-temp/objc/DBDateRecord.h djinni-output-temp/objc/DBDateRecord.h
djinni-output-temp/objc/DBDateRecord.mm djinni-output-temp/objc/DBDateRecord.mm
djinni-output-temp/objc/DBMapDateRecord.h djinni-output-temp/objc/DBMapDateRecord.h
...@@ -105,6 +118,10 @@ djinni-output-temp/objc/DBRecordWithNestedDerivings.h ...@@ -105,6 +118,10 @@ djinni-output-temp/objc/DBRecordWithNestedDerivings.h
djinni-output-temp/objc/DBRecordWithNestedDerivings.mm djinni-output-temp/objc/DBRecordWithNestedDerivings.mm
djinni-output-temp/objc/DBSetRecord.h djinni-output-temp/objc/DBSetRecord.h
djinni-output-temp/objc/DBSetRecord.mm djinni-output-temp/objc/DBSetRecord.mm
djinni-output-temp/objc/DBTestDuration+Private.h
djinni-output-temp/objc/DBTestDuration+Private.mm
djinni-output-temp/objc/DBRecordWithDurationAndDerivings+Private.h
djinni-output-temp/objc/DBRecordWithDurationAndDerivings+Private.mm
djinni-output-temp/objc/DBDateRecord+Private.h djinni-output-temp/objc/DBDateRecord+Private.h
djinni-output-temp/objc/DBDateRecord+Private.mm djinni-output-temp/objc/DBDateRecord+Private.mm
djinni-output-temp/objc/DBMapDateRecord+Private.h djinni-output-temp/objc/DBMapDateRecord+Private.h
...@@ -139,3 +156,4 @@ djinni-output-temp/objc/DBRecordWithNestedDerivings+Private.h ...@@ -139,3 +156,4 @@ djinni-output-temp/objc/DBRecordWithNestedDerivings+Private.h
djinni-output-temp/objc/DBRecordWithNestedDerivings+Private.mm djinni-output-temp/objc/DBRecordWithNestedDerivings+Private.mm
djinni-output-temp/objc/DBSetRecord+Private.h djinni-output-temp/objc/DBSetRecord+Private.h
djinni-output-temp/objc/DBSetRecord+Private.mm djinni-output-temp/objc/DBSetRecord+Private.mm
djinni-output-temp/yaml/yaml-test.yaml
#pragma once
#include "djinni_support.hpp"
#include <cassert>
#include <chrono>
namespace djinni
{
struct DurationJniInfo
{
const GlobalRef<jclass> clazz { jniFindClass("java/time/Duration") };
const jmethodID method_ofNanos { jniGetStaticMethodID(clazz.get(), "ofNanos", "(J)Ljava/time/Duration;") };
const jmethodID method_toNanos { jniGetMethodID(clazz.get(), "toNanos", "()J") };
};
// This is only a helper, trying to use it as member/param will fail
template<class Ratio>
struct DurationPeriod;
using Duration_h = DurationPeriod<std::ratio<3600>>;
using Duration_min = DurationPeriod<std::ratio<60>>;
using Duration_s = DurationPeriod<std::ratio<1>>;
using Duration_ms = DurationPeriod<std::milli>;
using Duration_us = DurationPeriod<std::micro>;
using Duration_ns = DurationPeriod<std::nano>;
template<class Rep, class Period>
struct Duration;
template<class Rep, class Ratio>
struct Duration<Rep, DurationPeriod<Ratio>>
{
using CppType = std::chrono::duration<typename Rep::CppType, Ratio>;
using JniType = jobject;
using Boxed = Duration;
static CppType toCpp(JNIEnv* jniEnv, JniType j)
{
assert(j != nullptr);
const auto& data = JniClass<DurationJniInfo>::get();
assert(jniEnv->IsInstanceOf(j, data.clazz.get()));
jlong nanos = jniEnv->CallLongMethod(j, data.method_toNanos);
jniExceptionCheck(jniEnv);
return std::chrono::duration_cast<CppType>(std::chrono::duration<jlong, std::nano>{nanos});
}
static LocalRef<jobject> fromCpp(JNIEnv* jniEnv, CppType c)
{
const auto& data = JniClass<DurationJniInfo>::get();
jlong nanos = std::chrono::duration_cast<std::chrono::duration<jlong, std::nano>>(c).count();
auto j = LocalRef<JniType>{jniEnv->CallStaticObjectMethod(data.clazz.get(), data.method_ofNanos, nanos)};
jniExceptionCheck(jniEnv);
return j;
}
};
}
#pragma once
#import <Foundation/Foundation.h>
#include <cassert>
#include <chrono>
namespace djinni
{
// This is only a helper, trying to use it as member/param will fail
template<class Ratio>
struct DurationPeriod;
using Duration_h = DurationPeriod<std::ratio<3600>>;
using Duration_min = DurationPeriod<std::ratio<60>>;
using Duration_s = DurationPeriod<std::ratio<1>>;
using Duration_ms = DurationPeriod<std::milli>;
using Duration_us = DurationPeriod<std::micro>;
using Duration_ns = DurationPeriod<std::nano>;
template<class Rep, class Period>
struct Duration;
template<class Rep, class Ratio>
struct Duration<Rep, DurationPeriod<Ratio>>
{
using CppType = std::chrono::duration<typename Rep::CppType, Ratio>;
using ObjcType = NSTimeInterval;
static CppType toCpp(ObjcType dt)
{
return std::chrono::duration_cast<CppType>(std::chrono::duration<double>{dt});
}
static ObjcType fromCpp(CppType dt)
{
return std::chrono::duration_cast<std::chrono::duration<double>>(dt).count();
}
struct Boxed
{
using ObjcType = NSNumber*;
static CppType toCpp(ObjcType dt)
{
assert(dt);
return std::chrono::duration_cast<CppType>(Duration::toCpp([dt doubleValue]));
}
static ObjcType fromCpp(CppType dt)
{
return [NSNumber numberWithDouble:Duration::fromCpp(dt)];
}
};
};
}
#include "test_duration.hpp"
std::string TestDuration::hoursString(std::chrono::duration<int32_t, std::ratio<3600>> dt)
{
return std::to_string(dt.count());
}
std::string TestDuration::minutesString(std::chrono::duration<int32_t, std::ratio<60>> dt)
{
return std::to_string(dt.count());
}
std::string TestDuration::secondsString(std::chrono::duration<int32_t, std::ratio<1>> dt)
{
return std::to_string(dt.count());
}
std::string TestDuration::millisString(std::chrono::duration<int32_t, std::milli> dt)
{
return std::to_string(dt.count());
}
std::string TestDuration::microsString(std::chrono::duration<int32_t, std::micro> dt)
{
return std::to_string(dt.count());
}
std::string TestDuration::nanosString(std::chrono::duration<int32_t, std::nano> dt)
{
return std::to_string(dt.count());
}
std::chrono::duration<int32_t, std::ratio<3600>> TestDuration::hours(int32_t count)
{
return std::chrono::duration<int32_t, std::ratio<3600>>{count};
}
std::chrono::duration<int32_t, std::ratio<60>> TestDuration::minutes(int32_t count)
{
return std::chrono::duration<int32_t, std::ratio<60>>{count};
}
std::chrono::duration<int32_t, std::ratio<1>> TestDuration::seconds(int32_t count)
{
return std::chrono::duration<int32_t, std::ratio<1>>{count};
}
std::chrono::duration<int32_t, std::milli> TestDuration::millis(int32_t count)
{
return std::chrono::duration<int32_t, std::milli>{count};
}
std::chrono::duration<int32_t, std::micro> TestDuration::micros(int32_t count)
{
return std::chrono::duration<int32_t, std::micro>{count};
}
std::chrono::duration<int32_t, std::nano> TestDuration::nanos(int32_t count)
{
return std::chrono::duration<int32_t, std::nano>{count};
}
std::chrono::duration<double, std::ratio<3600>> TestDuration::hoursf(double count)
{
return std::chrono::duration<double, std::ratio<3600>>{count};
}
std::chrono::duration<double, std::ratio<60>> TestDuration::minutesf(double count)
{
return std::chrono::duration<double, std::ratio<60>>{count};
}
std::chrono::duration<double, std::ratio<1>> TestDuration::secondsf(double count)
{
return std::chrono::duration<double, std::ratio<1>>{count};
}
std::chrono::duration<double, std::milli> TestDuration::millisf(double count)
{
return std::chrono::duration<double, std::milli>{count};
}
std::chrono::duration<double, std::micro> TestDuration::microsf(double count)
{
return std::chrono::duration<double, std::micro>{count};
}
std::chrono::duration<double, std::nano> TestDuration::nanosf(double count)
{
return std::chrono::duration<double, std::nano>{count};
}
std::experimental::optional<std::chrono::duration<int64_t, std::ratio<1>>> TestDuration::box(int64_t count)
{
using D = std::chrono::duration<int64_t, std::ratio<1>>;
using O = std::experimental::optional<D>;
return count < 0 ? O{} : O{D{count}};
}
int64_t TestDuration::unbox(std::experimental::optional<std::chrono::duration<int64_t, std::ratio<1>>> dt)
{
return dt ? dt->count() : -1;
}
...@@ -17,6 +17,7 @@ public class AllTests extends TestSuite { ...@@ -17,6 +17,7 @@ public class AllTests extends TestSuite {
mySuite.addTestSuite(EnumTest.class); mySuite.addTestSuite(EnumTest.class);
mySuite.addTestSuite(PrimitivesTest.class); mySuite.addTestSuite(PrimitivesTest.class);
mySuite.addTestSuite(TokenTest.class); mySuite.addTestSuite(TokenTest.class);
mySuite.addTestSuite(DurationTest.class);
return mySuite; return mySuite;
} }
......
package com.dropbox.djinni.test;
import junit.framework.TestCase;
import java.time.Duration;
public class DurationTest extends TestCase {
public void test() {
assertEquals(TestDuration.hoursString(Duration.ofHours(1)), "1");
assertEquals(TestDuration.minutesString(Duration.ofMinutes(1)), "1");
assertEquals(TestDuration.secondsString(Duration.ofSeconds(1)), "1");
assertEquals(TestDuration.millisString(Duration.ofMillis(1)), "1");
assertEquals(TestDuration.microsString(Duration.ofNanos(1000)), "1");
assertEquals(TestDuration.nanosString(Duration.ofNanos(1)), "1");
assertEquals(TestDuration.hours(1).toHours(), 1);
assertEquals(TestDuration.minutes(1).toMinutes(), 1);
assertEquals(TestDuration.seconds(1).getSeconds(), 1);
assertEquals(TestDuration.millis(1).toMillis(), 1);
assertEquals(TestDuration.micros(1).toNanos(), 1000);
assertEquals(TestDuration.nanos(1).toNanos(), 1);
assertEquals(TestDuration.hoursf(1.5).toMinutes(), 90);
assertEquals(TestDuration.minutesf(1.5).getSeconds(), 90);
assertEquals(TestDuration.secondsf(1.5).toMillis(), 1500);
assertEquals(TestDuration.millisf(1.5).toNanos(), 1500 * 1000);
assertEquals(TestDuration.microsf(1.5).toNanos(), 1500);
assertEquals(TestDuration.nanosf(1.0).toNanos(), 1);
assertEquals(TestDuration.box(1).getSeconds(), 1);
assertEquals(TestDuration.box(-1), null);
assertEquals(TestDuration.unbox(Duration.ofSeconds(1)), 1);
assertEquals(TestDuration.unbox(null), -1);
}
}
#import <XCTest/XCTest.h>
#import "DBDateRecord+Private.h" #import "DBDateRecord+Private.h"
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include "date_record.hpp" #include "date_record.hpp"
#import <XCTest/XCTest.h>
@interface DBDateRecordTests : XCTestCase @interface DBDateRecordTests : XCTestCase
...@@ -35,14 +35,14 @@ ...@@ -35,14 +35,14 @@
- (void)testObjcRoundTrip - (void)testObjcRoundTrip
{ {
NSDate *now = [NSDate date]; NSDate *now = [NSDate date];
DBDateRecord *date1 = [[DBDateRecord alloc] initWithCreatedAt:now]; DBDateRecord *date1 = [[DBDateRecord alloc] initWithCreatedAt:now];
const auto cpp_date1 = [date1 cppDateRecord]; const auto cpp_date1 = djinni_generated::DateRecord::toCpp(date1);
DBDateRecord *date2 = [[DBDateRecord alloc] initWithCppDateRecord:cpp_date1]; DBDateRecord *date2 = djinni_generated::DateRecord::fromCpp(cpp_date1);
const auto cpp_date2 = [date2 cppDateRecord]; const auto cpp_date2 = djinni_generated::DateRecord::toCpp(date2);
DBDateRecord *date3 = [[DBDateRecord alloc] initWithCppDateRecord:cpp_date2]; DBDateRecord *date3 = djinni_generated::DateRecord::fromCpp(cpp_date2);
const auto cpp_date3 = [date3 cppDateRecord]; const auto cpp_date3 = djinni_generated::DateRecord::toCpp(date3);
const bool cpp_is_equal = cpp_date1.created_at == cpp_date2.created_at && cpp_date2.created_at == cpp_date3.created_at; const bool cpp_is_equal = cpp_date1.created_at == cpp_date2.created_at && cpp_date2.created_at == cpp_date3.created_at;
// cpp is a integer representation (with less precision than NSDate), so direct comparison will work // cpp is a integer representation (with less precision than NSDate), so direct comparison will work
XCTAssertTrue(cpp_is_equal); XCTAssertTrue(cpp_is_equal);
...@@ -59,8 +59,8 @@ ...@@ -59,8 +59,8 @@
{ {
const auto now = std::chrono::system_clock::now(); const auto now = std::chrono::system_clock::now();
DateRecord cpp_date_now(now); DateRecord cpp_date_now(now);
DBDateRecord *objcDate = [[DBDateRecord alloc] initWithCppDateRecord:cpp_date_now]; DBDateRecord *objcDate = djinni_generated::DateRecord::fromCpp(cpp_date_now);
const auto boomerang_cpp_date = [objcDate cppDateRecord]; const auto boomerang_cpp_date = djinni_generated::DateRecord::toCpp(objcDate);
XCTAssertTrue(now == boomerang_cpp_date.created_at); XCTAssertTrue(now == boomerang_cpp_date.created_at);
} }
......
#import <Foundation/Foundation.h>
#import "DBTestHelpers.h"
#import "DBTestDuration.h"
#import <XCTest/XCTest.h>
@interface DBDurationTests : XCTestCase
@end
@implementation DBDurationTests
- (void)setUp
{
[super setUp];
}
- (void)tearDown
{
[super tearDown];
}
- (void)test
{
XCTAssertEqual([[DBTestDuration hoursString:3600] compare:@"1"], NSOrderedSame);
XCTAssertEqual([[DBTestDuration minutesString:60] compare:@"1"], NSOrderedSame);
XCTAssertEqual([[DBTestDuration secondsString:1] compare:@"1"], NSOrderedSame);
XCTAssertEqual([[DBTestDuration millisString:0.001] compare:@"1"], NSOrderedSame);
XCTAssertEqual([[DBTestDuration microsString:0.000001] compare:@"1"], NSOrderedSame);
XCTAssertEqual([[DBTestDuration nanosString:0.000000001] compare:@"1"], NSOrderedSame);
XCTAssertEqualWithAccuracy([DBTestDuration hours:1], 3600, 0.001);
XCTAssertEqualWithAccuracy([DBTestDuration minutes:1], 60, 0.001);
XCTAssertEqualWithAccuracy([DBTestDuration seconds:1], 1, 0.001);
XCTAssertEqualWithAccuracy([DBTestDuration millis:1], 0.001, 0.000001);
XCTAssertEqualWithAccuracy([DBTestDuration micros:1], 0.000001, 0.000000001);
XCTAssertEqualWithAccuracy([DBTestDuration nanos:1], 0.000000001, 0.000000000001);
XCTAssertEqualWithAccuracy([DBTestDuration hoursf:1.5], 5400, 0.001);
XCTAssertEqualWithAccuracy([DBTestDuration minutesf:1.5], 90, 0.001);
XCTAssertEqualWithAccuracy([DBTestDuration secondsf:1.5], 1.5, 0.001);
XCTAssertEqualWithAccuracy([DBTestDuration millisf:1.5], 0.0015, 0.000001);
XCTAssertEqualWithAccuracy([DBTestDuration microsf:1.5], 0.0000015, 0.000000001);
XCTAssertEqualWithAccuracy([DBTestDuration nanosf:1.0], 0.000000001, 0.000000000001);
XCTAssertEqual([[DBTestDuration box:1.0] intValue],1);
XCTAssertEqual([DBTestDuration box:-1.0], nil);
XCTAssertEqual([DBTestDuration unbox:[NSNumber numberWithDouble:1.0]], 1);
XCTAssertEqual([DBTestDuration unbox:nil], -1);
}
@end
...@@ -29,6 +29,7 @@ cpp_out="$base_dir/generated-src/cpp" ...@@ -29,6 +29,7 @@ cpp_out="$base_dir/generated-src/cpp"
jni_out="$base_dir/generated-src/jni" jni_out="$base_dir/generated-src/jni"
objc_out="$base_dir/generated-src/objc" objc_out="$base_dir/generated-src/objc"
java_out="$base_dir/generated-src/java/com/dropbox/djinni/test" java_out="$base_dir/generated-src/java/com/dropbox/djinni/test"
yaml_out="$base_dir/generated-src/yaml"
java_package="com.dropbox.djinni.test" java_package="com.dropbox.djinni.test"
...@@ -78,11 +79,38 @@ fi ...@@ -78,11 +79,38 @@ fi
--objcpp-out "$temp_out_relative/objc" \ --objcpp-out "$temp_out_relative/objc" \
--objc-type-prefix DB \ --objc-type-prefix DB \
\ \
--idl "$in_relative" \
--list-in-files "./generated-src/inFileList.txt" \ --list-in-files "./generated-src/inFileList.txt" \
--list-out-files "./generated-src/outFileList.txt"\ --list-out-files "./generated-src/outFileList.txt"\
\
--yaml-out "$temp_out_relative/yaml" \
--yaml-out-file "yaml-test.yaml" \
--yaml-prefix "test_" \
\
--idl "$in_relative" \
) )
# Make sure we can parse back our own generated YAML file
cp "$base_dir/djinni/yaml-test.djinni" "$temp_out/yaml"
"$base_dir/../src/run-assume-built" \
--java-out "$temp_out/java" \
--java-package $java_package \
--ident-java-field mFooBar \
\
--cpp-out "$temp_out/cpp" \
--ident-cpp-enum-type foo_bar \
--cpp-optional-template "std::experimental::optional" \
--cpp-optional-header "<experimental/optional>" \
\
--jni-out "$temp_out/jni" \
--ident-jni-class NativeFooBar \
--ident-jni-file NativeFooBar \
\
--objc-out "$temp_out/objc" \
--objcpp-out "$temp_out/objc" \
--objc-type-prefix DB \
\
--idl "$temp_out/yaml/yaml-test.djinni"
# Copy changes from "$temp_output" to final dir. # Copy changes from "$temp_output" to final dir.
mirror() { mirror() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment