Skip to content

Commit

Permalink
Merge pull request #105 from paulocoutinhox/java-interface-support
Browse files Browse the repository at this point in the history
java interface support
  • Loading branch information
li-feng-sc authored Sep 4, 2022
2 parents 60a4223 + 085b670 commit 2f5b0ae
Show file tree
Hide file tree
Showing 23 changed files with 467 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.DS_Store
.idea/
.vscode
# bazel
/bazel-*
.ijwb/
Expand All @@ -22,3 +23,4 @@ test-suite/djinni/vendor/third-party/proto/ts/*.js
.bsp
/project
/target
/.metals
14 changes: 11 additions & 3 deletions src/source/JavaGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* This file has been modified by Snap, Inc.
*/

Expand Down Expand Up @@ -174,8 +174,16 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
val typeParamList = javaTypeParams(typeParams)
writeDoc(w, doc)

val statics = i.methods.filter(m => m.static && m.lang.java)

javaAnnotationHeader.foreach(w.wl)
w.w(s"${javaClassAccessModifierString}abstract class $javaClass$typeParamList").braced {

// if no static and no cpp will use interface instead of abstract class
val genJavaInterface = spec.javaGenInterface && !statics.nonEmpty && !i.ext.cpp
val classOrInterfaceDesc = if (genJavaInterface) "interface" else "abstract class";
val methodPrefixDesc = if (genJavaInterface) "" else "public abstract ";

w.w(s"${javaClassAccessModifierString}${classOrInterfaceDesc} $javaClass$typeParamList").braced {
val skipFirst = SkipFirst()
generateJavaConstants(w, i.consts)

Expand All @@ -189,7 +197,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
nullityAnnotation + marshal.paramType(p.ty) + " " + idJava.local(p.ident)
})
marshal.nullityAnnotation(m.ret).foreach(w.wl)
w.wl("public abstract " + ret + " " + idJava.method(m.ident) + params.mkString("(", ", ", ")") + throwException + ";")
w.wl(methodPrefixDesc + ret + " " + idJava.method(m.ident) + params.mkString("(", ", ", ")") + throwException + ";")
}

