Commit 4c062e36 authored by Miro Knejp's avatar Miro Knejp

New meta MExtern with all data required to interface with extern types

Marshals use the respective members to resolve typenames, signatures, parameter types etc.
All names in MExtern are expected to be fully qualified.
parent 0521e806
......@@ -58,8 +58,14 @@ class CppMarshal(spec: Spec) extends Marshal(spec) {
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()
}
}
private def toCppType(ty: TypeRef, namespace: Option[String] = None): String = toCppType(ty.resolved, namespace)
private def toCppType(tm: MExpr, namespace: Option[String]): String = {
......@@ -78,6 +84,10 @@ class CppMarshal(spec: Spec) extends Marshal(spec) {
case DRecord => 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)
}
def expr(tm: MExpr): String = {
......@@ -99,6 +109,11 @@ class CppMarshal(spec: Spec) extends Marshal(spec) {
case DEnum => valueType
case _ => refType
}
case e: MExtern => e.defType match {
case DInterface => refType
case DEnum => valueType
case DRecord => if(e.cpp.byValue) valueType else refType
}
case MOptional => toType(expr.args.head)
case _ => refType
}
......
......@@ -36,6 +36,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
def references(m: Meta, exclude: String = ""): Seq[SymbolReference] = m match {
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 e: MExtern => List(ImportRef(e.jni.header))
case _ => List()
}
......@@ -46,6 +47,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
case MOptional => toJniType(m.args.head, true)
case MBinary => "jbyteArray"
case tp: MParam => helperClass(tp.name) + "::JniType"
case e: MExtern => helperClass(m) + (if(needRef) "::Boxed" else "") + "::JniType"
case _ => "jobject"
}
......@@ -70,6 +72,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
case MSet => "Ljava/util/HashSet;"
case MMap => "Ljava/util/HashMap;"
}
case e: MExtern => e.jni.typeSignature
case MParam(_) => "Ljava/lang/Object;"
case d: MDef => s"L${undecoratedTypename(d.name, d.body)};"
}
......@@ -80,6 +83,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
private def helperName(tm: MExpr): String = tm.base match {
case d: MDef => withNs(Some(spec.jniNamespace), helperClass(d.name))
case e: MExtern => e.jni.translator
case o => withNs(Some("djinni"), o match {
case p: MPrimitive => p.idlName match {
case "i8" => "I8"
......@@ -98,6 +102,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
case MSet => "Set"
case MMap => "Map"
case d: MDef => throw new AssertionError("unreachable")
case e: MExtern => throw new AssertionError("unreachable")
case p: MParam => throw new AssertionError("not applicable")
})
}
......
......@@ -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 _ => 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")
}
}
......@@ -317,6 +326,11 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
case "boolean" => s"(${idJava.field(f.ident)} ? 1 : 0)"
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")
}
w.wl(s"hashCode = hashCode * $multiplier + $fieldHashCode;")
......@@ -327,6 +341,18 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
}
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("@Override")
val nonnullAnnotation = javaNonnullAnnotation.map(_ + " ").getOrElse("")
......@@ -335,22 +361,17 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
for (f <- r.fields) {
f.ty.resolved.base match {
case MString => w.wl(s"tempResult = this.${idJava.field(f.ident)}.compareTo(other.${idJava.field(f.ident)});")
case t: MPrimitive =>
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 t: MPrimitive => primitiveCompare(f.ident)
case df: MDef => df.defType match {
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 _ => 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")
}
w.w("if (tempResult != 0)").braced {
......
......@@ -44,17 +44,22 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) {
ty.resolved.base match {
case MOptional => javaNullableAnnotation
case p: MPrimitive => None
case m: MDef =>
m.defType match {
case m: MDef => m.defType match {
case DInterface => javaNullableAnnotation
case DEnum => 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
}
}
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 = {
tm.base match {
case MOptional =>
......@@ -66,8 +71,8 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) {
case MOptional => throw new AssertionError("nested optional?")
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 =>
val args = if (tm.args.isEmpty) "" else tm.args.map(f(_, true)).mkString("<", ", ", ">")
val base = o match {
case p: MPrimitive => if (needRef) p.jBoxed else p.jName
case MString => "String"
......@@ -78,9 +83,10 @@ class JavaMarshal(spec: Spec) extends Marshal(spec) {
case MSet => "HashSet"
case MMap => "HashMap"
case d: MDef => withPackage(packageName, idJava.ty(d.name))
case e: MExtern => throw new AssertionError("unreachable")
case p: MParam => idJava.typeParam(p.name)
}
base + args
base + args(tm)
}
}
f(tm, false)
......
......@@ -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 _ => 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")
}
}
......@@ -311,6 +320,11 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
case DEnum => w.w(s"(NSUInteger)self.${idObjc.field(f.ident)}")
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")
}
}
......@@ -345,6 +359,11 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
case DEnum => generatePrimitiveOrder(f.ident, w)
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")
}
w.w("if (tempResult != NSOrderedSame)").braced {
......@@ -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)
def toObjcType(ty: TypeRef): (String, Boolean) = toObjcType(ty.resolved, false)
def toObjcType(ty: TypeRef, needRef: Boolean): (String, Boolean) = toObjcType(ty.resolved, needRef)
......@@ -406,6 +426,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
val ext = d.body.asInstanceOf[Interface].ext
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")
}
base
......@@ -414,6 +435,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
f(tm, needRef)
}
// TODO: this should be in ObjcMarshal
def toObjcTypeDef(ty: TypeRef): String = toObjcTypeDef(ty.resolved, false)
def toObjcTypeDef(ty: TypeRef, needRef: Boolean): String = toObjcTypeDef(ty.resolved, needRef)
def toObjcTypeDef(tm: MExpr): String = toObjcTypeDef(tm, false)
......
......@@ -16,15 +16,22 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) {
def fqTypename(name: String, ty: TypeDef): String = typename(name, ty)
def nullability(tm: MExpr): Option[String] = {
val nonnull = Some("nonnull")
val nullable = Some("nullable")
tm.base match {
case MOptional => Some("nullable")
case MOptional => nullable
case MPrimitive(_,_,_,_,_,_,_,_) => None
case d: MDef => d.defType match {
case DEnum => None
case DInterface => Some("nullable")
case DRecord => Some("nonnull")
case DInterface => nullable
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
}
}
......@@ -61,6 +68,7 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) {
val prefix = if (r.ext.objc) "../" else ""
List(ImportRef(q(spec.objcIncludePrefix + prefix + headerName(d.name))))
}
case e: MExtern => List(ImportRef(e.objc.header))
case p: MParam => List()
}
......@@ -101,6 +109,10 @@ class ObjcMarshal(spec: Spec) extends Marshal(spec) {
else
(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")
}
base
......
......@@ -43,6 +43,7 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) {
val objcName = d.name + (if (r.ext.objc) "_base" else "")
List(ImportRef(q(spec.objcppIncludePrefix + privateHeaderName(objcName))))
}
case e: MExtern => List(ImportRef(e.objcpp.header))
case p: MParam => List()
}
......@@ -56,6 +57,7 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) {
case DEnum => withNs(Some("djinni"), s"Enum<${cppMarshal.fqTypename(tm)}, ${objcMarshal.fqTypename(tm)}>")
case _ => withNs(Some(spec.objcppNamespace), helperClass(d.name))
}
case e: MExtern => e.objcpp.translator
case o => withNs(Some("djinni"), o match {
case p: MPrimitive => p.idlName match {
case "i8" => "I8"
......@@ -74,6 +76,7 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) {
case MSet => "Set"
case MMap => "Map"
case d: MDef => throw new AssertionError("unreachable")
case e: MExtern => throw new AssertionError("unreachable")
case p: MParam => throw new AssertionError("not applicable")
})
}
......
......@@ -30,6 +30,43 @@ abstract sealed class Meta
case class MParam(name: String) extends Meta { val numParams = 0 }
case class MDef(name: String, override val numParams: Int, defType: DefType, body: TypeDef) extends Meta
case class MExtern(name: String, override val numParams: Int, defType: DefType, body: TypeDef, cpp: MExtern.Cpp, objc: MExtern.Objc, objcpp: MExtern.Objcpp, java: MExtern.Java, jni: MExtern.Jni) extends Meta
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. 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 DefType
......
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