From 17466d4ee52daa9daec7cc0576cf6ebb48c1e2f9 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Tue, 7 Jan 2025 15:01:26 -0500 Subject: [PATCH 01/20] Added support for 'requiring' (will change to 'requires' eventually) --- src/main/scala/djinni/ast/ast.scala | 8 +++++++- src/main/scala/djinni/parser.scala | 25 ++++++++++++++++++++----- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/main/scala/djinni/ast/ast.scala b/src/main/scala/djinni/ast/ast.scala index f5ae3c3e..29db02bf 100644 --- a/src/main/scala/djinni/ast/ast.scala +++ b/src/main/scala/djinni/ast/ast.scala @@ -16,6 +16,7 @@ package djinni.ast import djinni.ast.Record.DerivingType.DerivingType +import djinni.ast.Interface.RequiringType.RequiringType import djinni.meta.MExpr import djinni.syntax.Loc @@ -110,9 +111,14 @@ object Record { case class Interface( ext: Ext, methods: Seq[Interface.Method], - consts: Seq[Const] + consts: Seq[Const], + requiringTypes: Set[RequiringType] ) extends TypeDef object Interface { + object RequiringType extends Enumeration { + type RequiringType = Value + val Eq, Ord = Value + } case class Method( ident: Ident, params: Seq[Field], diff --git a/src/main/scala/djinni/parser.scala b/src/main/scala/djinni/parser.scala index d7fc5449..c0512a0b 100644 --- a/src/main/scala/djinni/parser.scala +++ b/src/main/scala/djinni/parser.scala @@ -16,6 +16,7 @@ package djinni import djinni.ast.Interface.Method +import djinni.ast.Interface.RequiringType.RequiringType import djinni.ast.Record.DerivingType.DerivingType import djinni.ast._ import djinni.syntax._ @@ -189,11 +190,12 @@ case class Parser(includePaths: List[String]) { def interfaceHeader: Parser[Ext] = "interface" ~> extInterface def interface: Parser[Interface] = - interfaceHeader ~ bracesList(method | const) ^^ { - case ext ~ items => { + interfaceHeader ~ bracesList(method | const) ~ opt(requiring) ^^ { + case ext ~ items ~ requiring => { val methods = items collect { case m: Method => m } val consts = items collect { case c: Const => c } - Interface(ext, methods, consts) + val requiringTypes = requiring.getOrElse(Set[RequiringType]()) + Interface(ext, methods, consts, requiringTypes) } } @@ -209,8 +211,9 @@ case class Parser(includePaths: List[String]) { case ext ~ deriving => Record(ext, List(), List(), deriving.getOrElse(Set[DerivingType]())) } - def externInterface: Parser[Interface] = interfaceHeader ^^ { case ext => - Interface(ext, List(), List()) + def externInterface: Parser[Interface] = interfaceHeader ~ opt(requiring) ^^ { + case ext ~ requiring => + Interface(ext, List(), List(), requiring.getOrElse(Set[RequiringType]())) } def staticLabel: Parser[Boolean] = ("static ".r | "".r) ^^ { @@ -230,6 +233,18 @@ case class Parser(includePaths: List[String]) { } def ret: Parser[TypeRef] = ":" ~> typeRef + def requiring: Parser[Set[RequiringType]] = + "requiring" ~> parens(rep1sepend(ident, ",")) ^^ { + _.map(ident => + ident.name match { + case "eq" => Interface.RequiringType.Eq + case "ord" => Interface.RequiringType.Ord + case _ => + return err(s"""Unrecognized requiring type "${ident.name}"""") + } + ).toSet + } + def boolValue: Parser[Boolean] = "([Tt]rue)|([Ff]alse)".r ^^ { s: String => s.toBoolean } From 3972ff27386d2abec80ba6d342116f4eee62fe8b Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Tue, 7 Jan 2025 15:19:26 -0500 Subject: [PATCH 02/20] Renamed 'requiring' to 'requires' --- src/main/scala/djinni/ast/ast.scala | 8 ++++---- src/main/scala/djinni/parser.scala | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/scala/djinni/ast/ast.scala b/src/main/scala/djinni/ast/ast.scala index 29db02bf..d57637b9 100644 --- a/src/main/scala/djinni/ast/ast.scala +++ b/src/main/scala/djinni/ast/ast.scala @@ -16,7 +16,7 @@ package djinni.ast import djinni.ast.Record.DerivingType.DerivingType -import djinni.ast.Interface.RequiringType.RequiringType +import djinni.ast.Interface.RequiresType.RequiresType import djinni.meta.MExpr import djinni.syntax.Loc @@ -112,11 +112,11 @@ case class Interface( ext: Ext, methods: Seq[Interface.Method], consts: Seq[Const], - requiringTypes: Set[RequiringType] + requiresTypes: Set[RequiresType] ) extends TypeDef object Interface { - object RequiringType extends Enumeration { - type RequiringType = Value + object RequiresType extends Enumeration { + type RequiresType = Value val Eq, Ord = Value } case class Method( diff --git a/src/main/scala/djinni/parser.scala b/src/main/scala/djinni/parser.scala index c0512a0b..f5129534 100644 --- a/src/main/scala/djinni/parser.scala +++ b/src/main/scala/djinni/parser.scala @@ -16,7 +16,7 @@ package djinni import djinni.ast.Interface.Method -import djinni.ast.Interface.RequiringType.RequiringType +import djinni.ast.Interface.RequiresType.RequiresType import djinni.ast.Record.DerivingType.DerivingType import djinni.ast._ import djinni.syntax._ @@ -190,12 +190,12 @@ case class Parser(includePaths: List[String]) { def interfaceHeader: Parser[Ext] = "interface" ~> extInterface def interface: Parser[Interface] = - interfaceHeader ~ bracesList(method | const) ~ opt(requiring) ^^ { - case ext ~ items ~ requiring => { + interfaceHeader ~ bracesList(method | const) ~ opt(requires) ^^ { + case ext ~ items ~ requires => { val methods = items collect { case m: Method => m } val consts = items collect { case c: Const => c } - val requiringTypes = requiring.getOrElse(Set[RequiringType]()) - Interface(ext, methods, consts, requiringTypes) + val requiresTypes = requires.getOrElse(Set[RequiresType]()) + Interface(ext, methods, consts, requiresTypes) } } @@ -211,9 +211,9 @@ case class Parser(includePaths: List[String]) { case ext ~ deriving => Record(ext, List(), List(), deriving.getOrElse(Set[DerivingType]())) } - def externInterface: Parser[Interface] = interfaceHeader ~ opt(requiring) ^^ { - case ext ~ requiring => - Interface(ext, List(), List(), requiring.getOrElse(Set[RequiringType]())) + def externInterface: Parser[Interface] = interfaceHeader ~ opt(requires) ^^ { + case ext ~ requires => + Interface(ext, List(), List(), requires.getOrElse(Set[RequiresType]())) } def staticLabel: Parser[Boolean] = ("static ".r | "".r) ^^ { @@ -233,14 +233,14 @@ case class Parser(includePaths: List[String]) { } def ret: Parser[TypeRef] = ":" ~> typeRef - def requiring: Parser[Set[RequiringType]] = - "requiring" ~> parens(rep1sepend(ident, ",")) ^^ { + def requires: Parser[Set[RequiresType]] = + "requires" ~> parens(rep1sepend(ident, ",")) ^^ { _.map(ident => ident.name match { - case "eq" => Interface.RequiringType.Eq - case "ord" => Interface.RequiringType.Ord + case "eq" => Interface.RequiresType.Eq + case "ord" => Interface.RequiresType.Ord case _ => - return err(s"""Unrecognized requiring type "${ident.name}"""") + return err(s"""Unrecognized requires type "${ident.name}"""") } ).toSet } From 756e0f14b5de80feefeae6bb5c1f8d89cf5a2855 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Tue, 7 Jan 2025 21:20:35 -0500 Subject: [PATCH 03/20] Implemented basic equality for C++ & Java --- src/main/scala/djinni/CppGenerator.scala | 6 ++++++ src/main/scala/djinni/JNIGenerator.scala | 17 +++++++++++++++ src/main/scala/djinni/JavaGenerator.scala | 25 +++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index ced2b138..49a25ac8 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -15,6 +15,7 @@ package djinni +import djinni.ast.Interface.RequiresType import djinni.ast.Record.DerivingType import djinni.ast._ import djinni.generatorTools._ @@ -744,6 +745,11 @@ class CppGenerator(spec: Spec) extends Generator(spec) { .mkString("(", ", ", ")")}$constFlag = 0;") } } + // Requires + if (i.requiresTypes.contains(RequiresType.Eq)) { + w.wl + w.wl(s"virtual bool operator==(const ${self}& other) const = 0;") + } } } ) diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index 93d265b9..f434c82f 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -15,6 +15,7 @@ package djinni +import djinni.ast.Interface.RequiresType import djinni.ast._ import djinni.generatorTools._ import djinni.meta._ @@ -591,6 +592,22 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { } ) } + if (i.requiresTypes.contains(RequiresType.Eq)) { + val name = "native_operator_equals" + val methodNameMunged = name.replaceAllLiterally("_", "_1") + w.wl( + s"CJNIEXPORT jboolean JNICALL ${prefix}_00024CppProxy_$methodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_obj)" + ).braced { + w.w("try") + .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { + w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") + w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") + w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") + w.wl("auto r = *ref == *otherRef;") + w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") + } + } + } } } diff --git a/src/main/scala/djinni/JavaGenerator.scala b/src/main/scala/djinni/JavaGenerator.scala index 71557e7f..8635b7d4 100755 --- a/src/main/scala/djinni/JavaGenerator.scala +++ b/src/main/scala/djinni/JavaGenerator.scala @@ -15,6 +15,7 @@ package djinni +import djinni.ast.Interface.RequiresType import djinni.ast.Record.DerivingType import djinni.ast._ import djinni.generatorTools._ @@ -318,6 +319,30 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { s"private native $ret native_$meth(long _nativeRef${preComma(params)});" ) } + + if (i.requiresTypes.contains(RequiresType.Eq)) { + w.wl + w.wl("@Override") + val nullableAnnotation = + javaNullableAnnotation.map(_ + " ").getOrElse("") + w.w(s"public boolean equals(${nullableAnnotation}Object obj)") + .braced { + w.wl( + "assert !this.destroyed.get() : \"trying to use a destroyed object\";" + ) + w.wl + w.w(s"if (!(obj instanceof $javaClass))").braced { + w.wl("return false;") + } + w.wl + w.wl( + s"return native_operator_equals(this.nativeRef, ($javaClass)obj);" + ) + } + } + w.wl( + s"private native boolean native_operator_equals(long _nativeRef, $javaClass other);" + ) // Declare a native method for each of the interface's static methods. for (m <- i.methods if m.static) { From 5aeed7e5590ba61bbd67e756944049be6a5bffd7 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Tue, 7 Jan 2025 23:05:17 -0500 Subject: [PATCH 04/20] Added support for hashCode() --- src/main/scala/djinni/CppGenerator.scala | 2 ++ src/main/scala/djinni/JNIGenerator.scala | 23 +++++++++++++++++++---- src/main/scala/djinni/JavaGenerator.scala | 18 +++++++++++++++++- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index 49a25ac8..0c1590fa 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -749,6 +749,8 @@ class CppGenerator(spec: Spec) extends Generator(spec) { if (i.requiresTypes.contains(RequiresType.Eq)) { w.wl w.wl(s"virtual bool operator==(const ${self}& other) const = 0;") + w.wl + w.wl(s"virtual int32_t hashCode() const = 0;") } } } diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index f434c82f..b6f675e9 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -593,10 +593,10 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { ) } if (i.requiresTypes.contains(RequiresType.Eq)) { - val name = "native_operator_equals" - val methodNameMunged = name.replaceAllLiterally("_", "_1") + val equalsName = "native_operator_equals" + val equalsMethodNameMunged = equalsName.replaceAllLiterally("_", "_1") w.wl( - s"CJNIEXPORT jboolean JNICALL ${prefix}_00024CppProxy_$methodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_obj)" + s"CJNIEXPORT jboolean JNICALL ${prefix}_00024CppProxy_$equalsMethodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_obj)" ).braced { w.w("try") .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { @@ -604,9 +604,24 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") w.wl("auto r = *ref == *otherRef;") - w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") + w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") } } + + val hashCodeName = "native_hash_code" + val hashCodeMethodNameMunged = hashCodeName.replaceAllLiterally("_", "_1") + w.wl( + s"CJNIEXPORT jint JNICALL ${prefix}_00024CppProxy_$hashCodeMethodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)" + ).braced { + w.w("try") + .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { + w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") + w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") + w.wl("auto r = ref->hashCode();") + w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") + } + } + } } } diff --git a/src/main/scala/djinni/JavaGenerator.scala b/src/main/scala/djinni/JavaGenerator.scala index 8635b7d4..7e33589a 100755 --- a/src/main/scala/djinni/JavaGenerator.scala +++ b/src/main/scala/djinni/JavaGenerator.scala @@ -321,6 +321,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { } if (i.requiresTypes.contains(RequiresType.Eq)) { + // equals() override w.wl w.wl("@Override") val nullableAnnotation = @@ -339,9 +340,24 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { s"return native_operator_equals(this.nativeRef, ($javaClass)obj);" ) } + w.wl( + s"private native boolean native_operator_equals(long _nativeRef, $javaClass other);" + ) + + // hashCode() override + w.wl + w.wl("@Override") + w.w("public int hashCode()").braced { + w.wl( + "assert !this.destroyed.get() : \"trying to use a destroyed object\";" + ) + w.wl( + s"return native_hash_code(this.nativeRef);" + ) + } } w.wl( - s"private native boolean native_operator_equals(long _nativeRef, $javaClass other);" + s"private native int native_hash_code(long _nativeRef);" ) // Declare a native method for each of the interface's static methods. From 7e318859acb3aa60249153258e16eee36d97e854 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Tue, 7 Jan 2025 23:15:26 -0500 Subject: [PATCH 05/20] Accidentally changed 'Bool' to 'I32' -- reverting --- src/main/scala/djinni/JNIGenerator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index b6f675e9..383c666c 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -604,7 +604,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") w.wl("auto r = *ref == *otherRef;") - w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") + w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") } } From 01264899a9e7d39016c419aecd381d5930fb79cd Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Thu, 9 Jan 2025 19:06:48 -0500 Subject: [PATCH 06/20] Different approaching using methods equals() & hashCode() as static members of a class-in-a-class Operators. --- src/main/scala/djinni/CppGenerator.scala | 15 +++++++++++---- src/main/scala/djinni/JNIGenerator.scala | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index 0c1590fa..057a8590 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -746,11 +746,18 @@ class CppGenerator(spec: Spec) extends Generator(spec) { } } // Requires - if (i.requiresTypes.contains(RequiresType.Eq)) { - w.wl - w.wl(s"virtual bool operator==(const ${self}& other) const = 0;") + if (i.requiresTypes.contains(RequiresType.Eq) || i.requiresTypes + .contains(RequiresType.Ord) + ) { w.wl - w.wl(s"virtual int32_t hashCode() const = 0;") + w.w("class Operators").bracedSemi { + w.wlOutdent("public:") + if (i.requiresTypes.contains(RequiresType.Eq)) { + w.wl(s"static bool equals(const std::shared_ptr<${self}>& left, const std::shared_ptr<${self}>& right);") + w.wl + w.wl(s"static int32_t hashCode(const std::shared_ptr<${self}>& object);") + } + } } } } diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index 383c666c..9600ba2d 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -603,7 +603,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") - w.wl("auto r = *ref == *otherRef;") + w.wl(s"auto r = $cppSelf::Operators::equals(ref, otherRef);") w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") } } @@ -617,7 +617,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") - w.wl("auto r = ref->hashCode();") + w.wl(s"auto r = $cppSelf::Operators::hashCode(ref);") w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") } } From 118e85ac095fd94d3a9edd08c30c7839045fb3bd Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Fri, 10 Jan 2025 08:29:35 -0500 Subject: [PATCH 07/20] Switched from const std::shared_ptr<>& to const& for parameters to equals() & hashCode() --- src/main/scala/djinni/CppGenerator.scala | 4 ++-- src/main/scala/djinni/JNIGenerator.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index 057a8590..8c4e7956 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -753,9 +753,9 @@ class CppGenerator(spec: Spec) extends Generator(spec) { w.w("class Operators").bracedSemi { w.wlOutdent("public:") if (i.requiresTypes.contains(RequiresType.Eq)) { - w.wl(s"static bool equals(const std::shared_ptr<${self}>& left, const std::shared_ptr<${self}>& right);") + w.wl(s"static bool equals(const ${self}& left, const ${self}& right);") w.wl - w.wl(s"static int32_t hashCode(const std::shared_ptr<${self}>& object);") + w.wl(s"static int32_t hashCode(const ${self}& object);") } } } diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index 9600ba2d..7b438d71 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -603,7 +603,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") - w.wl(s"auto r = $cppSelf::Operators::equals(ref, otherRef);") + w.wl(s"auto r = $cppSelf::Operators::equals(*ref, *otherRef);") w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") } } @@ -617,7 +617,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") - w.wl(s"auto r = $cppSelf::Operators::hashCode(ref);") + w.wl(s"auto r = $cppSelf::Operators::hashCode(*ref);") w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") } } From ff551fb9f9bcc46c49081d5f61f33a847fa34521 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Sat, 11 Jan 2025 09:31:08 -0500 Subject: [PATCH 08/20] Moved prototype for native_hash_code() inside requires-if-statement block; should fix unit test failure. --- src/main/scala/djinni/JavaGenerator.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/djinni/JavaGenerator.scala b/src/main/scala/djinni/JavaGenerator.scala index 7e33589a..39f24447 100755 --- a/src/main/scala/djinni/JavaGenerator.scala +++ b/src/main/scala/djinni/JavaGenerator.scala @@ -355,10 +355,10 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { s"return native_hash_code(this.nativeRef);" ) } + w.wl( + s"private native int native_hash_code(long _nativeRef);" + ) } - w.wl( - s"private native int native_hash_code(long _nativeRef);" - ) // Declare a native method for each of the interface's static methods. for (m <- i.methods if m.static) { From f6bb7aa83f89e96ced26f059ce38d7e208ad6f68 Mon Sep 17 00:00:00 2001 From: Harald Date: Mon, 13 Jan 2025 07:56:52 +0100 Subject: [PATCH 09/20] New way of getting sbt (#172) December 2024 CI runners don't have sbt anymore Therefore, install it via actions, fix #171 --- .github/workflows/main.yaml | 50 +++++++++++++++++++--------------- .github/workflows/release.yaml | 14 ++++++---- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index e4576fe7..77e37962 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -10,15 +10,17 @@ on: jobs: build: runs-on: ubuntu-latest + env: + JAVA_OPTS: -Xms2048M -Xmx2048M -Xss6M -XX:ReservedCodeCacheSize=256M -Dfile.encoding=UTF-8 steps: - uses: actions/checkout@v4 - - name: Caching dependencies - uses: actions/cache@v3 + - name: Setup JDK + uses: actions/setup-java@v4 with: - path: | - ~/.sbt - ~/.ivy2 - key: scala-build-deps + distribution: temurin + java-version: 17 + cache: sbt + - uses: sbt/setup-sbt@v1 - name: Building run: sbt assembly - uses: actions/upload-artifact@v4 @@ -29,11 +31,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Caching dependencies - uses: actions/cache@v3 + - name: Setup JDK + uses: actions/setup-java@v4 with: - path: ~/.sbt - key: scala-fmt-deps + distribution: temurin + java-version: 17 + cache: sbt + - uses: sbt/setup-sbt@v1 - name: "Format check generator" run: sbt scalafmtCheck - name: "Format check integration test" @@ -43,13 +47,13 @@ jobs: needs: [build, formatCheck] steps: - uses: actions/checkout@v4 - - name: Caching dependencies - uses: actions/cache@v3 + - name: Setup JDK + uses: actions/setup-java@v4 with: - path: | - ~/.sbt - ~/.ivy2 - key: scala-build-deps + distribution: temurin + java-version: 17 + cache: sbt + - uses: sbt/setup-sbt@v1 - uses: actions/download-artifact@v4 with: name: djinni-generator @@ -61,15 +65,17 @@ jobs: buildWindows: runs-on: windows-latest + env: + JAVA_OPTS: -Xms2048M -Xmx2048M -Xss6M -XX:ReservedCodeCacheSize=256M -Dfile.encoding=UTF-8 steps: - uses: actions/checkout@v4 - - name: Caching dependencies - uses: actions/cache@v3 + - name: Setup JDK + uses: actions/setup-java@v4 with: - path: | - ~/.sbt - ~/.ivy2 - key: scala-build-deps-windows + distribution: temurin + java-version: 17 + cache: sbt + - uses: sbt/setup-sbt@v1 - name: Building run: sbt assembly - name: Testing diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index dff43650..99d33697 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,15 +8,17 @@ name: Upload Release Assets jobs: buildUnix: runs-on: ubuntu-latest + env: + JAVA_OPTS: -Xms2048M -Xmx2048M -Xss6M -XX:ReservedCodeCacheSize=256M -Dfile.encoding=UTF-8 steps: - uses: actions/checkout@v4 - - name: Caching dependencies - uses: actions/cache@v3 + - name: Setup JDK + uses: actions/setup-java@v4 with: - path: | - ~/.sbt - ~/.ivy2 - key: scala-build-deps + distribution: temurin + java-version: 17 + cache: sbt + - uses: sbt/setup-sbt@v1 - name: Building run: sbt assembly - uses: actions/upload-artifact@v4 From 5fcecbad4f24fbc14a8a664a8fc396905a20ad1a Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Tue, 7 Jan 2025 15:01:26 -0500 Subject: [PATCH 10/20] Added support for 'requiring' (will change to 'requires' eventually) --- src/main/scala/djinni/ast/ast.scala | 8 +++++++- src/main/scala/djinni/parser.scala | 25 ++++++++++++++++++++----- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/main/scala/djinni/ast/ast.scala b/src/main/scala/djinni/ast/ast.scala index f5ae3c3e..29db02bf 100644 --- a/src/main/scala/djinni/ast/ast.scala +++ b/src/main/scala/djinni/ast/ast.scala @@ -16,6 +16,7 @@ package djinni.ast import djinni.ast.Record.DerivingType.DerivingType +import djinni.ast.Interface.RequiringType.RequiringType import djinni.meta.MExpr import djinni.syntax.Loc @@ -110,9 +111,14 @@ object Record { case class Interface( ext: Ext, methods: Seq[Interface.Method], - consts: Seq[Const] + consts: Seq[Const], + requiringTypes: Set[RequiringType] ) extends TypeDef object Interface { + object RequiringType extends Enumeration { + type RequiringType = Value + val Eq, Ord = Value + } case class Method( ident: Ident, params: Seq[Field], diff --git a/src/main/scala/djinni/parser.scala b/src/main/scala/djinni/parser.scala index d7fc5449..c0512a0b 100644 --- a/src/main/scala/djinni/parser.scala +++ b/src/main/scala/djinni/parser.scala @@ -16,6 +16,7 @@ package djinni import djinni.ast.Interface.Method +import djinni.ast.Interface.RequiringType.RequiringType import djinni.ast.Record.DerivingType.DerivingType import djinni.ast._ import djinni.syntax._ @@ -189,11 +190,12 @@ case class Parser(includePaths: List[String]) { def interfaceHeader: Parser[Ext] = "interface" ~> extInterface def interface: Parser[Interface] = - interfaceHeader ~ bracesList(method | const) ^^ { - case ext ~ items => { + interfaceHeader ~ bracesList(method | const) ~ opt(requiring) ^^ { + case ext ~ items ~ requiring => { val methods = items collect { case m: Method => m } val consts = items collect { case c: Const => c } - Interface(ext, methods, consts) + val requiringTypes = requiring.getOrElse(Set[RequiringType]()) + Interface(ext, methods, consts, requiringTypes) } } @@ -209,8 +211,9 @@ case class Parser(includePaths: List[String]) { case ext ~ deriving => Record(ext, List(), List(), deriving.getOrElse(Set[DerivingType]())) } - def externInterface: Parser[Interface] = interfaceHeader ^^ { case ext => - Interface(ext, List(), List()) + def externInterface: Parser[Interface] = interfaceHeader ~ opt(requiring) ^^ { + case ext ~ requiring => + Interface(ext, List(), List(), requiring.getOrElse(Set[RequiringType]())) } def staticLabel: Parser[Boolean] = ("static ".r | "".r) ^^ { @@ -230,6 +233,18 @@ case class Parser(includePaths: List[String]) { } def ret: Parser[TypeRef] = ":" ~> typeRef + def requiring: Parser[Set[RequiringType]] = + "requiring" ~> parens(rep1sepend(ident, ",")) ^^ { + _.map(ident => + ident.name match { + case "eq" => Interface.RequiringType.Eq + case "ord" => Interface.RequiringType.Ord + case _ => + return err(s"""Unrecognized requiring type "${ident.name}"""") + } + ).toSet + } + def boolValue: Parser[Boolean] = "([Tt]rue)|([Ff]alse)".r ^^ { s: String => s.toBoolean } From a89c144cbc952f8ddb5353e341c7175978049a9a Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Tue, 7 Jan 2025 15:19:26 -0500 Subject: [PATCH 11/20] Renamed 'requiring' to 'requires' --- src/main/scala/djinni/ast/ast.scala | 8 ++++---- src/main/scala/djinni/parser.scala | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/scala/djinni/ast/ast.scala b/src/main/scala/djinni/ast/ast.scala index 29db02bf..d57637b9 100644 --- a/src/main/scala/djinni/ast/ast.scala +++ b/src/main/scala/djinni/ast/ast.scala @@ -16,7 +16,7 @@ package djinni.ast import djinni.ast.Record.DerivingType.DerivingType -import djinni.ast.Interface.RequiringType.RequiringType +import djinni.ast.Interface.RequiresType.RequiresType import djinni.meta.MExpr import djinni.syntax.Loc @@ -112,11 +112,11 @@ case class Interface( ext: Ext, methods: Seq[Interface.Method], consts: Seq[Const], - requiringTypes: Set[RequiringType] + requiresTypes: Set[RequiresType] ) extends TypeDef object Interface { - object RequiringType extends Enumeration { - type RequiringType = Value + object RequiresType extends Enumeration { + type RequiresType = Value val Eq, Ord = Value } case class Method( diff --git a/src/main/scala/djinni/parser.scala b/src/main/scala/djinni/parser.scala index c0512a0b..f5129534 100644 --- a/src/main/scala/djinni/parser.scala +++ b/src/main/scala/djinni/parser.scala @@ -16,7 +16,7 @@ package djinni import djinni.ast.Interface.Method -import djinni.ast.Interface.RequiringType.RequiringType +import djinni.ast.Interface.RequiresType.RequiresType import djinni.ast.Record.DerivingType.DerivingType import djinni.ast._ import djinni.syntax._ @@ -190,12 +190,12 @@ case class Parser(includePaths: List[String]) { def interfaceHeader: Parser[Ext] = "interface" ~> extInterface def interface: Parser[Interface] = - interfaceHeader ~ bracesList(method | const) ~ opt(requiring) ^^ { - case ext ~ items ~ requiring => { + interfaceHeader ~ bracesList(method | const) ~ opt(requires) ^^ { + case ext ~ items ~ requires => { val methods = items collect { case m: Method => m } val consts = items collect { case c: Const => c } - val requiringTypes = requiring.getOrElse(Set[RequiringType]()) - Interface(ext, methods, consts, requiringTypes) + val requiresTypes = requires.getOrElse(Set[RequiresType]()) + Interface(ext, methods, consts, requiresTypes) } } @@ -211,9 +211,9 @@ case class Parser(includePaths: List[String]) { case ext ~ deriving => Record(ext, List(), List(), deriving.getOrElse(Set[DerivingType]())) } - def externInterface: Parser[Interface] = interfaceHeader ~ opt(requiring) ^^ { - case ext ~ requiring => - Interface(ext, List(), List(), requiring.getOrElse(Set[RequiringType]())) + def externInterface: Parser[Interface] = interfaceHeader ~ opt(requires) ^^ { + case ext ~ requires => + Interface(ext, List(), List(), requires.getOrElse(Set[RequiresType]())) } def staticLabel: Parser[Boolean] = ("static ".r | "".r) ^^ { @@ -233,14 +233,14 @@ case class Parser(includePaths: List[String]) { } def ret: Parser[TypeRef] = ":" ~> typeRef - def requiring: Parser[Set[RequiringType]] = - "requiring" ~> parens(rep1sepend(ident, ",")) ^^ { + def requires: Parser[Set[RequiresType]] = + "requires" ~> parens(rep1sepend(ident, ",")) ^^ { _.map(ident => ident.name match { - case "eq" => Interface.RequiringType.Eq - case "ord" => Interface.RequiringType.Ord + case "eq" => Interface.RequiresType.Eq + case "ord" => Interface.RequiresType.Ord case _ => - return err(s"""Unrecognized requiring type "${ident.name}"""") + return err(s"""Unrecognized requires type "${ident.name}"""") } ).toSet } From 78809dd5e66ea76dc003dd8b62517294d9c4e0e6 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Tue, 7 Jan 2025 21:20:35 -0500 Subject: [PATCH 12/20] Implemented basic equality for C++ & Java --- src/main/scala/djinni/CppGenerator.scala | 6 ++++++ src/main/scala/djinni/JNIGenerator.scala | 17 +++++++++++++++ src/main/scala/djinni/JavaGenerator.scala | 25 +++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index ced2b138..49a25ac8 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -15,6 +15,7 @@ package djinni +import djinni.ast.Interface.RequiresType import djinni.ast.Record.DerivingType import djinni.ast._ import djinni.generatorTools._ @@ -744,6 +745,11 @@ class CppGenerator(spec: Spec) extends Generator(spec) { .mkString("(", ", ", ")")}$constFlag = 0;") } } + // Requires + if (i.requiresTypes.contains(RequiresType.Eq)) { + w.wl + w.wl(s"virtual bool operator==(const ${self}& other) const = 0;") + } } } ) diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index 93d265b9..f434c82f 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -15,6 +15,7 @@ package djinni +import djinni.ast.Interface.RequiresType import djinni.ast._ import djinni.generatorTools._ import djinni.meta._ @@ -591,6 +592,22 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { } ) } + if (i.requiresTypes.contains(RequiresType.Eq)) { + val name = "native_operator_equals" + val methodNameMunged = name.replaceAllLiterally("_", "_1") + w.wl( + s"CJNIEXPORT jboolean JNICALL ${prefix}_00024CppProxy_$methodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_obj)" + ).braced { + w.w("try") + .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { + w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") + w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") + w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") + w.wl("auto r = *ref == *otherRef;") + w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") + } + } + } } } diff --git a/src/main/scala/djinni/JavaGenerator.scala b/src/main/scala/djinni/JavaGenerator.scala index 71557e7f..8635b7d4 100755 --- a/src/main/scala/djinni/JavaGenerator.scala +++ b/src/main/scala/djinni/JavaGenerator.scala @@ -15,6 +15,7 @@ package djinni +import djinni.ast.Interface.RequiresType import djinni.ast.Record.DerivingType import djinni.ast._ import djinni.generatorTools._ @@ -318,6 +319,30 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { s"private native $ret native_$meth(long _nativeRef${preComma(params)});" ) } + + if (i.requiresTypes.contains(RequiresType.Eq)) { + w.wl + w.wl("@Override") + val nullableAnnotation = + javaNullableAnnotation.map(_ + " ").getOrElse("") + w.w(s"public boolean equals(${nullableAnnotation}Object obj)") + .braced { + w.wl( + "assert !this.destroyed.get() : \"trying to use a destroyed object\";" + ) + w.wl + w.w(s"if (!(obj instanceof $javaClass))").braced { + w.wl("return false;") + } + w.wl + w.wl( + s"return native_operator_equals(this.nativeRef, ($javaClass)obj);" + ) + } + } + w.wl( + s"private native boolean native_operator_equals(long _nativeRef, $javaClass other);" + ) // Declare a native method for each of the interface's static methods. for (m <- i.methods if m.static) { From a70bf9f8907a8353e85f462f932ee4496d18f710 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Tue, 7 Jan 2025 23:05:17 -0500 Subject: [PATCH 13/20] Added support for hashCode() --- src/main/scala/djinni/CppGenerator.scala | 2 ++ src/main/scala/djinni/JNIGenerator.scala | 23 +++++++++++++++++++---- src/main/scala/djinni/JavaGenerator.scala | 18 +++++++++++++++++- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index 49a25ac8..0c1590fa 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -749,6 +749,8 @@ class CppGenerator(spec: Spec) extends Generator(spec) { if (i.requiresTypes.contains(RequiresType.Eq)) { w.wl w.wl(s"virtual bool operator==(const ${self}& other) const = 0;") + w.wl + w.wl(s"virtual int32_t hashCode() const = 0;") } } } diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index f434c82f..b6f675e9 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -593,10 +593,10 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { ) } if (i.requiresTypes.contains(RequiresType.Eq)) { - val name = "native_operator_equals" - val methodNameMunged = name.replaceAllLiterally("_", "_1") + val equalsName = "native_operator_equals" + val equalsMethodNameMunged = equalsName.replaceAllLiterally("_", "_1") w.wl( - s"CJNIEXPORT jboolean JNICALL ${prefix}_00024CppProxy_$methodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_obj)" + s"CJNIEXPORT jboolean JNICALL ${prefix}_00024CppProxy_$equalsMethodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_obj)" ).braced { w.w("try") .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { @@ -604,9 +604,24 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") w.wl("auto r = *ref == *otherRef;") - w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") + w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") } } + + val hashCodeName = "native_hash_code" + val hashCodeMethodNameMunged = hashCodeName.replaceAllLiterally("_", "_1") + w.wl( + s"CJNIEXPORT jint JNICALL ${prefix}_00024CppProxy_$hashCodeMethodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)" + ).braced { + w.w("try") + .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { + w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") + w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") + w.wl("auto r = ref->hashCode();") + w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") + } + } + } } } diff --git a/src/main/scala/djinni/JavaGenerator.scala b/src/main/scala/djinni/JavaGenerator.scala index 8635b7d4..7e33589a 100755 --- a/src/main/scala/djinni/JavaGenerator.scala +++ b/src/main/scala/djinni/JavaGenerator.scala @@ -321,6 +321,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { } if (i.requiresTypes.contains(RequiresType.Eq)) { + // equals() override w.wl w.wl("@Override") val nullableAnnotation = @@ -339,9 +340,24 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { s"return native_operator_equals(this.nativeRef, ($javaClass)obj);" ) } + w.wl( + s"private native boolean native_operator_equals(long _nativeRef, $javaClass other);" + ) + + // hashCode() override + w.wl + w.wl("@Override") + w.w("public int hashCode()").braced { + w.wl( + "assert !this.destroyed.get() : \"trying to use a destroyed object\";" + ) + w.wl( + s"return native_hash_code(this.nativeRef);" + ) + } } w.wl( - s"private native boolean native_operator_equals(long _nativeRef, $javaClass other);" + s"private native int native_hash_code(long _nativeRef);" ) // Declare a native method for each of the interface's static methods. From 0fc7b2c8286fa15097a815f52c2ad016d2cce80a Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Tue, 7 Jan 2025 23:15:26 -0500 Subject: [PATCH 14/20] Accidentally changed 'Bool' to 'I32' -- reverting --- src/main/scala/djinni/JNIGenerator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index b6f675e9..383c666c 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -604,7 +604,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") w.wl("auto r = *ref == *otherRef;") - w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") + w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") } } From 5ed88ef44d2a610c93aa945b9c42d32562f5cc32 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Thu, 9 Jan 2025 19:06:48 -0500 Subject: [PATCH 15/20] Different approaching using methods equals() & hashCode() as static members of a class-in-a-class Operators. --- src/main/scala/djinni/CppGenerator.scala | 15 +++++++++++---- src/main/scala/djinni/JNIGenerator.scala | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index 0c1590fa..057a8590 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -746,11 +746,18 @@ class CppGenerator(spec: Spec) extends Generator(spec) { } } // Requires - if (i.requiresTypes.contains(RequiresType.Eq)) { - w.wl - w.wl(s"virtual bool operator==(const ${self}& other) const = 0;") + if (i.requiresTypes.contains(RequiresType.Eq) || i.requiresTypes + .contains(RequiresType.Ord) + ) { w.wl - w.wl(s"virtual int32_t hashCode() const = 0;") + w.w("class Operators").bracedSemi { + w.wlOutdent("public:") + if (i.requiresTypes.contains(RequiresType.Eq)) { + w.wl(s"static bool equals(const std::shared_ptr<${self}>& left, const std::shared_ptr<${self}>& right);") + w.wl + w.wl(s"static int32_t hashCode(const std::shared_ptr<${self}>& object);") + } + } } } } diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index 383c666c..9600ba2d 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -603,7 +603,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") - w.wl("auto r = *ref == *otherRef;") + w.wl(s"auto r = $cppSelf::Operators::equals(ref, otherRef);") w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") } } @@ -617,7 +617,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") - w.wl("auto r = ref->hashCode();") + w.wl(s"auto r = $cppSelf::Operators::hashCode(ref);") w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") } } From 13b48a91b61d209dea10c5293fefee537f4aee7f Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Fri, 10 Jan 2025 08:29:35 -0500 Subject: [PATCH 16/20] Switched from const std::shared_ptr<>& to const& for parameters to equals() & hashCode() --- src/main/scala/djinni/CppGenerator.scala | 4 ++-- src/main/scala/djinni/JNIGenerator.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index 057a8590..8c4e7956 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -753,9 +753,9 @@ class CppGenerator(spec: Spec) extends Generator(spec) { w.w("class Operators").bracedSemi { w.wlOutdent("public:") if (i.requiresTypes.contains(RequiresType.Eq)) { - w.wl(s"static bool equals(const std::shared_ptr<${self}>& left, const std::shared_ptr<${self}>& right);") + w.wl(s"static bool equals(const ${self}& left, const ${self}& right);") w.wl - w.wl(s"static int32_t hashCode(const std::shared_ptr<${self}>& object);") + w.wl(s"static int32_t hashCode(const ${self}& object);") } } } diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index 9600ba2d..7b438d71 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -603,7 +603,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") - w.wl(s"auto r = $cppSelf::Operators::equals(ref, otherRef);") + w.wl(s"auto r = $cppSelf::Operators::equals(*ref, *otherRef);") w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") } } @@ -617,7 +617,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") - w.wl(s"auto r = $cppSelf::Operators::hashCode(ref);") + w.wl(s"auto r = $cppSelf::Operators::hashCode(*ref);") w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") } } From c214cb8f91c64b85794e42af1853eb45d307d9e7 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Sat, 11 Jan 2025 09:31:08 -0500 Subject: [PATCH 17/20] Moved prototype for native_hash_code() inside requires-if-statement block; should fix unit test failure. --- src/main/scala/djinni/JavaGenerator.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/djinni/JavaGenerator.scala b/src/main/scala/djinni/JavaGenerator.scala index 7e33589a..39f24447 100755 --- a/src/main/scala/djinni/JavaGenerator.scala +++ b/src/main/scala/djinni/JavaGenerator.scala @@ -355,10 +355,10 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { s"return native_hash_code(this.nativeRef);" ) } + w.wl( + s"private native int native_hash_code(long _nativeRef);" + ) } - w.wl( - s"private native int native_hash_code(long _nativeRef);" - ) // Declare a native method for each of the interface's static methods. for (m <- i.methods if m.static) { From 675b900479d12c4faa294a95019fa27776e52d99 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Mon, 13 Jan 2025 14:40:54 -0500 Subject: [PATCH 18/20] Reorganized logic to support call from C++ to Java (needs JNI hookups) --- src/main/scala/djinni/CppGenerator.scala | 32 +++++++++++++++++------ src/main/scala/djinni/JNIGenerator.scala | 3 +++ src/main/scala/djinni/JavaGenerator.scala | 28 ++++++++++++++++++-- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index 8c4e7956..7585d835 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -746,16 +746,32 @@ class CppGenerator(spec: Spec) extends Generator(spec) { } } // Requires - if (i.requiresTypes.contains(RequiresType.Eq) || i.requiresTypes - .contains(RequiresType.Ord) - ) { - w.wl - w.w("class Operators").bracedSemi { - w.wlOutdent("public:") + if (!i.requiresTypes.isEmpty) { + if (i.ext.cpp) { + w.wl + w.w("class Operators").bracedSemi { + w.wlOutdent("public:") + if (i.requiresTypes.contains(RequiresType.Eq)) { + w.wl(s"static bool equals(const ${self}& left, const ${self}& right);") + w.wl + w.wl(s"static int32_t hashCode(const ${self}& object);") + } + } + } + + // TODO: how to apply formating rules to equals/compareTo/hashCode? + + if (i.ext.java) { if (i.requiresTypes.contains(RequiresType.Eq)) { - w.wl(s"static bool equals(const ${self}& left, const ${self}& right);") w.wl - w.wl(s"static int32_t hashCode(const ${self}& object);") + w.wl(s"virtual bool equals(const ${self}& other) const = 0;") + w.wl + w.wl(s"virtual int hashCode() const = 0;") + } + + if (i.requiresTypes.contains(RequiresType.Ord)) { + w.wl + w.wl(s"virtual int compareTo(const ${self}& other) const = 0;") } } } diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index 7b438d71..b6f10174 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -361,6 +361,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"friend $baseType;") w.wl if (i.ext.java) { + w.wl("// i.ext.java - writeJniPrototype()") w.wl( s"class JavaProxy final : ::djinni::JavaProxyHandle, public $cppSelf" ).bracedSemi { @@ -410,6 +411,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"$jniSelfWithParams::~$jniSelf() = default;") w.wl if (i.ext.java) { + w.wl("// i.ext.java - writeJniBody()") writeJniTypeParams(w, typeParams) w.wl( s"$jniSelfWithParams::JavaProxy::JavaProxy(JniType j) : Handle(::djinni::jniGetThreadEnv(), j) { }" @@ -484,6 +486,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { } } if (i.ext.cpp) { + w.wl("// i.ext.cpp - writeJniBody()") // Generate CEXPORT functions for JNI to call. val classIdentMunged = javaMarshal .fqTypename(ident, i) diff --git a/src/main/scala/djinni/JavaGenerator.scala b/src/main/scala/djinni/JavaGenerator.scala index 39f24447..3e8f5bb4 100755 --- a/src/main/scala/djinni/JavaGenerator.scala +++ b/src/main/scala/djinni/JavaGenerator.scala @@ -196,6 +196,12 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { writeDocAnnotations(w, doc) javaAnnotationHeader.foreach(w.wl) + + val interfaces = scala.collection.mutable.ArrayBuffer[String]() + if (i.requiresTypes.contains(RequiresType.Ord)) + interfaces += s"Comparable<$javaClass>" + val implementsSection = if (interfaces.isEmpty) "" + else " implements " + interfaces.mkString(", ") // Generate an interface or an abstract class depending on whether the use // of Java interfaces was requested. @@ -207,7 +213,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { val innerClassAccessibility = if (spec.javaGenerateInterfaces) "" else "private " w.w( - s"${javaClassAccessModifierString}$classPrefix $javaClass$typeParamList" + s"${javaClassAccessModifierString}$classPrefix $javaClass$typeParamList$implementsSection" ).braced { val skipFirst = SkipFirst() generateJavaConstants(w, i.consts, spec.javaGenerateInterfaces) @@ -262,6 +268,24 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { w.wl } } + + if (i.ext.java) { + if (i.requiresTypes.contains(RequiresType.Eq)) { + w.wl + w.wl("@Override") + if (i.ext.java) { + w.wl("public abstract boolean equals(@Nullable Object obj);") + } + } + + if (i.requiresTypes.contains(RequiresType.Ord)) { + w.wl + w.wl("@Override") + if (i.ext.java) { + w.wl(s"public abstract int compareTo($javaClass other);") + } + } + } if (i.ext.cpp) { w.wl @@ -306,7 +330,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { m.params.map(p => idJava.local(p.ident)).mkString(", ") val meth = idJava.method(m.ident) w.wl - w.wl(s"@Override") + w.wl("@Override") w.wl(s"public $ret $meth($params)$throwException").braced { w.wl( "assert !this.destroyed.get() : \"trying to use a destroyed object\";" From 11442eb3c65eb6ae4530f472f01273ada75c33b7 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Mon, 13 Jan 2025 21:44:23 -0500 Subject: [PATCH 19/20] Removed debugging lines (caused unit tests to fail) --- src/main/scala/djinni/JNIGenerator.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index b6f10174..7b438d71 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -361,7 +361,6 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"friend $baseType;") w.wl if (i.ext.java) { - w.wl("// i.ext.java - writeJniPrototype()") w.wl( s"class JavaProxy final : ::djinni::JavaProxyHandle, public $cppSelf" ).bracedSemi { @@ -411,7 +410,6 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { w.wl(s"$jniSelfWithParams::~$jniSelf() = default;") w.wl if (i.ext.java) { - w.wl("// i.ext.java - writeJniBody()") writeJniTypeParams(w, typeParams) w.wl( s"$jniSelfWithParams::JavaProxy::JavaProxy(JniType j) : Handle(::djinni::jniGetThreadEnv(), j) { }" @@ -486,7 +484,6 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { } } if (i.ext.cpp) { - w.wl("// i.ext.cpp - writeJniBody()") // Generate CEXPORT functions for JNI to call. val classIdentMunged = javaMarshal .fqTypename(ident, i) From 19acc8b1e3913ad3eff2d367c907fe1ebba79bd0 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Wed, 15 Jan 2025 09:41:29 -0500 Subject: [PATCH 20/20] Updated with scalafmtAll --- src/main/scala/djinni/CppGenerator.scala | 10 +++++--- src/main/scala/djinni/JNIGenerator.scala | 31 +++++++++++++++++------ src/main/scala/djinni/JavaGenerator.scala | 21 +++++++-------- src/main/scala/djinni/parser.scala | 10 ++++---- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index 7585d835..b9c3c324 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -752,15 +752,17 @@ class CppGenerator(spec: Spec) extends Generator(spec) { w.w("class Operators").bracedSemi { w.wlOutdent("public:") if (i.requiresTypes.contains(RequiresType.Eq)) { - w.wl(s"static bool equals(const ${self}& left, const ${self}& right);") + w.wl( + s"static bool equals(const ${self}& left, const ${self}& right);" + ) w.wl w.wl(s"static int32_t hashCode(const ${self}& object);") } } } - + // TODO: how to apply formating rules to equals/compareTo/hashCode? - + if (i.ext.java) { if (i.requiresTypes.contains(RequiresType.Eq)) { w.wl @@ -768,7 +770,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) { w.wl w.wl(s"virtual int hashCode() const = 0;") } - + if (i.requiresTypes.contains(RequiresType.Ord)) { w.wl w.wl(s"virtual int compareTo(const ${self}& other) const = 0;") diff --git a/src/main/scala/djinni/JNIGenerator.scala b/src/main/scala/djinni/JNIGenerator.scala index 7b438d71..7d78ea65 100644 --- a/src/main/scala/djinni/JNIGenerator.scala +++ b/src/main/scala/djinni/JNIGenerator.scala @@ -599,26 +599,41 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { s"CJNIEXPORT jboolean JNICALL ${prefix}_00024CppProxy_$equalsMethodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_obj)" ).braced { w.w("try") - .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { + .bracedEnd( + s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)" + ) { w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") - w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") - w.wl(s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);") + w.wl( + s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);" + ) + w.wl( + s"const auto& otherRef = ${withNs(Some(spec.jniNamespace), jniSelf)}::toCpp(jniEnv, j_obj);" + ) w.wl(s"auto r = $cppSelf::Operators::equals(*ref, *otherRef);") - w.wl("return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));") + w.wl( + "return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));" + ) } } val hashCodeName = "native_hash_code" - val hashCodeMethodNameMunged = hashCodeName.replaceAllLiterally("_", "_1") + val hashCodeMethodNameMunged = + hashCodeName.replaceAllLiterally("_", "_1") w.wl( s"CJNIEXPORT jint JNICALL ${prefix}_00024CppProxy_$hashCodeMethodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)" ).braced { w.w("try") - .bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)") { + .bracedEnd( + s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)" + ) { w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);") - w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);") + w.wl( + s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);" + ) w.wl(s"auto r = $cppSelf::Operators::hashCode(*ref);") - w.wl("return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));") + w.wl( + "return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));" + ) } } diff --git a/src/main/scala/djinni/JavaGenerator.scala b/src/main/scala/djinni/JavaGenerator.scala index 3e8f5bb4..468c016b 100755 --- a/src/main/scala/djinni/JavaGenerator.scala +++ b/src/main/scala/djinni/JavaGenerator.scala @@ -196,11 +196,12 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { writeDocAnnotations(w, doc) javaAnnotationHeader.foreach(w.wl) - + val interfaces = scala.collection.mutable.ArrayBuffer[String]() if (i.requiresTypes.contains(RequiresType.Ord)) interfaces += s"Comparable<$javaClass>" - val implementsSection = if (interfaces.isEmpty) "" + val implementsSection = + if (interfaces.isEmpty) "" else " implements " + interfaces.mkString(", ") // Generate an interface or an abstract class depending on whether the use @@ -268,7 +269,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { w.wl } } - + if (i.ext.java) { if (i.requiresTypes.contains(RequiresType.Eq)) { w.wl @@ -343,7 +344,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { s"private native $ret native_$meth(long _nativeRef${preComma(params)});" ) } - + if (i.requiresTypes.contains(RequiresType.Eq)) { // equals() override w.wl @@ -357,13 +358,13 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { ) w.wl w.w(s"if (!(obj instanceof $javaClass))").braced { - w.wl("return false;") + w.wl("return false;") + } + w.wl + w.wl( + s"return native_operator_equals(this.nativeRef, ($javaClass)obj);" + ) } - w.wl - w.wl( - s"return native_operator_equals(this.nativeRef, ($javaClass)obj);" - ) - } w.wl( s"private native boolean native_operator_equals(long _nativeRef, $javaClass other);" ) diff --git a/src/main/scala/djinni/parser.scala b/src/main/scala/djinni/parser.scala index f5129534..f1a2cac3 100644 --- a/src/main/scala/djinni/parser.scala +++ b/src/main/scala/djinni/parser.scala @@ -211,10 +211,10 @@ case class Parser(includePaths: List[String]) { case ext ~ deriving => Record(ext, List(), List(), deriving.getOrElse(Set[DerivingType]())) } - def externInterface: Parser[Interface] = interfaceHeader ~ opt(requires) ^^ { - case ext ~ requires => + def externInterface: Parser[Interface] = + interfaceHeader ~ opt(requires) ^^ { case ext ~ requires => Interface(ext, List(), List(), requires.getOrElse(Set[RequiresType]())) - } + } def staticLabel: Parser[Boolean] = ("static ".r | "".r) ^^ { case "static " => true @@ -237,8 +237,8 @@ case class Parser(includePaths: List[String]) { "requires" ~> parens(rep1sepend(ident, ",")) ^^ { _.map(ident => ident.name match { - case "eq" => Interface.RequiresType.Eq - case "ord" => Interface.RequiresType.Ord + case "eq" => Interface.RequiresType.Eq + case "ord" => Interface.RequiresType.Ord case _ => return err(s"""Unrecognized requires type "${ident.name}"""") }