val statics = i.methods.filter(m => m.static && m.lang.java)
Expand Down
4 changes: 4 additions & 0 deletions src/source/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ object Main {
var javaNonnullAnnotation: Option[String] = None
var javaImplementAndroidOsParcelable : Boolean = false
var javaUseFinalForRecord: Boolean = true
var javaGenInterface: Boolean = false
var jniOutFolder: Option[File] = None
var jniHeaderOutFolderOptional: Option[File] = None
var jniNamespace: String = "djinni_generated"
Expand Down Expand Up @@ -140,6 +141,8 @@ object Main {
.text("all generated java classes will implement the interface android.os.Parcelable")
opt[Boolean]("java-use-final-for-record").valueName("<use-final-for-record>").foreach(x => javaUseFinalForRecord = x)
.text("Whether generated Java classes for records should be marked 'final' (default: true). ")
opt[Boolean]("java-gen-interface").valueName("<true/false>").foreach(x => javaGenInterface = x)
.text("Generate Java interface instead of abstract class.")
note("")
opt[File]("cpp-out").valueName("<out-folder>").foreach(x => cppOutFolder = Some(x))
.text("The output folder for C++ files (Generator disabled if unspecified).")
Expand Down Expand Up @@ -373,6 +376,7 @@ object Main {
javaNonnullAnnotation,
javaImplementAndroidOsParcelable,
javaUseFinalForRecord,
javaGenInterface,
cppOutFolder,
cppHeaderOutFolder,
cppIncludePrefix,
Expand Down
1 change: 1 addition & 0 deletions src/source/generator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ package object generatorTools {
javaNonnullAnnotation: Option[String],
javaImplementAndroidOsParcelable: Boolean,
javaUseFinalForRecord: Boolean,
javaGenInterface: Boolean,
cppOutFolder: Option[File],
cppHeaderOutFolder: Option[File],
cppIncludePrefix: String,
Expand Down
7 changes: 7 additions & 0 deletions test-suite/djinni/interface_and_abstract_class.djinni
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
test_java_interface_only = interface +j {
test_method(): bool;
}

test_java_abstract_class_only = interface +c {
static test_method(): bool;
}
15 changes: 15 additions & 0 deletions test-suite/generated-src/cpp/test_java_abstract_class_only.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from interface_and_abstract_class.djinni

#pragma once

namespace testsuite {

class TestJavaAbstractClassOnly {
public:
virtual ~TestJavaAbstractClassOnly() = default;

static bool test_method();
};

} // namespace testsuite
15 changes: 15 additions & 0 deletions test-suite/generated-src/cpp/test_java_interface_only.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from interface_and_abstract_class.djinni

#pragma once

namespace testsuite {

class TestJavaInterfaceOnly {
public:
virtual ~TestJavaInterfaceOnly() = default;

virtual bool test_method() = 0;
};

} // namespace testsuite
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from interface_and_abstract_class.djinni

package com.dropbox.djinni.test;

import com.snapchat.djinni.NativeObjectManager;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

public abstract class TestJavaAbstractClassOnly {
public static native boolean testMethod();

public static final class CppProxy extends TestJavaAbstractClassOnly
{
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;
NativeObjectManager.register(this, nativeRef);
}
public static native void nativeDestroy(long nativeRef);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from interface_and_abstract_class.djinni

package com.dropbox.djinni.test;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

public interface TestJavaInterfaceOnly {
boolean testMethod();
}
29 changes: 29 additions & 0 deletions test-suite/generated-src/jni/NativeTestJavaAbstractClassOnly.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from interface_and_abstract_class.djinni

#include "NativeTestJavaAbstractClassOnly.hpp" // my header
#include "Marshal.hpp"

namespace djinni_generated {

NativeTestJavaAbstractClassOnly::NativeTestJavaAbstractClassOnly() : ::djinni::JniInterface<::testsuite::TestJavaAbstractClassOnly, NativeTestJavaAbstractClassOnly>("com/dropbox/djinni/test/TestJavaAbstractClassOnly$CppProxy") {}

NativeTestJavaAbstractClassOnly::~NativeTestJavaAbstractClassOnly() = default;


CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_TestJavaAbstractClassOnly_00024CppProxy_nativeDestroy(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
{
try {
delete reinterpret_cast<::djinni::CppProxyHandle<::testsuite::TestJavaAbstractClassOnly>*>(nativeRef);
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
}

CJNIEXPORT jboolean JNICALL Java_com_dropbox_djinni_test_TestJavaAbstractClassOnly_testMethod(JNIEnv* jniEnv, jobject /*this*/)
{
try {
auto r = ::testsuite::TestJavaAbstractClassOnly::test_method();
return ::djinni::release(::djinni::Bool::fromCpp(jniEnv, r));
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)
}

} // namespace djinni_generated
32 changes: 32 additions & 0 deletions test-suite/generated-src/jni/NativeTestJavaAbstractClassOnly.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from interface_and_abstract_class.djinni

#pragma once

#include "djinni_support.hpp"
#include "test_java_abstract_class_only.hpp"

namespace djinni_generated {

class NativeTestJavaAbstractClassOnly final : ::djinni::JniInterface<::testsuite::TestJavaAbstractClassOnly, NativeTestJavaAbstractClassOnly> {
public:
using CppType = std::shared_ptr<::testsuite::TestJavaAbstractClassOnly>;
using CppOptType = std::shared_ptr<::testsuite::TestJavaAbstractClassOnly>;
using JniType = jobject;

using Boxed = NativeTestJavaAbstractClassOnly;

~NativeTestJavaAbstractClassOnly();

static CppType toCpp(JNIEnv* jniEnv, JniType j) { return ::djinni::JniClass<NativeTestJavaAbstractClassOnly>::get()._fromJava(jniEnv, j); }
static ::djinni::LocalRef<JniType> fromCppOpt(JNIEnv* jniEnv, const CppOptType& c) { return {jniEnv, ::djinni::JniClass<NativeTestJavaAbstractClassOnly>::get()._toJava(jniEnv, c)}; }
static ::djinni::LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) { return fromCppOpt(jniEnv, c); }

private:
NativeTestJavaAbstractClassOnly();
friend ::djinni::JniClass<NativeTestJavaAbstractClassOnly>;
friend ::djinni::JniInterface<::testsuite::TestJavaAbstractClassOnly, NativeTestJavaAbstractClassOnly>;

};

} // namespace djinni_generated
26 changes: 26 additions & 0 deletions test-suite/generated-src/jni/NativeTestJavaInterfaceOnly.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from interface_and_abstract_class.djinni

#include "NativeTestJavaInterfaceOnly.hpp" // my header
#include "Marshal.hpp"

namespace djinni_generated {

NativeTestJavaInterfaceOnly::NativeTestJavaInterfaceOnly() : ::djinni::JniInterface<::testsuite::TestJavaInterfaceOnly, NativeTestJavaInterfaceOnly>() {}

NativeTestJavaInterfaceOnly::~NativeTestJavaInterfaceOnly() = default;

NativeTestJavaInterfaceOnly::JavaProxy::JavaProxy(JniType j) : Handle(::djinni::jniGetThreadEnv(), j) { }

NativeTestJavaInterfaceOnly::JavaProxy::~JavaProxy() = default;

bool NativeTestJavaInterfaceOnly::JavaProxy::test_method() {
auto jniEnv = ::djinni::jniGetThreadEnv();
::djinni::JniLocalScope jscope(jniEnv, 10);
const auto& data = ::djinni::JniClass<::djinni_generated::NativeTestJavaInterfaceOnly>::get();
auto jret = jniEnv->CallBooleanMethod(Handle::get().get(), data.method_testMethod);
::djinni::jniExceptionCheck(jniEnv);
return ::djinni::Bool::toCpp(jniEnv, jret);
}

} // namespace djinni_generated
46 changes: 46 additions & 0 deletions test-suite/generated-src/jni/NativeTestJavaInterfaceOnly.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from interface_and_abstract_class.djinni

#pragma once

#include "djinni_support.hpp"
#include "test_java_interface_only.hpp"

namespace djinni_generated {

class NativeTestJavaInterfaceOnly final : ::djinni::JniInterface<::testsuite::TestJavaInterfaceOnly, NativeTestJavaInterfaceOnly> {
public:
using CppType = std::shared_ptr<::testsuite::TestJavaInterfaceOnly>;
using CppOptType = std::shared_ptr<::testsuite::TestJavaInterfaceOnly>;
using JniType = jobject;

using Boxed = NativeTestJavaInterfaceOnly;

~NativeTestJavaInterfaceOnly();

static CppType toCpp(JNIEnv* jniEnv, JniType j) { return ::djinni::JniClass<NativeTestJavaInterfaceOnly>::get()._fromJava(jniEnv, j); }
static ::djinni::LocalRef<JniType> fromCppOpt(JNIEnv* jniEnv, const CppOptType& c) { return {jniEnv, ::djinni::JniClass<NativeTestJavaInterfaceOnly>::get()._toJava(jniEnv, c)}; }
static ::djinni::LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) { return fromCppOpt(jniEnv, c); }

private:
NativeTestJavaInterfaceOnly();
friend ::djinni::JniClass<NativeTestJavaInterfaceOnly>;
friend ::djinni::JniInterface<::testsuite::TestJavaInterfaceOnly, NativeTestJavaInterfaceOnly>;

class JavaProxy final : ::djinni::JavaProxyHandle<JavaProxy>, public ::testsuite::TestJavaInterfaceOnly
{
public:
JavaProxy(JniType j);
~JavaProxy();

bool test_method() override;

private:
friend ::djinni::JniInterface<::testsuite::TestJavaInterfaceOnly, ::djinni_generated::NativeTestJavaInterfaceOnly>;
};

const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/TestJavaInterfaceOnly") };
const jmethodID method_testMethod { ::djinni::jniGetMethodID(clazz.get(), "testMethod", "()Z") };
};

} // namespace djinni_generated
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from interface_and_abstract_class.djinni

#include "test_java_abstract_class_only.hpp"
#include <memory>

static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");

@class DBTestJavaAbstractClassOnly;

namespace djinni_generated {

class TestJavaAbstractClassOnly
{
public:
using CppType = std::shared_ptr<::testsuite::TestJavaAbstractClassOnly>;
using CppOptType = std::shared_ptr<::testsuite::TestJavaAbstractClassOnly>;
using ObjcType = DBTestJavaAbstractClassOnly*;

using Boxed = TestJavaAbstractClassOnly;

static CppType toCpp(ObjcType objc);
static ObjcType fromCppOpt(const CppOptType& cpp);
static ObjcType fromCpp(const CppType& cpp) { return fromCppOpt(cpp); }

private:
class ObjcProxy;
};

} // namespace djinni_generated

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from interface_and_abstract_class.djinni

#import "DBTestJavaAbstractClassOnly+Private.h"
#import "DBTestJavaAbstractClassOnly.h"
#import "DJICppWrapperCache+Private.h"
#import "DJIError.h"
#import "DJIMarshal+Private.h"
#include <exception>
#include <stdexcept>
#include <utility>

static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");

@interface DBTestJavaAbstractClassOnly ()

- (id)initWithCpp:(const std::shared_ptr<::testsuite::TestJavaAbstractClassOnly>&)cppRef;

@end

@implementation DBTestJavaAbstractClassOnly {
::djinni::CppProxyCache::Handle<std::shared_ptr<::testsuite::TestJavaAbstractClassOnly>> _cppRefHandle;
}

- (id)initWithCpp:(const std::shared_ptr<::testsuite::TestJavaAbstractClassOnly>&)cppRef
{
if (self = [super init]) {
_cppRefHandle.assign(cppRef);
}
return self;
}

+ (BOOL)testMethod {
try {
auto objcpp_result_ = ::testsuite::TestJavaAbstractClassOnly::test_method();
return ::djinni::Bool::fromCpp(objcpp_result_);
} DJINNI_TRANSLATE_EXCEPTIONS()
}

namespace djinni_generated {

auto TestJavaAbstractClassOnly::toCpp(ObjcType objc) -> CppType
{
if (!objc) {
return nullptr;
}
return objc->_cppRefHandle.get();
}

auto TestJavaAbstractClassOnly::fromCppOpt(const CppOptType& cpp) -> ObjcType
{
if (!cpp) {
return nil;
}
return ::djinni::get_cpp_proxy<DBTestJavaAbstractClassOnly>(cpp);
}

} // namespace djinni_generated

@end
Loading

0 comments on commit 2f5b0ae

Please sign in to comment.