diff --git a/Info.plist b/Info.plist
new file mode 100644
index 0000000..ff784fb
Binary files /dev/null and b/Info.plist differ
diff --git a/archive_jnf.sh b/archive_jnf.sh
new file mode 100644
index 0000000..ee187bb
--- /dev/null
+++ b/archive_jnf.sh
@@ -0,0 +1,12 @@
+#!/bin/sh -e
+
+distdir=$(pwd)/dist
+
+mkdir ${distdir}
+rm -f ${distdir}/JavaNativeFoundation.zip
+rm -f ${distdir}/JavaNativeFoundation.dmg
+
+(cd buildNative/Frameworks; zip --symlinks -r ${distdir}/JavaNativeFoundation.zip JavaNativeFoundation.framework)
+
+hdiutil create -srcfolder buildNative/Frameworks -volname JavaNativeFoundation ${distdir}/JavaNativeFoundation.dmg
+
diff --git a/build.gradle.kts b/build.gradle.kts
index f5f3900..c30d124 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -40,11 +40,9 @@ val buildJNF by tasks.registering(Exec::class) {
commandLine("sh", "build_jnf.sh")
}
-val archiveJNF by tasks.registering(Zip::class) {
+val archiveJNF by tasks.registering(Exec::class) {
dependsOn(buildJNF)
- archiveFileName.set("JavaNativeFoundation.framework.zip")
- destinationDirectory.set(project.buildDir.resolve("frameworks"))
- from("buildNative/Frameworks/JavaNativeFoundation.framework")
+ commandLine("sh", "archive_jnf.sh");
}
fun registerJNFConfiguration(architecture : String) = configurations.registering {
diff --git a/build_jnf.sh b/build_jnf.sh
index 1a4a4c6..9dd72c9 100644
--- a/build_jnf.sh
+++ b/build_jnf.sh
@@ -2,7 +2,7 @@
if [[ $(arch) == "arm64" ]] ; then
echo "Re-execing build using Rosetta." >&2
- exec arch -x86_64 ${0} "${@}"
+ exec arch -x86_64 /bin/sh ${0} "${@}"
fi
SDK_NAME=macosx
diff --git a/build_jnf2.sh b/build_jnf2.sh
new file mode 100644
index 0000000..175f36a
--- /dev/null
+++ b/build_jnf2.sh
@@ -0,0 +1,75 @@
+#!/bin/sh -e
+
+SDK_NAME=macosx
+
+CONCURRENCY=$(sysctl -n hw.activecpu)
+CODE_SIGN_IDENTITY=${AMFITRUSTED_IDENTITY:--}
+DEBUG_LEVEL=release
+
+# Exporting this such that /usr/bin/clang uses it implictly
+export SDKROOT=$(xcrun --sdk ${SDK_NAME} --show-sdk-path)
+MACOSX_DEPLOYMENT_TARGET=10.10
+export LD_DYLIB_INSTALL_NAME=@rpath/JavaNativeFoundation.framework/Versions/A/JavaNativeFoundation
+
+cc=/usr/bin/cc
+lipo=/usr/bin/lipo
+srcdir=$(pwd)/src/JavaNativeFoundation
+include1=${JAVA_HOME}/include
+include2=${JAVA_HOME}/include/darwin
+include3=${srcdir}/..
+
+topdir=$(pwd)
+libdir=$(pwd)/buildNative/lib
+fwdir=$(pwd)/buildNative/Frameworks
+
+build1() {
+ arch=$1
+ echo building for $arch
+ ${cc} -arch $arch -o ${libdir}/lib$arch.dylib -I${include1} -I${include2} -I${include3} -framework Cocoa \
+ -dynamiclib -ObjC -fvisibility=hidden -install_name ${LD_DYLIB_INSTALL_NAME} -g \
+ -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET} -Wl,-current_version,80 -Wl,-compatibility_version,1 \
+ ${srcdir}/*.m
+}
+
+do_jnf() {
+ rm -rf buildNative
+ mkdir -p buildNative/Frameworks
+ mkdir -p buildNative/lib
+ build1 x86_64
+ build1 arm64
+ ${lipo} ${libdir}/libx86_64.dylib ${libdir}/libarm64.dylib -create -output ${libdir}/JavaNativeFoundation
+ ${lipo} ${libdir}/libx86_64.dylib.dSYM/Contents/Resources/DWARF/libx86_64.dylib \
+ ${libdir}/libarm64.dylib.dSYM/Contents/Resources/DWARF/libarm64.dylib \
+ -create -output ${libdir}/JavaNativeFoundation-DSYM
+
+ cd ${fwdir}
+ mkdir JavaNativeFoundation.framework
+ cd JavaNativeFoundation.framework
+ mkdir -p Versions/A/Headers
+ mkdir -p Versions/A/Resources
+ mkdir -p Versions/A/Modules
+ cp -p ${libdir}/JavaNativeFoundation Versions/A
+ cp -p ${srcdir}/J*.h Versions/A/Headers
+ cp -p ${srcdir}/Modules/module.modulemap Versions/A/Modules
+ xcrun tapi stubify --filetype=tbd-v5 -o Versions/A/JavaNativeFoundation.tbd \
+ Versions/A/JavaNativeFoundation
+ cp -p ${topdir}/Info.plist Versions/A/Resources
+
+ (cd Versions; ln -s A Current)
+ ln -s Versions/Current/Headers .
+ ln -s Versions/Current/Resources .
+ ln -s Versions/Current/Modules .
+ ln -s Versions/Current/JavaNativeFoundation .
+ ln -s Versions/Current/JavaNativeFoundation.tbd .
+
+ cd ${fwdir}
+ mkdir -p JavaNativeFoundation.framework.dSYM/Contents/Resources/DWARF
+ cp -p ${libdir}/JavaNativeFoundation-DSYM JavaNativeFoundation.framework.dSYM/Contents/Resources/DWARF/JavaNativeFoundation
+ cp -p ${topdir}/dsym-Info.plist JavaNativeFoundation.framework.dSYM/Contents/Info.plist
+
+ codesign --sign ${CODE_SIGN_IDENTITY} --timestamp --force --verbose JavaNativeFoundation.framework/*.tbd
+ codesign --sign ${CODE_SIGN_IDENTITY} --timestamp --force --verbose JavaNativeFoundation.framework
+ codesign --sign ${CODE_SIGN_IDENTITY} --timestamp --force --verbose JavaNativeFoundation.framework.dSYM
+}
+
+do_jnf
diff --git a/dsym-Info.plist b/dsym-Info.plist
new file mode 100644
index 0000000..54e4ffa
--- /dev/null
+++ b/dsym-Info.plist
@@ -0,0 +1,20 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleIdentifier
+ com.apple.xcode.dsym.com.apple.JavaNativeFoundation
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ dSYM
+ CFBundleSignature
+ ????
+ CFBundleShortVersionString
+ 80
+ CFBundleVersion
+ 80
+
+
diff --git a/module.modulemap b/module.modulemap
new file mode 100644
index 0000000..bde3b31
--- /dev/null
+++ b/module.modulemap
@@ -0,0 +1,5 @@
+framework module JavaNativeFoundation [extern_c] {
+ umbrella header "JavaNativeFoundation.h"
+ export *
+ module * { export * }
+}
diff --git a/openjdk b/openjdk
deleted file mode 160000
index 356491b..0000000
--- a/openjdk
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 356491bda24e6c4781c6b650f4efda05a6bc1296
diff --git a/src/JavaNativeFoundation/JNFAssert.h b/src/JavaNativeFoundation/JNFAssert.h
new file mode 100644
index 0000000..9bfbfb2
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFAssert.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Assertions used by the JNF_COCOA_ENTER()/JNF_COCOA_EXIT() and class
+ * caching macros. When building debug builds, improper use of the caching
+ * macros will trigger warnings output to the console.
+ */
+
+#import
+
+#ifdef DEBUG
+#define JAVA_ASSERTIONS_ON
+#endif /* DEBUG */
+
+// Use the WARN macro to send a message to stderr in the
+// debug build. It gets removed from the optimized build
+// during preprocessing.
+#ifdef DEBUG
+#define JNF_WARN JNFDebugWarning
+#else
+#define JNF_WARN if (0) JNFDebugWarning
+#endif /* DEBUG */
+
+__BEGIN_DECLS
+
+JNF_EXPORT extern void JNFDebugWarning(const char *fmt, ...);
+
+JNF_EXPORT extern void JNFAssertionFailure(const char *file, int line, const char *condition, const char *msg);
+
+#ifdef JAVA_ASSERTIONS_ON
+
+#define JNF_ASSERT_FAILURE(condition, msg) \
+ JNFAssertionFailure(__FILE__, __LINE__, condition, msg) \
+
+
+#define JNF_ASSERT_MSG(condition, msg) \
+do { \
+ if (!(condition)) { \
+ JNF_ASSERT_FAILURE(#condition, msg); \
+ } \
+} while(0) \
+
+
+#define JNF_ASSERT_COND(condition) \
+ JNF_ASSERT_MSG(condition, NULL) \
+
+
+#define JNF_EXCEPTION_WARN(env, msg) \
+do { \
+ (*(env))->ExceptionDescribe(env); \
+ JNF_ASSERT_FAILURE("Java exception thrown", msg); \
+} while (0) \
+
+
+#define JNF_ASSERT_NO_EXCEPTION_MSG(env, msg) \
+if ((*(env))->ExceptionOccurred(env)) { \
+ JNF_EXCEPTION_WARN(env, msg); \
+} \
+
+
+#define JNF_ASSERT_NO_EXCEPTION(env) \
+ JNF_ASSERT_NO_EXCEPTION_MSG(env, NULL) \
+
+#else
+
+#define JNF_ASSERT_COND(condition)
+#define JNF_ASSERT_MSG(condition, msg)
+#define JNF_EXCEPTION_WARN(env, msg)
+#define JNF_ASSERT_NO_EXCEPTION(env)
+#define JNF_ASSERT_NO_EXCEPTION_MSG(env, msg)
+
+#endif /* JAVA_ASSERTIONS_ON */
+
+JNF_EXPORT extern void JNFDumpJavaStack(JNIEnv *env);
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFAssert.m b/src/JavaNativeFoundation/JNFAssert.m
new file mode 100644
index 0000000..fceb8f8
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFAssert.m
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFJNI.h"
+#import "JNFAssert.h"
+
+#import "debug.h"
+
+static void JNFDebugMessageV(const char *fmt, va_list args) {
+ // Prints a message and breaks into debugger.
+ fprintf(stderr, "JavaNativeFoundation: ");
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+}
+
+static void JNFDebugMessage(const char *fmt, ...) {
+ // Takes printf args and then calls DebugBreak
+ va_list args;
+ va_start(args, fmt);
+ JNFDebugMessageV(fmt, args);
+ va_end(args);
+}
+
+void JNFDebugWarning(const char *fmt, ...) {
+ // Takes printf args and then calls DebugBreak
+ va_list args;
+ va_start(args, fmt);
+ JNFDebugMessageV(fmt, args);
+ va_end(args);
+}
+
+void JNFAssertionFailure(const char *file, int line, const char *condition, const char *msg) {
+ JNFDebugMessage("Assertion failure: %s", condition);
+ if (msg) JNFDebugMessage(msg);
+ JNFDebugMessage("File %s; Line %d", file, line);
+}
+
+void JNFDumpJavaStack(JNIEnv *env) {
+ static JNF_CLASS_CACHE(jc_Thread, "java/lang/Thread");
+ static JNF_STATIC_MEMBER_CACHE(jsm_Thread_dumpStack, jc_Thread, "dumpStack", "()V");
+ JNFCallVoidMethod(env, jc_Thread.cls, jsm_Thread_dumpStack);
+}
diff --git a/src/JavaNativeFoundation/JNFAutoreleasePool.h b/src/JavaNativeFoundation/JNFAutoreleasePool.h
new file mode 100644
index 0000000..7d3c543
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFAutoreleasePool.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Utility class used by the JNF_COCOA_ENTER()/JNF_COCOA_EXIT() macros
+ * from JNFJNI.h. Do not use this class or releated functions directly.
+ */
+
+#import
+
+#import
+
+__BEGIN_DECLS
+
+typedef void JNFAutoreleasePoolToken;
+
+// JNFNativeMethodEnter - called on entry to each native method by the
+// JNF_COCOA_ENTER(env) macro in JNFJNI.h
+JNF_EXPORT extern JNFAutoreleasePoolToken *JNFNativeMethodEnter(void);
+
+// JNFNativeMethodExit - called on exit from each native method by the
+// JNF_COCOA_EXIT(env) macro in JNFJNI.h
+JNF_EXPORT extern void JNFNativeMethodExit(JNFAutoreleasePoolToken *token);
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFAutoreleasePool.m b/src/JavaNativeFoundation/JNFAutoreleasePool.m
new file mode 100644
index 0000000..50d513b
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFAutoreleasePool.m
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * The JNFAutoreleasePool manages setting up and tearing down autorelease
+ * pools for Java calls into the Cocoa frameworks.
+ *
+ * The external entry point into this machinery is JNFMethodEnter() and JNFMethodExit().
+ */
+
+#import "JNFAutoreleasePool.h"
+
+#import
+
+// These are vended by the Objective-C runtime, but they are unfortunately
+// not available as API in the macOS SDK. We are following suit with swift
+// and clang in declaring them inline here. They canot be removed or changed
+// in the OS without major bincompat ramifications.
+//
+// These were added in macOS 10.7.
+void * _Nonnull objc_autoreleasePoolPush(void);
+void objc_autoreleasePoolPop(void * _Nonnull context);
+
+#if TIMED
+static int64_t elapsedTime = 0;
+#endif
+
+#pragma mark -
+#pragma mark External API
+
+// JNFNativeMethodEnter - called on entry to each native method
+//
+// It sets up an autorelease pool, and will return a token if
+// JNFNativeMethodExit should be called. It attempts to consider
+// how much time has elapsed since the last autorelease pop.
+
+JNFAutoreleasePoolToken *JNFNativeMethodEnter() {
+#if TIMED
+ int64_t start = mach_absolute_time();
+#endif
+
+ JNFAutoreleasePoolToken * const tokenToReturn = objc_autoreleasePoolPush();
+
+#if TIMED
+ elapsedTime += (mach_absolute_time() - start);
+#endif
+
+ return tokenToReturn;
+}
+
+
+// JNFNativeMethodExit - called on exit from native methods
+//
+// This method is only called on exit from the first
+// native method to appear in the execution stack.
+// This function does not need to be called on exit
+// from the inner native methods (as an optimization).
+// JNFNativeMethodEnter sets the token to non-nil if
+// JNFNativeMethodExit needs to be called on exit.
+
+void JNFNativeMethodExit(JNFAutoreleasePoolToken *token) {
+
+#if TIMED
+ int64_t start = mach_absolute_time();
+#endif
+
+ objc_autoreleasePoolPop(token);
+
+#if TIMED
+ elapsedTime += (mach_absolute_time() - start);
+
+ NSLog(@"elapsedTime: %llu", elapsedTime);
+ // elapsedTime = 0;
+#endif
+}
diff --git a/src/JavaNativeFoundation/JNFDate.h b/src/JavaNativeFoundation/JNFDate.h
new file mode 100644
index 0000000..93f0e6d
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFDate.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Functions to convert between date container classes.
+ */
+
+#import
+
+#import
+
+__BEGIN_DECLS
+
+/*
+ * Converts java.util.Calendar and java.util.Date to an NSDate
+ * NOTE: Return value is auto-released.
+ */
+JNF_EXPORT extern NSDate *JNFJavaToNSDate(JNIEnv *env, jobject date);
+
+/*
+ * Converts an NSDate to a java.util.Calendar
+ * NOTE: This returns a JNI local ref. Any code that calls this should call DeleteLocalRef when done with the return value.
+ */
+JNF_EXPORT extern jobject JNFNSToJavaCalendar(JNIEnv *env, NSDate *date);
+
+/*
+ * Converts a millisecond time interval since the Java Jan 1, 1970 epoch into an
+ * NSTimeInterval since Mac OS X's Jan 1, 2001 epoch.
+ */
+JNF_EXPORT extern NSTimeInterval JNFJavaMillisToNSTimeInterval(jlong javaMillisSince1970);
+
+/*
+ * Converts an NSTimeInterval since the Mac OS X Jan 1, 2001 epoch into a
+ * Java millisecond time interval since Java's Jan 1, 1970 epoch.
+ */
+JNF_EXPORT extern jlong JNFNSTimeIntervalToJavaMillis(NSTimeInterval intervalSince2001);
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFDate.m b/src/JavaNativeFoundation/JNFDate.m
new file mode 100644
index 0000000..f3b4042
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFDate.m
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFDate.h"
+#import "JNFJNI.h"
+
+
+static JNF_CLASS_CACHE(sjc_Calendar, "java/util/Calendar");
+static JNF_CLASS_CACHE(sjc_Date, "java/util/Date");
+
+JNF_EXPORT extern NSTimeInterval JNFJavaMillisToNSTimeInterval(jlong javaMillisSince1970)
+{
+ return (NSTimeInterval)(((double)javaMillisSince1970 / 1000.0) - NSTimeIntervalSince1970);
+}
+
+JNF_EXPORT extern jlong JNFNSTimeIntervalToJavaMillis(NSTimeInterval intervalSince2001)
+{
+ return (jlong)((intervalSince2001 + NSTimeIntervalSince1970) * 1000.0);
+}
+
+JNF_EXPORT extern NSDate *JNFJavaToNSDate(JNIEnv *env, jobject date)
+{
+ if (date == NULL) return nil;
+
+ jlong millis = 0;
+ if (JNFIsInstanceOf(env, date, &sjc_Calendar)) {
+ static JNF_MEMBER_CACHE(jm_getTimeInMillis, sjc_Calendar, "getTimeInMillis", "()J");
+ millis = JNFCallLongMethod(env, date, jm_getTimeInMillis);
+ } else if (JNFIsInstanceOf(env, date, &sjc_Date)) {
+ static JNF_MEMBER_CACHE(jm_getTime, sjc_Date, "getTime", "()J");
+ millis = JNFCallLongMethod(env, date, jm_getTime);
+ }
+
+ if (millis == 0) {
+ return nil;
+ }
+
+ return [NSDate dateWithTimeIntervalSince1970:((double)millis / 1000.0)];
+}
+
+JNF_EXPORT extern jobject JNFNSToJavaCalendar(JNIEnv *env, NSDate *date)
+{
+ if (date == nil) return NULL;
+
+ const jlong millis = (jlong)([date timeIntervalSince1970] * 1000.0);
+
+ static JNF_STATIC_MEMBER_CACHE(jsm_getInstance, sjc_Calendar, "getInstance", "()Ljava/util/Calendar;");
+ jobject calendar = JNFCallStaticObjectMethod(env, jsm_getInstance);
+
+ static JNF_MEMBER_CACHE(jm_setTimeInMillis, sjc_Calendar, "setTimeInMillis", "(J)V");
+ JNFCallVoidMethod(env, calendar, jm_setTimeInMillis, millis);
+
+ return calendar;
+}
diff --git a/src/JavaNativeFoundation/JNFException.h b/src/JavaNativeFoundation/JNFException.h
new file mode 100644
index 0000000..c93be64
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFException.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * JNFExceptions handle bridging exceptions between Foundation and Java. NSExceptions are
+ * caught by the JNF_COCOA_ENTER()/JNF_COCOA_EXIT() macros, and transformed and thrown as
+ * Java exceptions at the JNI boundry. The macros in JNFJNI.h also check for Java exceptions
+ * and rethrow them as NSExceptions until they hit an @try/@catch block, or the
+ * JNF_COCOA_ENTER()/JNF_COCOA_EXIT() macros.
+ */
+
+#import
+
+#import
+
+__BEGIN_DECLS
+
+// Some exception class names.
+// These strings contain the full class name of each Java exception, so
+// they are handy to use when you need to throw an exception.
+JNF_EXPORT extern const char *kOutOfMemoryError;
+JNF_EXPORT extern const char *kClassNotFoundException;
+JNF_EXPORT extern const char *kNullPointerException;
+JNF_EXPORT extern const char *kIllegalAccessException;
+JNF_EXPORT extern const char *kIllegalArgumentException;
+JNF_EXPORT extern const char *kNoSuchFieldException;
+JNF_EXPORT extern const char *kNoSuchMethodException;
+JNF_EXPORT extern const char *kRuntimeException;
+
+// JNFException - a subclass of NSException that wraps a Java exception
+//
+// When a java exception is thrown out to a native method, use +raiseUnnamedException:
+// to turn it into an NSException. When returning out of a native method in
+// which an NSException has been raised, use the -raiseToJava: method to turn
+// it back into a Java exception and "throw" it, in the Java sense.
+
+JNF_EXPORT
+@interface JNFException : NSException
+
++ (void)raiseUnnamedException:(JNIEnv *)env;
++ (void)raise:(JNIEnv *)env throwable:(jthrowable)throwable;
++ (void)raise:(JNIEnv *)env as:(const char *)javaExceptionType reason:(const char *)reasonMsg;
+
+- init:(JNIEnv *)env throwable:(jthrowable)throwable;
+- init:(JNIEnv *)env as:(const char *)javaExceptionType reason:(const char *)reasonMsg;
+
++ (void)throwToJava:(JNIEnv *)env exception:(NSException *)exception;
++ (void)throwToJava:(JNIEnv *)env exception:(NSException *)exception as:(const char *)javaExceptionType;
+
+- (void)raiseToJava:(JNIEnv *)env;
+
+@end
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFException.m b/src/JavaNativeFoundation/JNFException.m
new file mode 100644
index 0000000..28a75dd
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFException.m
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFException.h"
+
+#import
+#import
+
+#import "JNFObject.h"
+#import "JNFString.h"
+#import "JNFAssert.h"
+#import "JNFThread.h"
+#import "debug.h"
+
+
+#define JAVA_LANG "java/lang/"
+const char* kOutOfMemoryError = JAVA_LANG "OutOfMemoryError";
+const char* kClassNotFoundException = JAVA_LANG "ClassNotFoundException";
+const char* kNullPointerException = JAVA_LANG "NullPointerException";
+const char* kIllegalAccessException = JAVA_LANG "IllegalAccessException";
+const char* kIllegalArgumentException = JAVA_LANG "IllegalArgumentException";
+const char* kNoSuchFieldException = JAVA_LANG "NoSuchFieldException";
+const char* kNoSuchMethodException = JAVA_LANG "NoSuchMethodException";
+const char* kRuntimeException = JAVA_LANG "RuntimeException";
+
+@interface JNFException ()
+@property (readwrite, nonatomic, assign) jthrowable javaException;
+@end
+
+@interface JNFException(_JNFPrivateExceptionLifecycle)
+
+- (void) _setThrowable:(jthrowable)throwable withEnv:(JNIEnv *)env;
+- (void) _clearThrowableWithEnv:(JNIEnv *)env;
+- (void) _setThrowable:(jthrowable)throwable withNonNullEnv:(JNIEnv *)env;
+- (void) _clearThrowableWithNonNullEnv:(JNIEnv *)env;
+
+@end
+
+
+@implementation JNFException
+
+- initUnnamed:(JNIEnv *)env {
+ JNF_ASSERT_COND(env);
+ jthrowable throwable = (*env)->ExceptionOccurred(env);
+ if (throwable) return [self init:env throwable:throwable];
+
+ return [self initWithName:@"JavaNativeException" reason:@"See Java exception" userInfo:nil];
+}
+
++ (void)raiseUnnamedException:(JNIEnv *)env {
+ JNF_ASSERT_COND(env);
+ [[[[JNFException alloc] initUnnamed:env] autorelease] raise];
+}
+
++ (void)raise:(JNIEnv *)env throwable:(jthrowable)throwable {
+ JNF_ASSERT_COND(env);
+ [[[[JNFException alloc] init:env throwable:throwable] autorelease] raise];
+}
+
++ (void)raise:(JNIEnv *)env as:(const char *)javaExceptionType reason:(const char *)reasonMsg {
+ JNF_ASSERT_COND(env);
+ [[[[JNFException alloc] init:env as:javaExceptionType reason:reasonMsg] autorelease] raise];
+}
+
+- init:(JNIEnv *)env throwable:(jthrowable)throwable {
+ [self _setThrowable:throwable withEnv:env];
+ (*env)->ExceptionClear(env); // The exception will be rethrown in -raiseToJava
+
+ static jclass jc_Throwable = NULL;
+ if (jc_Throwable == NULL) {
+ jc_Throwable = (*env)->FindClass(env, "java/lang/Throwable");
+ jthrowable unexpected = (*env)->ExceptionOccurred(env);
+ if (unexpected) {
+ (*env)->ExceptionClear(env);
+ return [self initWithName:@"JavaNativeException" reason:@"Internal JNF Error: could not find Throwable class" userInfo:nil];
+ }
+ }
+
+ static jmethodID jm_Throwable_getMessage = NULL;
+ if (jm_Throwable_getMessage == NULL && jc_Throwable != NULL) {
+ jm_Throwable_getMessage = (*env)->GetMethodID(env, jc_Throwable, "toString", "()Ljava/lang/String;");
+ jthrowable unexpected = (*env)->ExceptionOccurred(env);
+ if (unexpected) {
+ (*env)->ExceptionClear(env);
+ return [self initWithName:@"JavaNativeException" reason:@"Internal JNF Error: could not find Throwable.toString() method" userInfo:nil];
+ }
+ }
+
+ if (jm_Throwable_getMessage == NULL) {
+ return [self initWithName:@"JavaNativeException" reason:@"Internal JNF Error: exception occurred, unable to determine cause" userInfo:nil];
+ }
+
+ jobject msg = (*env)->CallObjectMethod(env, throwable, jm_Throwable_getMessage);
+ jthrowable unexpected = (*env)->ExceptionOccurred(env);
+ if (unexpected) {
+ (*env)->ExceptionClear(env);
+ return [self initWithName:@"JavaNativeException" reason:@"Internal JNF Error: failed calling Throwable.toString()" userInfo:nil];
+ }
+
+ NSString *reason = JNFJavaToNSString(env, msg);
+ (*env)->DeleteLocalRef(env, msg);
+ return [self initWithName:@"JavaNativeException" reason:reason userInfo:nil];
+}
+
+- init:(JNIEnv *)env as:(const char *)javaExceptionType reason:(const char *)reasonMsg {
+ jclass exceptionClass = NULL;
+ char *buf = NULL;
+
+ JNF_ASSERT_COND(env);
+ if (javaExceptionType != NULL) {
+ exceptionClass = (*env)->FindClass(env, javaExceptionType);
+ }
+
+ if (exceptionClass == NULL) {
+ // Try to throw an AWTError exception
+ static jthrowable panicExceptionClass = NULL;
+ const char* panicExceptionName = kRuntimeException;
+ if (panicExceptionClass == NULL) {
+ jclass cls = (*env)->FindClass(env, panicExceptionName);
+ if (cls != NULL) {
+ panicExceptionClass = (*env)->NewGlobalRef(env, cls);
+ }
+ }
+
+ exceptionClass = panicExceptionClass;
+ if (javaExceptionType == NULL) {
+ reasonMsg = "Missing Java exception class name while trying to throw a new Java exception";
+ } else {
+ // Quick and dirty thread-safe message buffer.
+ buf = calloc(1, 512);
+ if (buf != NULL) {
+ sprintf(buf, "Unknown throwable class: %s.80", javaExceptionType);
+ reasonMsg = buf;
+ } else {
+ reasonMsg = "Unknown throwable class, out of memory!";
+ }
+ }
+ javaExceptionType = panicExceptionName;
+ }
+
+ // Can't throw squat if there's no class to throw
+ if (exceptionClass != NULL) {
+ (*env)->ThrowNew(env, exceptionClass, reasonMsg);
+ jthrowable ex = (*env)->ExceptionOccurred(env);
+ if (ex) {
+ (*env)->ExceptionClear(env); // Exception will be rethrown in -raiseToJava
+ }
+ [self _setThrowable:ex withEnv:env];
+ }
+
+ if (reasonMsg == NULL) reasonMsg = "unknown";
+
+ @try {
+ return [self initWithName:[NSString stringWithUTF8String:javaExceptionType]
+ reason:[NSString stringWithUTF8String:reasonMsg]
+ userInfo:nil];
+ } @finally {
+ if (buf != NULL) free(buf);
+ }
+
+ return self;
+}
+
++ (void)throwToJava:(JNIEnv *)env exception:(NSException *)exception {
+ [self throwToJava:env exception:exception as:kRuntimeException];
+}
+
++ (void)throwToJava:(JNIEnv *)env exception:(NSException *)exception as:(const char *)javaExceptionType{
+ if (![exception isKindOfClass:[JNFException class]]) {
+ exception = [[JNFException alloc] init:env as:javaExceptionType reason:[[NSString stringWithFormat:@"Non-Java exception raised, not handled! (Original problem: %@)", [exception reason]] UTF8String]];
+ [exception autorelease];
+ JNF_WARN("NSException not handled by native method. Passing to Java.");
+ }
+
+ [(JNFException *)exception raiseToJava:env];
+}
+
+- (void)raiseToJava:(JNIEnv *)env {
+ jthrowable const javaException = self.javaException;
+
+ JNF_ASSERT_COND(env);
+ JNF_ASSERT_COND(javaException != NULL);
+ (*env)->Throw(env, javaException);
+}
+
+- (NSString *)description {
+ jthrowable const javaException = self.javaException;
+ NSString *desc = [super description];
+ if (!javaException) return desc;
+
+ @try {
+ JNFThreadContext ctx = JNFThreadDetachImmediately;
+ JNIEnv *env = JNFObtainEnv(&ctx);
+ if (!env) {
+ NSLog(@"JavaNativeFoundation: NULL JNIEnv error occurred obtaining Java exception description");
+ return desc;
+ }
+ (*env)->ExceptionClear(env);
+ NSString *stackTrace = JNFGetStackTraceAsNSString(env, javaException);
+ JNFReleaseEnv(env, &ctx);
+
+ return stackTrace;
+ } @catch (NSException *e) {
+ // we clearly blew up trying to print our own exception, so we should
+ // not try to do that again, even if it looks helpful - its a trap!
+ NSLog(@"JavaNativeFoundation error occurred obtaining Java exception description");
+ }
+ return desc;
+}
+
+- (void) _setThrowable:(jthrowable)throwable withEnv:(JNIEnv *)env {
+ if (env) {
+ [self _clearThrowableWithNonNullEnv:env];
+ [self _setThrowable:throwable withNonNullEnv:env];
+ return;
+ }
+
+ JNFThreadContext threadContext = JNFThreadDetachImmediately;
+ env = JNFObtainEnv(&threadContext);
+ if (env == NULL) return;
+
+ [self _clearThrowableWithNonNullEnv:env];
+ [self _setThrowable:throwable withEnv:env];
+
+ JNFReleaseEnv(env, &threadContext);
+}
+
+- (void) _clearThrowableWithEnv:(JNIEnv *)env {
+ if (env) {
+ [self _clearThrowableWithNonNullEnv:env];
+ return;
+ }
+
+ JNFThreadContext threadContext = JNFThreadDetachImmediately;
+ env = JNFObtainEnv(&threadContext);
+ if (env == NULL) return; // leak?
+
+ [self _clearThrowableWithNonNullEnv:env];
+
+ JNFReleaseEnv(env, &threadContext);
+}
+
+- (void) _setThrowable:(jthrowable)throwable withNonNullEnv:(JNIEnv *)env {
+ if (!throwable) return;
+ self.javaException = (*env)->NewGlobalRef(env, throwable);
+}
+
+// delete and clear
+- (void) _clearThrowableWithNonNullEnv:(JNIEnv *)env {
+ jthrowable const javaException = self.javaException;
+ if (!javaException) return;
+ self.javaException = NULL;
+ (*env)->DeleteGlobalRef(env, javaException);
+}
+
+- (void) dealloc {
+ [self _clearThrowableWithEnv:NULL];
+ [super dealloc];
+}
+
+@end
diff --git a/src/JavaNativeFoundation/JNFJNI.h b/src/JavaNativeFoundation/JNFJNI.h
new file mode 100644
index 0000000..a5b34fb
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFJNI.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * The basic building blocks of writing Java JNI code that interacts with Objective-C.
+ *
+ * All JNI functions should call JNF_COCOA_ENTER()/JNF_COCOA_EXIT() to properly
+ * catch thrown NSExceptions and periodically flush the autorelease pool for the
+ * current thread. JNF_COCOA_DURING()/JNF_COCOA_HANDLE() should only be used when
+ * AppKit is known to not be initialized yet.
+ *
+ * JNF_CLASS_CACHE()/JNF_MEMBER_CACHE()/JNF_STATIC_MEMBER_CACHE()/JNF_CTOR_CACHE()
+ * all cache references to Java classes, methods, and variables for use by the
+ * GET/SET/CALL functions. These functions check for Java exceptions, immediately
+ * re-throwing them as JNFExceptions, and are simpler than their pure JNI equivalents.
+ */
+
+#import
+#import
+#import
+
+#define JNF_EXPORT __attribute__ ((visibility ("default"))) API_UNAVAILABLE(ios)
+
+#import
+#import
+
+__BEGIN_DECLS
+
+// from jlong.h
+// All pointers in and out of JNI functions should be expressed as jlongs
+// to accomodate for both 32-bit and 64-bit pointer sizes
+#ifndef jlong_to_ptr
+#define jlong_to_ptr(a) ((void *)(uintptr_t)(a))
+#endif
+
+#ifndef ptr_to_jlong
+#define ptr_to_jlong(a) ((jlong)(uintptr_t)(a))
+#endif
+
+// JNF_COCOA_DURING - Outermost exception scope for a JNI native method
+//
+// Use this macro only if you don't want any autorelease pool set or
+// other JNFThreadContext setup (ie, if the AppKit isn't running
+// yet). Usually, you want to use JNF_COCOA_ENTER & JNF_COCOA_EXIT
+#define JNF_COCOA_DURING(env) \
+@try {
+
+
+// JNF_COCOA_HANDLE - Close of JNF_COCOA_DURING
+//
+// Use this macro to match an JNF_COCOA_DURING
+// This macro ensures that no NSException is thrown into
+// the VM. It turns NSExceptions into Java exceptions.
+#define JNF_COCOA_HANDLE(env) \
+} @catch(NSException *localException) { \
+ [JNFException throwToJava:env exception:localException]; \
+} \
+
+
+// JNF_COCOA_ENTER - Place at the beginning of every JNI method
+//
+// Sets up an exception handler and an autorelease pool if one is
+// not already setup.
+//
+// Note: if the native method executes before AppKit is
+// initialized, use JNF_COCOA_DURING.
+#define JNF_COCOA_ENTER(env) \
+{ \
+ JNFAutoreleasePoolToken* _token = JNFNativeMethodEnter(); \
+ JNF_COCOA_DURING(env)
+
+
+// JNF_COCOA_EXIT - Place at the end of every JNI method
+//
+// Catches NSExceptions and re-throws them as Java exceptions.
+// Use this macro to match JNF_COCOA_ENTER.
+#define JNF_COCOA_EXIT(env) \
+ JNF_COCOA_HANDLE(env) \
+ @finally { \
+ if (_token) JNFNativeMethodExit(_token); \
+ } \
+}
+
+// JNF_CHECK_AND_RETHROW_EXCEPTION - rethrows exceptions from Java
+//
+// Takes an exception thrown from Java, and transforms it into an
+// NSException. The NSException should bubble up to the upper-most
+// JNF_COCOA_ENTER/JNF_COCOA_EXIT pair, and then be re-thrown as
+// a Java exception when returning from JNI. This check should be
+// done after raw JNI operations which could cause a Java exception
+// to be be thrown. The JNF{Get/Set/Call} macros below do this
+// check automatically.
+#define JNF_CHECK_AND_RETHROW_EXCEPTION(env) \
+{ \
+ jthrowable _exception = (*env)->ExceptionOccurred(env); \
+ if (_exception) [JNFException raise:env throwable:_exception]; \
+}
+
+
+// Use JNF_CLASS_CACHE, JNF_MEMBER_CACHE, JNF_STATIC_MEMBER_CACHE
+// and JNF_CTOR_CACHE as convenient ways to create
+// JNFClassInfo and JNFMemberInfo records that can
+// be passed to the utility functions that follow.
+
+// JNF_CLASS_CACHE - Create a JNFClassInfo struct
+//
+// Use this macro to define a JNFClassInfo struct.
+// For example:
+// JNF_CLASS_CACHE(jc_java_awt_Font, "java/awt/Font");
+// defines the symbol jc_java_awt_Font to point to the
+// appropriately initialized JNFClassInfo struct.
+// The "jc_" prefix is short for "java class."
+#define JNF_CLASS_CACHE(cache_symbol, name) \
+ JNFClassInfo cache_symbol = {name, NULL}
+
+// JNF_MEMBER_CACHE - Create a JNFMemberInfo struct
+//
+// This macro creates and initializes a JNFMemberInfo
+// struct, and defines a pointer to it. Example:
+// JNF_MEMBER_CACHE(jm_Font_isBold, jc_java_awt_Font, "isBold", "Z");
+// This defines the symbol jm_Font_isBold to point to a
+// JNFMemberInfo struct that represents the isBold method
+// of the class java.awt.Font. Use this macro for both
+// fields and methods.
+#define JNF_MEMBER_CACHE(cache_symbol, class_cache_symbol, name, sig) \
+ JNFMemberInfo _ ## cache_symbol = {name, sig, NO, &class_cache_symbol, {NULL}}, *cache_symbol=&_ ## cache_symbol
+
+// JNF_STATIC_MEMBER_CACHE - Create a JNFMemberInfo struct for static members
+//
+// Same as JNF_MEMBER_CACHE, but used for static fields and mehods.
+#define JNF_STATIC_MEMBER_CACHE(cache_symbol, class_cache_symbol, name, sig) \
+ JNFMemberInfo _ ## cache_symbol = {name, sig, YES, &class_cache_symbol, {NULL}}, *cache_symbol=&_ ## cache_symbol
+
+// JNF_CTOR_CACHE - Create a JNFMemberInfo struct for a constructor
+//
+// Same as JNF_MEMBER_CACHE, but for constructors
+#define JNF_CTOR_CACHE(cache_symbol, class_cache_symbol, sig) \
+ JNFMemberInfo _ ## cache_symbol = {"", sig, NO, &class_cache_symbol, {NULL}}, *cache_symbol=&_ ## cache_symbol
+
+
+// JNFClassInfo - struct for caching a java class reference
+//
+// Create one of these by using the JNF_CLASS_CACHE macro (below).
+// The class ref is resolved lazily.
+typedef struct _JNFClassInfo {
+ const char *name; // fully/qualified/ClassName
+ jclass cls; // The JNI global class reference.
+} JNFClassInfo;
+
+// JNFMemberInfo - struct for caching a field or method ID
+//
+// Create these by using the JNF_MEMBER_CACHE macro (below).
+// The member ID is resolved lazily.
+typedef struct _JNFMemberInfo {
+ const char *name; // The name of the member
+ const char *sig; // The signature of the member
+ BOOL isStatic; // Is this member declared static?
+ JNFClassInfo *classInfo; // points to the JNFClassInfo struct of
+ // which this field/method is a member.
+ union _j {
+ jfieldID fieldID; // If field, the JNI field ID
+ jmethodID methodID; // If method, the JNI method ID
+ } j;
+} JNFMemberInfo;
+
+
+/*
+ * JNI Utility Functions
+ *
+ * These functions make use of class and method ID caching, so they
+ * are more efficient than simply calling their JNI equivalents directly.
+ * They also detect Java exceptions and throw a corresponding
+ * NSException when JNI returns with a Java exception.
+ * Therefore, you should be prepared to handle exceptions
+ * before they propagate either back to the VM or up
+ * to the run loop.
+ */
+
+// JNFIsInstanceOf - returns whether obj is an instance of clazz
+JNF_EXPORT extern BOOL JNFIsInstanceOf(JNIEnv *env, jobject obj, JNFClassInfo *clazz);
+
+// Creating instances
+JNF_EXPORT extern jobject JNFNewObject(JNIEnv *env, JNFMemberInfo *constructor, ...);
+
+// Creating arrays
+JNF_EXPORT extern jobjectArray JNFNewObjectArray (JNIEnv *env, JNFClassInfo *clazz, jsize length);
+JNF_EXPORT extern jbooleanArray JNFNewBooleanArray (JNIEnv *env, jsize length);
+JNF_EXPORT extern jbyteArray JNFNewByteArray (JNIEnv *env, jsize length);
+JNF_EXPORT extern jcharArray JNFNewCharArray (JNIEnv *env, jsize length);
+JNF_EXPORT extern jshortArray JNFNewShortArray (JNIEnv *env, jsize length);
+JNF_EXPORT extern jintArray JNFNewIntArray (JNIEnv *env, jsize length);
+JNF_EXPORT extern jlongArray JNFNewLongArray (JNIEnv *env, jsize length);
+JNF_EXPORT extern jfloatArray JNFNewFloatArray (JNIEnv *env, jsize length);
+JNF_EXPORT extern jdoubleArray JNFNewDoubleArray (JNIEnv *env, jsize length);
+
+// Non-static getters
+JNF_EXPORT extern jobject JNFGetObjectField (JNIEnv *env, jobject obj, JNFMemberInfo *field);
+JNF_EXPORT extern jboolean JNFGetBooleanField(JNIEnv *env, jobject obj, JNFMemberInfo *field);
+JNF_EXPORT extern jbyte JNFGetByteField (JNIEnv *env, jobject obj, JNFMemberInfo *field);
+JNF_EXPORT extern jchar JNFGetCharField (JNIEnv *env, jobject obj, JNFMemberInfo *field);
+JNF_EXPORT extern jshort JNFGetShortField (JNIEnv *env, jobject obj, JNFMemberInfo *field);
+JNF_EXPORT extern jint JNFGetIntField (JNIEnv *env, jobject obj, JNFMemberInfo *field);
+JNF_EXPORT extern jlong JNFGetLongField (JNIEnv *env, jobject obj, JNFMemberInfo *field);
+JNF_EXPORT extern jfloat JNFGetFloatField (JNIEnv *env, jobject obj, JNFMemberInfo *field);
+JNF_EXPORT extern jdouble JNFGetDoubleField (JNIEnv *env, jobject obj, JNFMemberInfo *field);
+
+// Static getters
+JNF_EXPORT extern jobject JNFGetStaticObjectField (JNIEnv *env, JNFMemberInfo *field);
+JNF_EXPORT extern jboolean JNFGetStaticBooleanField(JNIEnv *env, JNFMemberInfo *field);
+JNF_EXPORT extern jbyte JNFGetStaticByteField (JNIEnv *env, JNFMemberInfo *field);
+JNF_EXPORT extern jchar JNFGetStaticCharField (JNIEnv *env, JNFMemberInfo *field);
+JNF_EXPORT extern jshort JNFGetStaticShortField (JNIEnv *env, JNFMemberInfo *field);
+JNF_EXPORT extern jint JNFGetStaticIntField (JNIEnv *env, JNFMemberInfo *field);
+JNF_EXPORT extern jlong JNFGetStaticLongField (JNIEnv *env, JNFMemberInfo *field);
+JNF_EXPORT extern jfloat JNFGetStaticFloatField (JNIEnv *env, JNFMemberInfo *field);
+JNF_EXPORT extern jdouble JNFGetStaticDoubleField (JNIEnv *env, JNFMemberInfo *field);
+
+// Non-static setters
+JNF_EXPORT extern void JNFSetObjectField (JNIEnv *env, jobject obj, JNFMemberInfo *field, jobject val);
+JNF_EXPORT extern void JNFSetBooleanField(JNIEnv *env, jobject obj, JNFMemberInfo *field, jboolean val);
+JNF_EXPORT extern void JNFSetByteField (JNIEnv *env, jobject obj, JNFMemberInfo *field, jbyte val);
+JNF_EXPORT extern void JNFSetCharField (JNIEnv *env, jobject obj, JNFMemberInfo *field, jchar val);
+JNF_EXPORT extern void JNFSetShortField (JNIEnv *env, jobject obj, JNFMemberInfo *field, jshort val);
+JNF_EXPORT extern void JNFSetIntField (JNIEnv *env, jobject obj, JNFMemberInfo *field, jint val);
+JNF_EXPORT extern void JNFSetLongField (JNIEnv *env, jobject obj, JNFMemberInfo *field, jlong val);
+JNF_EXPORT extern void JNFSetFloatField (JNIEnv *env, jobject obj, JNFMemberInfo *field, jfloat val);
+JNF_EXPORT extern void JNFSetDoubleField (JNIEnv *env, jobject obj, JNFMemberInfo *field, jdouble val);
+
+// Static setters
+JNF_EXPORT extern void JNFSetStaticObjectField (JNIEnv *env, JNFMemberInfo *field, jobject val);
+JNF_EXPORT extern void JNFSetStaticBooleanField(JNIEnv *env, JNFMemberInfo *field, jboolean val);
+JNF_EXPORT extern void JNFSetStaticByteField (JNIEnv *env, JNFMemberInfo *field, jbyte val);
+JNF_EXPORT extern void JNFSetStaticCharField (JNIEnv *env, JNFMemberInfo *field, jchar val);
+JNF_EXPORT extern void JNFSetStaticShortField (JNIEnv *env, JNFMemberInfo *field, jshort val);
+JNF_EXPORT extern void JNFSetStaticIntField (JNIEnv *env, JNFMemberInfo *field, jint val);
+JNF_EXPORT extern void JNFSetStaticLongField (JNIEnv *env, JNFMemberInfo *field, jlong val);
+JNF_EXPORT extern void JNFSetStaticFloatField (JNIEnv *env, JNFMemberInfo *field, jfloat val);
+JNF_EXPORT extern void JNFSetStaticDoubleField (JNIEnv *env, JNFMemberInfo *field, jdouble val);
+
+// Calling instance methods
+JNF_EXPORT extern void JNFCallVoidMethod (JNIEnv *env, jobject obj, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jobject JNFCallObjectMethod (JNIEnv *env, jobject obj, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jboolean JNFCallBooleanMethod(JNIEnv *env, jobject obj, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jbyte JNFCallByteMethod (JNIEnv *env, jobject obj, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jchar JNFCallCharMethod (JNIEnv *env, jobject obj, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jshort JNFCallShortMethod (JNIEnv *env, jobject obj, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jint JNFCallIntMethod (JNIEnv *env, jobject obj, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jlong JNFCallLongMethod (JNIEnv *env, jobject obj, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jfloat JNFCallFloatMethod (JNIEnv *env, jobject obj, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jdouble JNFCallDoubleMethod (JNIEnv *env, jobject obj, JNFMemberInfo *method, ...);
+
+// Calling static methods
+JNF_EXPORT extern void JNFCallStaticVoidMethod (JNIEnv *env, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jobject JNFCallStaticObjectMethod (JNIEnv *env, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jboolean JNFCallStaticBooleanMethod(JNIEnv *env, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jbyte JNFCallStaticByteMethod (JNIEnv *env, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jchar JNFCallStaticCharMethod (JNIEnv *env, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jshort JNFCallStaticShortMethod (JNIEnv *env, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jint JNFCallStaticIntMethod (JNIEnv *env, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jlong JNFCallStaticLongMethod (JNIEnv *env, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jfloat JNFCallStaticFloatMethod (JNIEnv *env, JNFMemberInfo *method, ...);
+JNF_EXPORT extern jdouble JNFCallStaticDoubleMethod (JNIEnv *env, JNFMemberInfo *method, ...);
+
+// Global references
+JNF_EXPORT extern jobject JNFNewGlobalRef(JNIEnv *env, jobject obj);
+JNF_EXPORT extern void JNFDeleteGlobalRef(JNIEnv *env, jobject globalRef);
+JNF_EXPORT extern jobject JNFNewWeakGlobalRef(JNIEnv *env, jobject obj);
+JNF_EXPORT extern void JNFDeleteWeakGlobalRef(JNIEnv *env, jobject globalRef);
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFJNI.m b/src/JavaNativeFoundation/JNFJNI.m
new file mode 100644
index 0000000..1d20327
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFJNI.m
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFJNI.h"
+
+#import "JNFAssert.h"
+#import "debug.h"
+
+// constants ripped from jvm.h
+#define JVM_SIGNATURE_ARRAY '['
+#define JVM_SIGNATURE_BYTE 'B'
+#define JVM_SIGNATURE_CHAR 'C'
+#define JVM_SIGNATURE_CLASS 'L'
+#define JVM_SIGNATURE_ENDCLASS ';'
+#define JVM_SIGNATURE_ENUM 'E'
+#define JVM_SIGNATURE_FLOAT 'F'
+#define JVM_SIGNATURE_DOUBLE 'D'
+#define JVM_SIGNATURE_FUNC '('
+#define JVM_SIGNATURE_ENDFUNC ')'
+#define JVM_SIGNATURE_INT 'I'
+#define JVM_SIGNATURE_LONG 'J'
+#define JVM_SIGNATURE_SHORT 'S'
+#define JVM_SIGNATURE_VOID 'V'
+#define JVM_SIGNATURE_BOOLEAN 'Z'
+
+// IS_METHOD - Does the parameter point to a method member?
+//
+// This is used mostly in asserts.
+#define IS_METHOD(member) (*(member->sig) == SIGNATURE_FUNC)
+
+static inline BOOL isMethod(JNFMemberInfo *member) {
+ return (*(member->sig)) == JVM_SIGNATURE_FUNC;
+}
+
+static void JNFLookupClass(JNIEnv *env, JNFClassInfo *class) {
+ jclass localCls = NULL;
+ JNF_ASSERT_COND(class);
+ localCls = (*env)->FindClass(env, class->name);
+ if (localCls == NULL) [JNFException raiseUnnamedException:env];
+
+ class->cls = JNFNewGlobalRef(env, localCls);
+ (*env)->DeleteLocalRef(env, localCls);
+ if (class->cls == NULL) [JNFException raiseUnnamedException:env];
+}
+
+static void JNFLookupMemberID(JNIEnv *env, JNFMemberInfo *member) {
+ JNF_ASSERT_COND(member);
+ JNF_ASSERT_COND(member->classInfo);
+
+ if (member->classInfo->cls == NULL) JNFLookupClass(env, member->classInfo);
+
+ if (isMethod(member)) {
+ member->j.methodID = member->isStatic ?
+ (*env)->GetStaticMethodID(env, member->classInfo->cls, member->name, member->sig) :
+ (*env)->GetMethodID(env, member->classInfo->cls, member->name, member->sig);
+ } else { // This member is a field
+ member->j.fieldID = member->isStatic ?
+ (*env)->GetStaticFieldID(env, member->classInfo->cls, member->name, member->sig) :
+ (*env)->GetFieldID(env, member->classInfo->cls, member->name, member->sig);
+ }
+
+ // If NULL, then exception occurred
+ if (member->j.methodID == NULL) [JNFException raiseUnnamedException:env];
+}
+
+BOOL JNFIsInstanceOf(JNIEnv *env, jobject obj, JNFClassInfo *clazz) {
+ if (clazz->cls == NULL) JNFLookupClass(env, clazz);
+ return (BOOL)(*env)->IsInstanceOf(env, obj, clazz->cls);
+}
+
+//
+// A whole mess o' macros
+//
+// All of these macros provide caching of class refs and member IDs,
+// which speeds all member accesses following the first. In addition,
+// Java exceptions are caught and turned into NSExceptions,
+// so error handling is much easier on the native side.
+//
+// You can use these if you need them for optimization, but it's generally
+// easier and better to use the utility functions in jni_utilities.h.
+
+// LOOKUP_MEMBER_ID - Lookup a fieldID if needed
+//
+// Give this macro a JNFMemberInfo*, and it will
+// lookup and cache the field ID as needed.
+#define LOOKUP_MEMBER_ID(env, member) \
+if (member->j.fieldID == NULL) \
+JNFLookupMemberID(env, member)
+
+#ifdef RAWT_DEBUG
+#define VERIFY_MEMBERS
+#endif
+
+#ifdef VERIFY_MEMBERS
+#define VERIFY_FIELD(env, obj, field, sig) VerifyMember(env, obj, field, sig, NO)
+#define VERIFY_METHOD(env, obj, method, sig) VerifyMember(env, obj, method, sig, YES)
+#else
+#define VERIFY_FIELD(env, obj, member, sig)
+#define VERIFY_METHOD(env, obj, member, sig)
+#endif /* RAWT_DEBUG */
+
+// GET_FIELD - Safe way to get a java field
+//
+// This macro takes care of the caching and exception
+// propgation.
+#define GET_FIELD(env, obj, field, result, sig, jni_call) \
+ LOOKUP_MEMBER_ID(env, field); \
+ VERIFY_FIELD(env, obj, field, sig); \
+ result = (*env)->jni_call(env, obj, field->j.fieldID); \
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+// GET_STATIC_FIELD
+//
+#define GET_STATIC_FIELD(env, field, result, sig, jni_call) \
+ LOOKUP_MEMBER_ID(env, field); \
+ VERIFY_FIELD(env, NULL, field, sig); \
+ result = (*env)->jni_call(env, field->classInfo->cls, field->j.fieldID); \
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+// SET_FIELD - Safe way to set a java field
+//
+// Similar to GET_FIELD
+#define SET_FIELD(env, obj, field, val, sig, jni_call) \
+ LOOKUP_MEMBER_ID(env, field); \
+ VERIFY_FIELD(env, obj, field, sig); \
+ (*env)->jni_call(env, obj, field->j.fieldID, val); \
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+// SET_STATIC_FIELD
+//
+#define SET_STATIC_FIELD(env, field, val, sig, jni_call) \
+ LOOKUP_MEMBER_ID(env, field); \
+ VERIFY_FIELD(env, NULL, field, sig); \
+ (*env)->jni_call(env, field->classInfo->cls, field->j.fieldID, val); \
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+// CALL_VOID_METHOD
+//
+// "args" is a va_list
+#define CALL_VOID_METHOD(env, obj, method, jni_call, args) \
+ LOOKUP_MEMBER_ID(env, method); \
+ VERIFY_METHOD(env, obj, method, JVM_SIGNATURE_VOID); \
+ (*env)->jni_call(env, obj, method->j.methodID, args); \
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+// CALL_STATIC_VOID_METHOD
+//
+// "args" is a va_list
+#define CALL_STATIC_VOID_METHOD(env, method, jni_call, args) \
+ LOOKUP_MEMBER_ID(env, method); \
+ VERIFY_METHOD(env, NULL, method, JVM_SIGNATURE_VOID); \
+ (*env)->jni_call(env, method->classInfo->cls, method->j.methodID, args); \
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+// CALL_METHOD - Call a method that returns a value
+//
+// "args" is a va_list
+#define CALL_METHOD(env, obj, method, result, sig, jni_call, args ) \
+ LOOKUP_MEMBER_ID(env, method); \
+ VERIFY_METHOD(env, obj, method, sig); \
+ result = (*env)->jni_call(env, obj, method->j.methodID, args); \
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+// CALL_STATIC_METHOD - Call a static method that returns a value
+//
+// "args" is a va_list
+#define CALL_STATIC_METHOD(env, method, result, sig, jni_call, args) \
+ LOOKUP_MEMBER_ID(env, method); \
+ VERIFY_METHOD(env, NULL, method, sig); \
+ result = (*env)->jni_call(env, method->classInfo->cls, method->j.methodID, args); \
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+
+// NEW_OBJECT - Create instances
+//
+// "constructor" is a JNFMemberInfo* to a constructor method
+// "args" is a va_list of arguments
+#define NEW_OBJECT(env, constructor, obj, args) \
+ LOOKUP_MEMBER_ID(env, constructor); \
+ VERIFY_METHOD(env, NULL, constructor, JVM_SIGNATURE_VOID); \
+ obj = (*env)->NewObjectV(env, constructor->classInfo->cls, constructor->j.methodID, args); \
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+
+//
+// Non-static getters & setters
+#define GET_FIELD_IMPLEMENTATION(type, sig, jni_call) \
+ type JNF ## jni_call(JNIEnv *env, jobject obj, JNFMemberInfo* field) { \
+ type result; \
+ JNF_ASSERT_COND(!field->isStatic); \
+ GET_FIELD(env, obj, field, result, sig, jni_call); \
+ return result; \
+ }
+
+#define SET_FIELD_IMPLEMENTATION(type, sig, jni_call) \
+ void JNF ## jni_call(JNIEnv *env, jobject obj, JNFMemberInfo* field, type val) {\
+ JNF_ASSERT_COND(!field->isStatic); \
+ SET_FIELD(env, obj, field, val, sig, jni_call); \
+ }
+
+//
+// Non-static getter implemenations
+GET_FIELD_IMPLEMENTATION(jobject, JVM_SIGNATURE_ENDCLASS, GetObjectField)
+GET_FIELD_IMPLEMENTATION(jboolean, JVM_SIGNATURE_BOOLEAN, GetBooleanField)
+GET_FIELD_IMPLEMENTATION(jbyte, JVM_SIGNATURE_BYTE, GetByteField)
+GET_FIELD_IMPLEMENTATION(jchar, JVM_SIGNATURE_CHAR, GetCharField)
+GET_FIELD_IMPLEMENTATION(jshort, JVM_SIGNATURE_SHORT, GetShortField)
+GET_FIELD_IMPLEMENTATION(jint, JVM_SIGNATURE_INT, GetIntField)
+GET_FIELD_IMPLEMENTATION(jlong, JVM_SIGNATURE_LONG, GetLongField)
+GET_FIELD_IMPLEMENTATION(jfloat, JVM_SIGNATURE_FLOAT, GetFloatField)
+GET_FIELD_IMPLEMENTATION(jdouble, JVM_SIGNATURE_DOUBLE, GetDoubleField)
+
+//
+// Non-static setter implemenations
+SET_FIELD_IMPLEMENTATION(jobject, JVM_SIGNATURE_ENDCLASS, SetObjectField)
+SET_FIELD_IMPLEMENTATION(jboolean, JVM_SIGNATURE_BOOLEAN, SetBooleanField)
+SET_FIELD_IMPLEMENTATION(jbyte, JVM_SIGNATURE_BYTE, SetByteField)
+SET_FIELD_IMPLEMENTATION(jchar, JVM_SIGNATURE_CHAR, SetCharField)
+SET_FIELD_IMPLEMENTATION(jshort, JVM_SIGNATURE_SHORT, SetShortField)
+SET_FIELD_IMPLEMENTATION(jint, JVM_SIGNATURE_INT, SetIntField)
+SET_FIELD_IMPLEMENTATION(jlong, JVM_SIGNATURE_LONG, SetLongField)
+SET_FIELD_IMPLEMENTATION(jfloat, JVM_SIGNATURE_FLOAT, SetFloatField)
+SET_FIELD_IMPLEMENTATION(jdouble, JVM_SIGNATURE_DOUBLE, SetDoubleField)
+
+//
+// Static getters & setters
+#define GET_STATIC_FIELD_IMPLEMENTATION(type, sig, jni_call) \
+ type JNF ## jni_call(JNIEnv *env, JNFMemberInfo* field) { \
+ type result; \
+ JNF_ASSERT_COND(field->isStatic); \
+ GET_STATIC_FIELD(env, field, result, sig, jni_call); \
+ return result; \
+ }
+
+#define SET_STATIC_FIELD_IMPLEMENTATION(type, sig, jni_call) \
+ void JNF ## jni_call(JNIEnv *env, JNFMemberInfo* field, type val) { \
+ JNF_ASSERT_COND(field->isStatic); \
+ SET_STATIC_FIELD(env, field, val, sig, jni_call); \
+ }
+
+//
+// Static getter implementations
+GET_STATIC_FIELD_IMPLEMENTATION(jobject, JVM_SIGNATURE_ENDCLASS, GetStaticObjectField)
+GET_STATIC_FIELD_IMPLEMENTATION(jboolean, JVM_SIGNATURE_BOOLEAN, GetStaticBooleanField)
+GET_STATIC_FIELD_IMPLEMENTATION(jbyte, JVM_SIGNATURE_BYTE, GetStaticByteField)
+GET_STATIC_FIELD_IMPLEMENTATION(jchar, JVM_SIGNATURE_CHAR, GetStaticCharField)
+GET_STATIC_FIELD_IMPLEMENTATION(jshort, JVM_SIGNATURE_SHORT, GetStaticShortField)
+GET_STATIC_FIELD_IMPLEMENTATION(jint, JVM_SIGNATURE_INT, GetStaticIntField)
+GET_STATIC_FIELD_IMPLEMENTATION(jlong, JVM_SIGNATURE_LONG, GetStaticLongField)
+GET_STATIC_FIELD_IMPLEMENTATION(jfloat, JVM_SIGNATURE_FLOAT, GetStaticFloatField)
+GET_STATIC_FIELD_IMPLEMENTATION(jdouble, JVM_SIGNATURE_DOUBLE, GetStaticDoubleField)
+
+//
+// Static setter implemenations
+SET_STATIC_FIELD_IMPLEMENTATION(jobject, JVM_SIGNATURE_ENDCLASS, SetStaticObjectField)
+SET_STATIC_FIELD_IMPLEMENTATION(jboolean, JVM_SIGNATURE_BOOLEAN, SetStaticBooleanField)
+SET_STATIC_FIELD_IMPLEMENTATION(jbyte, JVM_SIGNATURE_BYTE, SetStaticByteField)
+SET_STATIC_FIELD_IMPLEMENTATION(jchar, JVM_SIGNATURE_CHAR, SetStaticCharField)
+SET_STATIC_FIELD_IMPLEMENTATION(jshort, JVM_SIGNATURE_SHORT, SetStaticShortField)
+SET_STATIC_FIELD_IMPLEMENTATION(jint, JVM_SIGNATURE_INT, SetStaticIntField)
+SET_STATIC_FIELD_IMPLEMENTATION(jlong, JVM_SIGNATURE_LONG, SetStaticLongField)
+SET_STATIC_FIELD_IMPLEMENTATION(jfloat, JVM_SIGNATURE_FLOAT, SetStaticFloatField)
+SET_STATIC_FIELD_IMPLEMENTATION(jdouble, JVM_SIGNATURE_DOUBLE, SetStaticDoubleField)
+
+
+//
+// Calling instance methods
+//
+// FIX: if an exception is thrown, va_end is not called.
+// On i386, va_end is a null macro. Check on PPC to verify the same.
+void JNFCallVoidMethod(JNIEnv *env, jobject obj, JNFMemberInfo* method, ...) {
+ va_list args;
+ JNF_ASSERT_COND(!method->isStatic);
+ va_start(args, method);
+ CALL_VOID_METHOD(env, obj, method, CallVoidMethodV, args);
+ va_end(args);
+}
+
+#define CALL_METHOD_IMPLEMENTATION(type, sig, jni_call) \
+ type JNF ## jni_call(JNIEnv *env, jobject obj, JNFMemberInfo* method, ...) {\
+ type result; \
+ va_list args; \
+ JNF_ASSERT_COND(!method->isStatic); \
+ va_start(args, method); \
+ CALL_METHOD(env, obj, method, result, sig, jni_call ## V, args); \
+ va_end(args); \
+ return result; \
+ }
+
+CALL_METHOD_IMPLEMENTATION(jobject, JVM_SIGNATURE_ENDCLASS, CallObjectMethod)
+CALL_METHOD_IMPLEMENTATION(jboolean, JVM_SIGNATURE_BOOLEAN, CallBooleanMethod)
+CALL_METHOD_IMPLEMENTATION(jbyte, JVM_SIGNATURE_BYTE, CallByteMethod)
+CALL_METHOD_IMPLEMENTATION(jchar, JVM_SIGNATURE_CHAR, CallCharMethod)
+CALL_METHOD_IMPLEMENTATION(jshort, JVM_SIGNATURE_SHORT, CallShortMethod)
+CALL_METHOD_IMPLEMENTATION(jint, JVM_SIGNATURE_INT, CallIntMethod)
+CALL_METHOD_IMPLEMENTATION(jlong, JVM_SIGNATURE_LONG, CallLongMethod)
+CALL_METHOD_IMPLEMENTATION(jfloat, JVM_SIGNATURE_FLOAT, CallFloatMethod)
+CALL_METHOD_IMPLEMENTATION(jdouble, JVM_SIGNATURE_DOUBLE, CallDoubleMethod)
+
+//
+// Calling static methods
+//
+// FIX: if an exception is thrown, va_end is not called.
+// On i386, va_end is a null macro. Check on PPC to verify the same.
+void JNFCallStaticVoidMethod(JNIEnv *env, JNFMemberInfo* method, ...) {
+ va_list args;
+ JNF_ASSERT_COND(method->isStatic);
+ va_start(args, method);
+ CALL_STATIC_VOID_METHOD(env, method, CallStaticVoidMethodV, args);
+ va_end(args);
+}
+
+#define CALL_STATIC_METHOD_IMPLEMENTATION(type, sig, jni_call) \
+ type JNF ## jni_call(JNIEnv *env, JNFMemberInfo* method, ...) { \
+ type result; \
+ va_list args; \
+ JNF_ASSERT_COND(method->isStatic); \
+ va_start(args, method); \
+ CALL_STATIC_METHOD(env, method, result, sig, jni_call ## V, args); \
+ va_end(args); \
+ return result; \
+ }
+
+CALL_STATIC_METHOD_IMPLEMENTATION(jobject, JVM_SIGNATURE_ENDCLASS, CallStaticObjectMethod)
+CALL_STATIC_METHOD_IMPLEMENTATION(jboolean, JVM_SIGNATURE_BOOLEAN, CallStaticBooleanMethod)
+CALL_STATIC_METHOD_IMPLEMENTATION(jbyte, JVM_SIGNATURE_BYTE, CallStaticByteMethod)
+CALL_STATIC_METHOD_IMPLEMENTATION(jchar, JVM_SIGNATURE_CHAR, CallStaticCharMethod)
+CALL_STATIC_METHOD_IMPLEMENTATION(jshort, JVM_SIGNATURE_SHORT, CallStaticShortMethod)
+CALL_STATIC_METHOD_IMPLEMENTATION(jint, JVM_SIGNATURE_INT, CallStaticIntMethod)
+CALL_STATIC_METHOD_IMPLEMENTATION(jlong, JVM_SIGNATURE_LONG, CallStaticLongMethod)
+CALL_STATIC_METHOD_IMPLEMENTATION(jfloat, JVM_SIGNATURE_FLOAT, CallStaticFloatMethod)
+CALL_STATIC_METHOD_IMPLEMENTATION(jdouble, JVM_SIGNATURE_DOUBLE, CallStaticDoubleMethod)
+
+//
+// Creating new object instances
+jobject JNFNewObject(JNIEnv *env, JNFMemberInfo* constructor, ...)
+{
+ jobject newobj;
+ va_list args;
+ JNF_ASSERT_COND(!constructor->isStatic);
+ va_start(args, constructor);
+ NEW_OBJECT(env, constructor, newobj, args);
+ va_end(args);
+ return newobj;
+}
+
+jobjectArray JNFNewObjectArray(JNIEnv *env, JNFClassInfo *clazz, jsize length)
+{
+ if (clazz->cls == NULL) JNFLookupClass(env, clazz);
+ JNF_ASSERT_COND(clazz->cls);
+ jobjectArray newArray = (*env)->NewObjectArray(env, length, clazz->cls, NULL);
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+ return newArray;
+}
+
+#define NEW_PRIMITIVE_ARRAY(primitiveArrayType, methodName) \
+ primitiveArrayType JNF ## methodName(JNIEnv *env, jsize length) { \
+ primitiveArrayType array = (*env)->methodName(env, length); \
+ JNF_CHECK_AND_RETHROW_EXCEPTION(env); \
+ return array; \
+ }
+
+NEW_PRIMITIVE_ARRAY(jbooleanArray, NewBooleanArray)
+NEW_PRIMITIVE_ARRAY(jbyteArray, NewByteArray)
+NEW_PRIMITIVE_ARRAY(jcharArray, NewCharArray)
+NEW_PRIMITIVE_ARRAY(jshortArray, NewShortArray)
+NEW_PRIMITIVE_ARRAY(jintArray, NewIntArray)
+NEW_PRIMITIVE_ARRAY(jlongArray, NewLongArray)
+NEW_PRIMITIVE_ARRAY(jfloatArray, NewFloatArray)
+NEW_PRIMITIVE_ARRAY(jdoubleArray, NewDoubleArray)
+
+
+// Class-related functions
+
+// A bottleneck for creating global references
+jobject JNFNewGlobalRef(JNIEnv *env, jobject obj)
+{
+ if (!obj) return NULL;
+
+ jobject globalRef = (*env)->NewGlobalRef(env, obj);
+ if (!globalRef) JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+ // JNF_WARN("Created global ref %#08lx to object:", globalRef);
+ // JNFDumpJavaObject(env, globalRef);
+ return globalRef;
+}
+
+// A bottleneck for deleting global references.
+void JNFDeleteGlobalRef(JNIEnv *env, jobject globalRef)
+{
+ if (!globalRef) return;
+ // JNF_WARN("Deleting global ref %#08lx to object:", globalRef);
+ // JNFDumpJavaObject(env, globalRef);
+ (*env)->DeleteGlobalRef(env, globalRef);
+}
+
+// A bottleneck for creating weak global references
+jobject JNFNewWeakGlobalRef(JNIEnv *env, jobject obj)
+{
+ if (!obj) return NULL;
+
+ jobject globalRef = (*env)->NewWeakGlobalRef(env, obj);
+ if (!globalRef) JNF_CHECK_AND_RETHROW_EXCEPTION(env);
+
+ // JNF_WARN("Created global ref %#08lx to object:", globalRef);
+ // JNFDumpJavaObject(env, globalRef);
+ return globalRef;
+}
+
+// A bottleneck for deleting weak global references.
+void JNFDeleteWeakGlobalRef(JNIEnv *env, jobject globalRef)
+{
+ if (!globalRef) return;
+ // JNF_WARN("Deleting global ref %#08lx to object:", globalRef);
+ // JNFDumpJavaObject(env, globalRef);
+ (*env)->DeleteWeakGlobalRef(env, globalRef);
+}
diff --git a/src/JavaNativeFoundation/JNFJObjectWrapper.h b/src/JavaNativeFoundation/JNFJObjectWrapper.h
new file mode 100644
index 0000000..58f5f0b
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFJObjectWrapper.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Simple wrapper classes to hold Java Objects in JNI global references.
+ *
+ * This is used to pass Java objects across thread boundries, often through
+ * -performSelectorOnMainThread invocations. This wrapper properly creates a
+ * new global ref, and clears it on -dealloc or -finalize, attaching to the
+ * current VM, attaching the current thread if necessary, releasing the global
+ * ref, and detaching the thread from the VM if it attached it.
+ *
+ * Destruction of this wrapper is expensive if the jobject has not been
+ * pre-cleared, because it must re-attach to the JVM.
+ *
+ * The JNFWeakJObjectWrapper manages a weak global reference which may become
+ * invalid if the JVM garbage collects the original object.
+ */
+
+#import
+#import
+
+__BEGIN_DECLS
+
+JNF_EXPORT
+@interface JNFJObjectWrapper : NSObject
+
++ (JNFJObjectWrapper *) wrapperWithJObject:(jobject)jObjectIn withEnv:(JNIEnv *)env;
+- (id) initWithJObject:(jobject)jObjectIn withEnv:(JNIEnv *)env;
+- (void) setJObject:(jobject)jObjectIn withEnv:(JNIEnv *)env; // clears any pre-existing global-ref
+- (jobject) jObjectWithEnv:(JNIEnv *)env; // returns a new local-ref, must be released with DeleteLocalRef
+
+@property (readonly, nonatomic, assign) jobject jObject;
+
+@end
+
+
+JNF_EXPORT
+@interface JNFWeakJObjectWrapper : JNFJObjectWrapper { }
+
++ (JNFWeakJObjectWrapper *) wrapperWithJObject:(jobject)jObjectIn withEnv:(JNIEnv *)env;
+
+@end
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFJObjectWrapper.m b/src/JavaNativeFoundation/JNFJObjectWrapper.m
new file mode 100644
index 0000000..17eac99
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFJObjectWrapper.m
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFJObjectWrapper.h"
+
+#import "JNFJNI.h"
+#import "JNFThread.h"
+
+@interface JNFJObjectWrapper ()
+@property (readwrite, nonatomic, assign) jobject jObject;
+@end
+
+@implementation JNFJObjectWrapper
+
+- (jobject) _getWithEnv:(__unused JNIEnv *)env {
+ return self.jObject;
+}
+
+- (jobject) _createObj:(jobject)jObjectIn withEnv:(JNIEnv *)env {
+ return JNFNewGlobalRef(env, jObjectIn);
+}
+
+- (void) _destroyObj:(jobject)jObjectIn withEnv:(JNIEnv *)env {
+ JNFDeleteGlobalRef(env, jObjectIn);
+}
+
++ (JNFJObjectWrapper *) wrapperWithJObject:(jobject)jObjectIn withEnv:(JNIEnv *)env {
+ return [[[JNFJObjectWrapper alloc] initWithJObject:jObjectIn withEnv:env] autorelease];
+}
+
+- (id) initWithJObject:(jobject)jObjectIn withEnv:(JNIEnv *)env {
+ self = [super init];
+ if (!self) return self;
+
+ if (jObjectIn) {
+ self.jObject = [self _createObj:jObjectIn withEnv:env];
+ }
+
+ return self;
+}
+
+- (jobject) jObjectWithEnv:(JNIEnv *)env {
+ jobject validObj = [self _getWithEnv:env];
+ if (!validObj) return NULL;
+
+ return (*env)->NewLocalRef(env, validObj);
+}
+
+- (void) setJObject:(jobject)jObjectIn withEnv:(JNIEnv *)env {
+ jobject const jobj = self.jObject;
+ if (jobj == jObjectIn) return;
+
+ if (jobj) {
+ [self _destroyObj:jobj withEnv:env];
+ }
+
+ if (jObjectIn) {
+ self.jObject = [self _createObj:jObjectIn withEnv:env];
+ } else {
+ self.jObject = NULL;
+ }
+}
+
+- (void) clearJObjectReference {
+ jobject const jobj = self.jObject;
+ if (!jobj) return;
+
+ JNFThreadContext threadContext = JNFThreadDetachImmediately;
+ JNIEnv *env = JNFObtainEnv(&threadContext);
+ if (env == NULL) return; // leak?
+
+ [self _destroyObj:jobj withEnv:env];
+ self.jObject = NULL;
+
+ JNFReleaseEnv(env, &threadContext);
+}
+
+- (void) dealloc {
+ [self clearJObjectReference];
+ [super dealloc];
+}
+
+@end
+
+
+@implementation JNFWeakJObjectWrapper
+
++ (JNFWeakJObjectWrapper *) wrapperWithJObject:(jobject)jObjectIn withEnv:(JNIEnv *)env {
+ return [[[JNFWeakJObjectWrapper alloc] initWithJObject:jObjectIn withEnv:env] autorelease];
+}
+
+- (jobject) _getWithEnv:(JNIEnv *)env {
+ jobject const jobj = self.jObject;
+
+ if ((*env)->IsSameObject(env, jobj, NULL) == JNI_TRUE) {
+ self.jObject = NULL; // object went invalid
+ return NULL;
+ }
+ return jobj;
+}
+
+- (jobject) _createObj:(jobject)jObjectIn withEnv:(JNIEnv *)env {
+ return JNFNewWeakGlobalRef(env, jObjectIn);
+}
+
+- (void) _destroyObj:(jobject)jObjectIn withEnv:(JNIEnv *)env {
+ JNFDeleteWeakGlobalRef(env, jObjectIn);
+}
+
+@end
diff --git a/src/JavaNativeFoundation/JNFNumber.h b/src/JavaNativeFoundation/JNFNumber.h
new file mode 100644
index 0000000..0a4683b
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFNumber.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Functions that convert between number container classes.
+ */
+
+#import
+
+#import
+
+__BEGIN_DECLS
+
+/*
+ * Converts java.lang.Number to an NSNumber
+ * NOTE: Return value is auto-released, so if you need to hang on to it, you should retain it.
+ */
+JNF_EXPORT extern NSNumber *JNFJavaToNSNumber(JNIEnv *env, jobject n);
+
+/*
+ * Converts an NSNumber to a java.lang.Number
+ * Only returns java.lang.Longs or java.lang.Doubles.
+ * NOTE: This returns a JNI Local Ref. Any code that calls must call DeleteLocalRef with the return value.
+ */
+JNF_EXPORT extern jobject JNFNSToJavaNumber(JNIEnv *env, NSNumber *n);
+
+/*
+ * Converts a java.lang.Boolean constants to the CFBooleanRef constants
+ */
+JNF_EXPORT extern CFBooleanRef JNFJavaToCFBoolean(JNIEnv* env, jobject b);
+
+/*
+ * Converts a CFBooleanRef constants to the java.lang.Boolean constants
+ */
+JNF_EXPORT extern jobject JNFCFToJavaBoolean(JNIEnv *env, CFBooleanRef b);
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFNumber.m b/src/JavaNativeFoundation/JNFNumber.m
new file mode 100644
index 0000000..aa83276
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFNumber.m
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFNumber.h"
+#import "JNFJNI.h"
+
+static JNF_CLASS_CACHE(sjc_Long, "java/lang/Long");
+static JNF_CLASS_CACHE(sjc_Double, "java/lang/Double");
+static JNF_CLASS_CACHE(sjc_Boolean, "java/lang/Boolean");
+
+NSNumber *JNFJavaToNSNumber(JNIEnv* env, jobject n)
+{
+ if (n == NULL) return nil;
+
+ static JNF_CLASS_CACHE(sjc_Number, "java/lang/Number");
+ static JNF_CLASS_CACHE(sjc_Integer, "java/lang/Integer");
+ static JNF_CLASS_CACHE(sjc_Float, "java/lang/Float");
+ static JNF_CLASS_CACHE(sjc_Byte, "java/lang/Byte");
+ static JNF_CLASS_CACHE(sjc_Short, "java/lang/Short");
+
+ // AWT_THREADING Safe (known object)
+ if (JNFIsInstanceOf(env, n, &sjc_Integer)) {
+ static JNF_MEMBER_CACHE(jm_intValue, sjc_Number, "intValue", "()I");
+ return [NSNumber numberWithInt:JNFCallIntMethod(env, n, jm_intValue)];
+ } else if (JNFIsInstanceOf(env, n, &sjc_Long)) {
+ static JNF_MEMBER_CACHE(jm_longValue, sjc_Number, "longValue", "()J");
+ return [NSNumber numberWithLongLong:JNFCallLongMethod(env, n, jm_longValue)];
+ } else if (JNFIsInstanceOf(env, n, &sjc_Float)) {
+ static JNF_MEMBER_CACHE(jm_floatValue, sjc_Number, "floatValue", "()F");
+ return [NSNumber numberWithFloat:JNFCallFloatMethod(env, n, jm_floatValue)];
+ } else if (JNFIsInstanceOf(env, n, &sjc_Double)) {
+ static JNF_MEMBER_CACHE(jm_doubleValue, sjc_Number, "doubleValue", "()D");
+ return [NSNumber numberWithDouble:JNFCallDoubleMethod(env, n, jm_doubleValue)];
+ } else if (JNFIsInstanceOf(env, n, &sjc_Byte)) {
+ static JNF_MEMBER_CACHE(jm_byteValue, sjc_Number, "byteValue", "()B");
+ return [NSNumber numberWithChar:JNFCallByteMethod(env, n, jm_byteValue)];
+ } else if (JNFIsInstanceOf(env, n, &sjc_Short)) {
+ static JNF_MEMBER_CACHE(jm_shortValue, sjc_Number, "shortValue", "()S");
+ return [NSNumber numberWithShort:JNFCallShortMethod(env, n, jm_shortValue)];
+ }
+
+ return [NSNumber numberWithInt:0];
+}
+
+jobject JNFNSToJavaNumber(JNIEnv *env, NSNumber *n)
+{
+ if (n == nil) return NULL;
+
+ if (CFNumberIsFloatType((CFNumberRef)n)) {
+ static JNF_CTOR_CACHE(jm_Double, sjc_Double, "(D)V");
+ return JNFNewObject(env, jm_Double, [n doubleValue]); // AWT_THREADING Safe (known object)
+ } else {
+ static JNF_CTOR_CACHE(jm_Long, sjc_Long, "(J)V");
+ return JNFNewObject(env, jm_Long, [n longLongValue]); // AWT_THREADING Safe (known object)
+ }
+}
+
+CFBooleanRef JNFJavaToCFBoolean(JNIEnv* env, jobject b)
+{
+ if (b == NULL) return NULL;
+ if (!JNFIsInstanceOf(env, b, &sjc_Boolean)) return NULL;
+ static JNF_MEMBER_CACHE(jm_booleanValue, sjc_Boolean, "booleanValue", "()Z");
+ return JNFCallBooleanMethod(env, b, jm_booleanValue) ? kCFBooleanTrue : kCFBooleanTrue;
+}
+
+jobject JNFCFToJavaBoolean(JNIEnv *env, CFBooleanRef b)
+{
+ if (b == NULL) return NULL;
+ static JNF_STATIC_MEMBER_CACHE(js_TRUE, sjc_Boolean, "TRUE", "java/lang/Boolean");
+ static JNF_STATIC_MEMBER_CACHE(js_FALSE, sjc_Boolean, "FALSE", "java/lang/Boolean");
+ return JNFGetStaticObjectField(env, (b == kCFBooleanTrue) ? js_TRUE : js_FALSE);
+}
diff --git a/src/JavaNativeFoundation/JNFObject.h b/src/JavaNativeFoundation/JNFObject.h
new file mode 100644
index 0000000..24c1134
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFObject.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Functions that access some of the base functionality of java.lang.Object.
+ */
+
+#import
+
+#import
+
+__BEGIN_DECLS
+
+/*
+ * Returns Object.equals() for the two items
+ */
+JNF_EXPORT extern BOOL JNFObjectEquals(JNIEnv* env, jobject a, jobject b);
+
+/*
+ * Returns Object.toString() as an NSString
+ */
+JNF_EXPORT extern NSString *JNFObjectToString(JNIEnv *env, jobject obj);
+
+/*
+ * Returns Object.getClass().toString() as an NSString. Useful in gdb.
+ */
+JNF_EXPORT extern NSString *JNFObjectClassName(JNIEnv* env, jobject obj);
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFObject.m b/src/JavaNativeFoundation/JNFObject.m
new file mode 100644
index 0000000..510e58e
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFObject.m
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFObject.h"
+
+#import "JNFJNI.h"
+#import "JNFString.h"
+
+static JNF_CLASS_CACHE(sjc_Object, "java/lang/Object");
+
+BOOL JNFObjectEquals(JNIEnv* env, jobject a, jobject b)
+{
+ if ((a == NULL) && (b == NULL)) return YES;
+ if ((a == NULL) || (b == NULL)) return NO;
+
+ static JNF_MEMBER_CACHE(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z");
+ return (BOOL)JNFCallBooleanMethod(env, a, jm_equals, b); // AWT_THREADING Safe (!appKit)
+}
+
+NSString *JNFObjectToString(JNIEnv *env, jobject obj)
+{
+ static JNF_MEMBER_CACHE(jm_toString, sjc_Object, "toString", "()Ljava/lang/String;");
+ jobject name = JNFCallObjectMethod(env, obj, jm_toString); // AWT_THREADING Safe (known object)
+
+ id result = JNFJavaToNSString(env, name);
+ (*env)->DeleteLocalRef(env, name);
+ return result;
+}
+
+NSString *JNFObjectClassName(JNIEnv* env, jobject obj)
+{
+ static JNF_MEMBER_CACHE(jm_getClass, sjc_Object, "getClass", "()Ljava/lang/Class;");
+
+ jobject clz = JNFCallObjectMethod(env, obj, jm_getClass); // AWT_THREADING Safe (known object)
+ NSString *result = JNFObjectToString(env, clz);
+ (*env)->DeleteLocalRef(env, clz);
+ return result;
+}
diff --git a/src/JavaNativeFoundation/JNFPath.h b/src/JavaNativeFoundation/JNFPath.h
new file mode 100644
index 0000000..401bd69
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFPath.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Functions that create strings that are in the proper format for holding
+ * paths in Java and native.
+ */
+
+#import
+
+#import
+
+__BEGIN_DECLS
+
+/*
+ * Returns a jstring in precomposed UTF16 format that is compatable with Java's
+ * expectation of the UTF16 format for strings to be displayed.
+ */
+JNF_EXPORT extern jstring JNFNormalizedJavaStringForPath(JNIEnv *env, NSString *path);
+
+/*
+ * Returns an NSString in decomposed UTF16 format that is compatable with HFS's
+ * expectation of the UTF16 format for file system paths.
+ * NOTE: this NSString is autoreleased.
+ */
+JNF_EXPORT extern NSString *JNFNormalizedNSStringForPath(JNIEnv *env, jstring path);
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFPath.m b/src/JavaNativeFoundation/JNFPath.m
new file mode 100644
index 0000000..608a9e3
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFPath.m
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFPath.h"
+
+#import
+
+#import "JNFString.h"
+
+jstring JNFNormalizedJavaStringForPath(JNIEnv *env, NSString *inString)
+{
+ if (inString == nil) return NULL;
+
+ CFMutableStringRef mutableDisplayName = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)inString);
+ CFStringNormalize(mutableDisplayName, kCFStringNormalizationFormC);
+ jstring returnValue = JNFNSToJavaString(env, (NSString *)mutableDisplayName);
+ CFRelease(mutableDisplayName);
+
+ return returnValue;
+}
+
+NSString *JNFNormalizedNSStringForPath(JNIEnv *env, jstring javaString)
+{
+ if (javaString == NULL) return nil;
+
+ // We were given a filename, so convert it to a compatible representation for the file system.
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSString *fileName = JNFJavaToNSString(env, javaString);
+ const char *compatibleFilename = [fm fileSystemRepresentationWithPath:fileName];
+ return [fm stringWithFileSystemRepresentation:compatibleFilename length:strlen(compatibleFilename)];
+}
diff --git a/src/JavaNativeFoundation/JNFRunLoop.h b/src/JavaNativeFoundation/JNFRunLoop.h
new file mode 100644
index 0000000..e03e2a1
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFRunLoop.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Used to perform selectors and blocks in the Java runloop mode.
+ */
+
+#import
+#import
+
+__BEGIN_DECLS
+
+JNF_EXPORT extern NSString *JNFRunLoopDidStartNotification;
+
+JNF_EXPORT
+@interface JNFRunLoop : NSObject { }
+
++ (NSString *)javaRunLoopMode;
++ (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait;
+#if __BLOCKS__
++ (void)performOnMainThreadWaiting:(BOOL)waitUntilDone withBlock:(void (^)(void))block;
+#endif
+
+@end
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFRunLoop.m b/src/JavaNativeFoundation/JNFRunLoop.m
new file mode 100644
index 0000000..c3ece5b
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFRunLoop.m
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFRunLoop.h"
+
+#import
+
+
+NSString *JNFRunLoopDidStartNotification = @"JNFRunLoopDidStartNotification";
+
+static NSString *AWTRunLoopMode = @"AWTRunLoopMode";
+static NSArray *sPerformModes = nil;
+
+@implementation JNFRunLoop
+
++ (void)initialize {
+ if (sPerformModes) return;
+ sPerformModes = [[NSArray alloc] initWithObjects:NSDefaultRunLoopMode, NSModalPanelRunLoopMode, NSEventTrackingRunLoopMode, AWTRunLoopMode, nil];
+}
+
++ (NSString *)javaRunLoopMode {
+ return AWTRunLoopMode;
+}
+
++ (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)waitUntilDone {
+ [target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:waitUntilDone modes:sPerformModes];
+}
+
+#if __BLOCKS__
+
++ (void)_performDirectBlock:(void (^)(void))block {
+ block();
+}
+
++ (void)_performCopiedBlock:(void (^)(void))newBlock {
+ newBlock();
+ Block_release(newBlock);
+}
+
++ (void)performOnMainThreadWaiting:(BOOL)waitUntilDone withBlock:(void (^)(void))block {
+ if (waitUntilDone) {
+ [self performOnMainThread:@selector(_performDirectBlock:) on:self withObject:block waitUntilDone:YES];
+ } else {
+ void (^newBlock)(void) = Block_copy(block);
+ [self performOnMainThread:@selector(_performCopiedBlock:) on:self withObject:newBlock waitUntilDone:NO];
+ }
+}
+
+#endif
+
+@end
diff --git a/src/JavaNativeFoundation/JNFRunnable.h b/src/JavaNativeFoundation/JNFRunnable.h
new file mode 100644
index 0000000..1db4e4e
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFRunnable.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Creates NSInvocations which wrap java.lang.Runnables.
+ */
+
+#import
+#import
+
+__BEGIN_DECLS
+
+JNF_EXPORT
+@interface JNFRunnable : NSObject { }
++ (NSInvocation *) invocationWithRunnable:(jobject)runnable withEnv:(JNIEnv *)env;
+#if __BLOCKS__
++ (void(^)(void)) blockWithRunnable:(jobject)runnable withEnv:(JNIEnv *)env;
+#endif
+@end
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFRunnable.m b/src/JavaNativeFoundation/JNFRunnable.m
new file mode 100644
index 0000000..7df6249
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFRunnable.m
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFRunnable.h"
+#import "JNFThread.h"
+#import "JNFJObjectWrapper.h"
+
+
+static JNF_CLASS_CACHE(jc_Runnable, "java/lang/Runnable");
+static JNF_MEMBER_CACHE(jm_run, jc_Runnable, "run", "()V");
+
+@interface JNFRunnableWrapper : JNFJObjectWrapper { }
+- (void) invokeRunnable;
+@end
+
+@implementation JNFRunnableWrapper
+
+- (void) invokeRunnable {
+ JNFThreadContext ctx = JNFThreadDetachOnThreadDeath | JNFThreadSetSystemClassLoaderOnAttach | JNFThreadAttachAsDaemon;
+ JNIEnv *env = JNFObtainEnv(&ctx);
+ JNFCallVoidMethod(env, [self jObjectWithEnv:env], jm_run);
+ JNFReleaseEnv(env, &ctx);
+}
+
+@end
+
+
+@implementation JNFRunnable
+
++ (NSInvocation *) invocationWithRunnable:(jobject)runnable withEnv:(JNIEnv *)env {
+ SEL sel = @selector(invokeRunnable);
+ NSMethodSignature *sig = [JNFRunnableWrapper instanceMethodSignatureForSelector:sel];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
+ [invocation retainArguments];
+ [invocation setSelector:sel];
+
+ JNFRunnableWrapper *runnableWrapper = [[JNFRunnableWrapper alloc] initWithJObject:runnable withEnv:env];
+ [invocation setTarget:runnableWrapper];
+ [runnableWrapper release];
+
+ return invocation;
+}
+
+#if __BLOCKS__
++ (void(^)(void)) blockWithRunnable:(jobject)runnable withEnv:(JNIEnv *)env {
+ JNFJObjectWrapper *runnableWrapper = [JNFJObjectWrapper wrapperWithJObject:runnable withEnv:env];
+
+ return [[^() {
+ JNFThreadContext ctx = JNFThreadDetachOnThreadDeath | JNFThreadSetSystemClassLoaderOnAttach | JNFThreadAttachAsDaemon;
+ JNIEnv *_block_local_env = JNFObtainEnv(&ctx);
+ JNFCallVoidMethod(env, [runnableWrapper jObjectWithEnv:_block_local_env], jm_run);
+ JNFReleaseEnv(_block_local_env, &ctx);
+ } copy] autorelease];
+}
+#endif
+
+@end
diff --git a/src/JavaNativeFoundation/JNFString.h b/src/JavaNativeFoundation/JNFString.h
new file mode 100644
index 0000000..6a8ba6d
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFString.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Functions that create NSStrings, UTF16 unichars, or UTF8 chars from java.lang.Strings
+ */
+
+#import
+
+#import
+
+__BEGIN_DECLS
+
+// Returns an NSString given a java.lang.String object
+// NOTE: Return value is auto-released, so if you need to hang on to it, you should retain it.
+JNF_EXPORT extern NSString *JNFJavaToNSString(JNIEnv *env, jstring javaString);
+
+// Returns a java.lang.String object as a JNI local ref.
+// NOTE: This returns a JNI Local Ref. Any code that calls this should call DeleteLocalRef with the return value.
+JNF_EXPORT extern jstring JNFNSToJavaString(JNIEnv *env, NSString *nsString);
+
+/*
+ * Gets UTF16 unichars from a Java string, and checks for errors and raises a JNFException if
+ * the unichars cannot be obtained from Java.
+ */
+JNF_EXPORT extern const unichar *JNFGetStringUTF16UniChars(JNIEnv *env, jstring javaString);
+
+/*
+ * Releases the unichars obtained from JNFGetStringUTF16UniChars()
+ */
+JNF_EXPORT extern void JNFReleaseStringUTF16UniChars(JNIEnv *env, jstring javaString, const unichar *unichars);
+
+/*
+ * Gets UTF8 chars from a Java string, and checks for errors and raises a JNFException if
+ * the chars cannot be obtained from Java.
+ */
+JNF_EXPORT extern const char *JNFGetStringUTF8Chars(JNIEnv *env, jstring javaString);
+
+/*
+ * Releases the chars obtained from JNFGetStringUTF8Chars()
+ */
+JNF_EXPORT extern void JNFReleaseStringUTF8Chars(JNIEnv *env, jstring javaString, const char *chars);
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFString.m b/src/JavaNativeFoundation/JNFString.m
new file mode 100644
index 0000000..db6d465
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFString.m
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFString.h"
+
+#import "JNFJNI.h"
+#import "JNFAssert.h"
+#import "debug.h"
+
+#define STACK_BUFFER_SIZE 64
+
+/*
+ * Utility function to convert java String to NSString. We don't go through intermediate cString
+ * representation, since we are trying to preserve unicode characters from Java to NSString.
+ */
+NSString *JNFJavaToNSString(JNIEnv *env, jstring javaString)
+{
+ // We try very hard to only allocate and memcopy once.
+ if (javaString == NULL) return nil;
+
+ jsize length = (*env)->GetStringLength(env, javaString);
+ unichar *buffer = (unichar *)calloc((size_t)length, sizeof(unichar));
+ (*env)->GetStringRegion(env, javaString, 0, length, buffer);
+ NSString *str = (NSString *)CFStringCreateWithCharactersNoCopy(NULL, buffer, length, kCFAllocatorMalloc);
+ // NSLog(@"%@", str);
+ return [(NSString *)CFMakeCollectable(str) autorelease];
+}
+
+/*
+ * Utility function to convert NSString to Java string. We don't go through intermediate cString
+ * representation, since we are trying to preserve unicode characters in translation.
+ */
+jstring JNFNSToJavaString(JNIEnv *env, NSString *nsString)
+{
+ jstring res = nil;
+ if (nsString == nil) return NULL;
+
+ unsigned long length = [nsString length];
+ unichar *buffer;
+ unichar stackBuffer[STACK_BUFFER_SIZE];
+ if (length > STACK_BUFFER_SIZE) {
+ buffer = (unichar *)calloc(length, sizeof(unichar));
+ } else {
+ buffer = stackBuffer;
+ }
+
+ JNF_ASSERT_COND(buffer != NULL);
+ [nsString getCharacters:buffer];
+ res = (*env)->NewString(env, buffer, (jsize)length);
+ if (buffer != stackBuffer) free(buffer);
+ return res;
+}
+
+const unichar *JNFGetStringUTF16UniChars(JNIEnv *env, jstring javaString)
+{
+ const jchar *unichars = NULL;
+ JNF_ASSERT_COND(javaString != NULL);
+ unichars = (*env)->GetStringChars(env, javaString, NULL);
+ if (unichars == NULL) [JNFException raise:env as:kNullPointerException reason:"unable to obtain characters from GetStringChars"];
+ return (const unichar *)unichars;
+}
+
+void JNFReleaseStringUTF16UniChars(JNIEnv *env, jstring javaString, const unichar *unichars)
+{
+ JNF_ASSERT_COND(unichars != NULL);
+ (*env)->ReleaseStringChars(env, javaString, (const jchar *)unichars);
+}
+
+const char *JNFGetStringUTF8Chars(JNIEnv *env, jstring javaString)
+{
+ const char *chars = NULL;
+ JNF_ASSERT_COND(javaString != NULL);
+ chars = (*env)->GetStringUTFChars(env, javaString, NULL);
+ if (chars == NULL) [JNFException raise:env as:kNullPointerException reason:"unable to obtain characters from GetStringUTFChars"];
+ return chars;
+}
+
+void JNFReleaseStringUTF8Chars(JNIEnv *env, jstring javaString, const char *chars)
+{
+ JNF_ASSERT_COND(chars != NULL);
+ (*env)->ReleaseStringUTFChars(env, javaString, (const char *)chars);
+}
diff --git a/src/JavaNativeFoundation/JNFThread.h b/src/JavaNativeFoundation/JNFThread.h
new file mode 100644
index 0000000..f50f801
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFThread.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Functions to help obtain a JNIEnv pointer in places where one cannot be passed
+ * though (callbacks, catagory functions, etc). Use sparingly.
+ */
+
+#import
+#import
+
+__BEGIN_DECLS
+
+// Options only apply if thread was not already attached to the JVM.
+enum {
+ JNFThreadDetachImmediately = (1 << 1),
+ JNFThreadDetachOnThreadDeath = (1 << 2),
+ JNFThreadSetSystemClassLoaderOnAttach = (1 << 3),
+ JNFThreadAttachAsDaemon = (1 << 4)
+};
+
+typedef jlong JNFThreadContext;
+
+
+/*
+ * Attaches the current thread to the Java VM if needed, and obtains a JNI environment
+ * to interact with the VM. Use a provided JNIEnv pointer for your current thread
+ * whenever possible, since this method is particularly expensive to the Java VM if
+ * used repeatedly.
+ *
+ * Provide a pointer to a JNFThreadContext to pass to JNFReleaseEnv().
+ */
+JNF_EXPORT extern JNIEnv *JNFObtainEnv(JNFThreadContext *context) API_DEPRECATED("This functionality is no longer supported and may stop working in a future version of macOS.", macos(10.10, 10.16));
+
+/*
+ * Release the JNIEnv for this thread, and detaches the current thread from the VM if
+ * it was not already attached.
+ */
+JNF_EXPORT extern void JNFReleaseEnv(JNIEnv *env, JNFThreadContext *context) API_DEPRECATED("This functionality is no longer supported and may stop working in a future version of macOS.", macos(10.10, 10.16));
+
+
+#if __BLOCKS__
+
+/*
+ * Performs the same attach/detach as JNFObtainEnv() and JNFReleaseEnv(), but executes a
+ * block that accepts the obtained JNIEnv.
+ */
+typedef void (^JNIEnvBlock)(JNIEnv *);
+JNF_EXPORT extern void JNFPerformEnvBlock(JNFThreadContext context, JNIEnvBlock block) API_DEPRECATED("This functionality is no longer supported and may stop working in a future version of macOS.", macos(10.10, 10.16));
+
+#endif
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFThread.m b/src/JavaNativeFoundation/JNFThread.m
new file mode 100644
index 0000000..c5feb27
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFThread.m
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFThread.h"
+
+#import
+#import
+
+static JavaVM *GetGlobalVM() { // obtains a connection to the current VM
+ static JavaVM *globalVM;
+
+ if (globalVM != NULL) {
+ return globalVM;
+ }
+
+ void *jvmHandle = dlopen("@rpath/libjvm.dylib", RTLD_NOW);
+ if (!jvmHandle) {
+ NSLog(@"JavaNativeFoundation: %s: Failed to locate @rpath/libjvm.dylib for JNI_GetCreatedJavaVMs(). A JVM must be loaded before calling this function.", __FUNCTION__);
+ return NULL;
+ }
+
+ jint (*_JNI_GetCreatedJavaVMs)(JavaVM **, jsize, jsize *) = dlsym(jvmHandle, "JNI_GetCreatedJavaVMs");
+ if (!_JNI_GetCreatedJavaVMs) {
+ NSLog(@"JavaNativeFoundation: %s: Failed to locate JNI_GetCreatedJavaVMs symbol in @rpath/libjvm.dylib", __FUNCTION__);
+ return NULL;
+ }
+
+ JavaVM *vmArray;
+ jsize numVMs = 0;
+ if (_JNI_GetCreatedJavaVMs(&vmArray, 1, &numVMs) == 0 && numVMs >= 1) {
+ globalVM = &vmArray[0];
+ }
+
+ if (globalVM == NULL) {
+ NSLog(@"JavaNativeFoundation: %s: JNI_GetCreatedJavaVMs() failed to get any VM.", __FUNCTION__);
+ return NULL;
+ }
+
+ return globalVM;
+}
+
+// private marker to indicate if we need to detach on release
+enum {
+ JNFThreadWillDetachOnRelease = (1 << 12)
+};
+
+static void setSystemClassLoader(JNIEnv *env) {
+ // setup the context class loader for this new thread coming into the JVM
+ JNF_CLASS_CACHE(jc_Thread, "java/lang/Thread");
+ JNF_STATIC_MEMBER_CACHE(jm_currentThread, jc_Thread, "currentThread", "()Ljava/lang/Thread;");
+ jobject currentThread = JNFCallStaticObjectMethod(env, jm_currentThread);
+
+ JNF_CLASS_CACHE(jc_ClassLoader, "java/lang/ClassLoader");
+ JNF_STATIC_MEMBER_CACHE(jm_getSystemClassLoader, jc_ClassLoader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
+ jobject systemClassLoader = JNFCallStaticObjectMethod(env, jm_getSystemClassLoader);
+
+ JNF_MEMBER_CACHE(jm_setContextClassLoader, jc_Thread, "setContextClassLoader", "(Ljava/lang/ClassLoader;)V");
+ JNFCallVoidMethod(env, currentThread, jm_setContextClassLoader, systemClassLoader);
+}
+
+static JNFThreadContext GetEnvUsingJVM(JavaVM *jvm, void **envPtr, BOOL shouldDetachOnRelease, BOOL setClassLoader, BOOL attachAsDaemon) {
+ jint status = (*jvm)->GetEnv(jvm, envPtr, JNI_VERSION_1_4);
+ if (status == JNI_OK) {
+ // common path
+ return 0;
+ }
+
+ if (status != JNI_EDETACHED) {
+ // can't use JNF_ASSERT macros, since we don't really know if we have an env :(
+ NSLog(@"JavaNativeFoundation: JNFObtainEnv unable to obtain JNIEnv (%d)", (int)status);
+ return 0;
+ }
+
+ // we need to attach
+ if (attachAsDaemon) {
+ status = (*jvm)->AttachCurrentThreadAsDaemon(jvm, envPtr, NULL);
+ } else {
+ status = (*jvm)->AttachCurrentThread(jvm, envPtr, NULL);
+ }
+
+ if (status != JNI_OK) {
+ // failed - need to clear our mark to detach, if present
+ return 0;
+ }
+
+ if (setClassLoader) {
+ setSystemClassLoader((JNIEnv *)(*envPtr));
+ }
+
+ // by default, we detach at pthread death, but if requested, we will detach on env-release
+ if (shouldDetachOnRelease) {
+ return JNFThreadWillDetachOnRelease;
+ }
+
+ // we don't do anything in this case, because HotSpot on Mac OS X will detach for us.
+ // Can we install a pthread_atexit handler to detach if we haven't already?
+ return 0;
+}
+
+// public call to obtain an env, and attach the current thread to the VM in needed
+JNIEnv *JNFObtainEnv(JNFThreadContext *context) {
+ JavaVM *jvm = GetGlobalVM();
+ if (!jvm) {
+ *context = 0;
+ return NULL;
+ }
+
+ JNFThreadContext ctx = *context;
+ BOOL shouldDetachOnRelease = (0 != (ctx & JNFThreadDetachImmediately));
+ BOOL setClassLoader = (0 != (ctx & JNFThreadSetSystemClassLoaderOnAttach));
+ BOOL attachAsDaemon = (0 != (ctx & JNFThreadAttachAsDaemon));
+
+ void *env = NULL;
+ *context = GetEnvUsingJVM(jvm, &env, shouldDetachOnRelease, setClassLoader, attachAsDaemon);
+ return (JNIEnv *)env;
+}
+
+void JNFReleaseEnv(__unused JNIEnv *env, JNFThreadContext *context) {
+ if ((*context & JNFThreadWillDetachOnRelease) == 0) {
+ return;
+ }
+
+ JavaVM *jvm = GetGlobalVM();
+ if (!jvm) return;
+
+ jint status = (*jvm)->DetachCurrentThread(jvm);
+ if (status != JNI_OK) {
+ // can't use JNF_ASSERT macros, since we don't really know if we have an env :(
+ NSLog(@"JavaNativeFoundation: %s: unable to release JNIEnv (%d)", __FUNCTION__, (int)status);
+ }
+}
+
+
+#if __BLOCKS__
+
+JNF_EXPORT extern void JNFPerformEnvBlock(JNFThreadContext context, JNIEnvBlock block) {
+ JNIEnv *env = JNFObtainEnv(&context);
+ if (env == NULL) [NSException raise:@"Unable to obtain JNIEnv" format:@"Unable to obtain JNIEnv for context: %p", (void *)context];
+
+ @try {
+ block(env);
+ } @finally {
+ JNFReleaseEnv(env, &context);
+ }
+}
+
+#endif
diff --git a/src/JavaNativeFoundation/JNFTypeCoercion.h b/src/JavaNativeFoundation/JNFTypeCoercion.h
new file mode 100644
index 0000000..4dbaa99
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFTypeCoercion.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Type Coercion system that translates between Java VM objects and Objective-C Foundation objects.
+ *
+ * JNFTypeCoercions are registered into JNFTypeCoercers, which can be chained to other
+ * JNFTypeCoercers using -deriveCoercer or -initWithParent. If the set of Coercions
+ * in a Coercer aren't capable of converting an object, the Coercer will delegate up to
+ * it's parent.
+ *
+ * Coercions are registered by Objective-C class and Java class name. If an object is an
+ * instance of the registered class name, the coercion will be invoked. Default
+ * implementations for several basic types are provided by JNFDefaultCoercions, and can
+ * be installed in any order. More specific coercions should be placed farther down
+ * a coercer chain, and more generic coercions should be placed higher. A Coercer can be
+ * initialized with a basic Coercion that may want to handle "all cases", like calling
+ * Object.toString() and -describe on all objects passed to it.
+ *
+ * Coercions are passed the Coercion-object that was originally invoked on the
+ * target object. This permits the lowest level Coercion to be used for subsequent
+ * object translations for composite objects. The provided List, Map, and Set Coercions
+ * only handle object hierarchies, and will infinitely recurse if confronted with a
+ * cycle in the object graph.
+ *
+ * Null and nil are both perfectly valid return types for Coercions, and do not indicate
+ * a failure to coerce an object. Coercers are not thread safe.
+ */
+
+#import
+#import
+
+__BEGIN_DECLS
+
+@class JNFTypeCoercion;
+
+JNF_EXPORT
+@protocol JNFTypeCoercion
+
+- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer;
+- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer;
+
+@end
+
+
+JNF_EXPORT
+@interface JNFTypeCoercer : NSObject
+
+- (id) init;
+- (id) initWithParent:(NSObject *)parentIn;
+- (JNFTypeCoercer *) deriveCoercer;
+- (void) addCoercion:(NSObject *)coercion forNSClass:(Class)nsClass javaClass:(NSString *)javaClassName;
+
+- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env;
+- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env;
+
+@end
+
+
+JNF_EXPORT
+@interface JNFDefaultCoercions : NSObject { }
+
++ (void) addStringCoercionTo:(JNFTypeCoercer *)coercer;
++ (void) addNumberCoercionTo:(JNFTypeCoercer *)coercer;
++ (void) addDateCoercionTo:(JNFTypeCoercer *)coercer;
++ (void) addListCoercionTo:(JNFTypeCoercer *)coercer;
++ (void) addMapCoercionTo:(JNFTypeCoercer *)coercer;
++ (void) addSetCoercionTo:(JNFTypeCoercer *)coercer;
+
++ (JNFTypeCoercer *) defaultCoercer; // returns autoreleased copy, not shared, not thread safe
+
+@end
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/JNFTypeCoercion.m b/src/JavaNativeFoundation/JNFTypeCoercion.m
new file mode 100644
index 0000000..8eae113
--- /dev/null
+++ b/src/JavaNativeFoundation/JNFTypeCoercion.m
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "JNFTypeCoercion.h"
+
+#import "JNFJNI.h"
+#import "JNFObject.h"
+#import "JNFString.h"
+#import "JNFNumber.h"
+#import "JNFDate.h"
+#import "JNFJObjectWrapper.h"
+
+// #define DEBUG 1
+
+@interface JNFInternalJavaClassToCoersionHolder : JNFJObjectWrapper
+
+- (id) initWithCoercion:(NSObject *)coercionIn className:(NSString *)className withEnv:(JNIEnv *)env;
+- (BOOL) isClassFor:(jobject)obj withEnv:(JNIEnv *)env;
+- (NSObject *)coercion;
+
+@property (nonatomic, readwrite, strong) NSObject *coercion;
+
+@end
+
+@interface JNFTypeCoercer ()
+
+@property (nonatomic, readwrite, strong) NSObject *parent;
+@property (nonatomic, readwrite, strong) NSMutableDictionary *nsClassToCoercion;
+@property (nonatomic, readwrite, strong) NSMutableDictionary *javaClassNameToCoercion;
+@property (nonatomic, readwrite, strong) NSArray *javaClassToCoercion;
+
+@end
+
+@implementation JNFTypeCoercer
+
+- (id) init {
+ return [self initWithParent:nil];
+}
+
+- (id) initWithParent:(NSObject *)parentIn {
+ self = [super init];
+ if (!self) return self;
+
+ self.parent = parentIn;
+ self.nsClassToCoercion = [NSMutableDictionary dictionary];
+ self.javaClassNameToCoercion = [NSMutableDictionary dictionary];
+ self.javaClassToCoercion = nil;
+
+ return self;
+}
+
+- (void) dealloc {
+ self.parent = nil;
+ self.nsClassToCoercion = nil;
+ self.javaClassNameToCoercion = nil;
+ self.javaClassToCoercion = nil;
+
+ [super dealloc];
+}
+
+- (JNFTypeCoercer *) deriveCoercer {
+ return [[[JNFTypeCoercer alloc] initWithParent:self] autorelease];
+}
+
+- (void) addCoercion:(NSObject *)coercion forNSClass:(Class)nsClass javaClass:(NSString *)javaClassName {
+ if (nsClass) [self.nsClassToCoercion setObject:coercion forKey:(id)nsClass];
+ if (javaClassName) [self.javaClassNameToCoercion setObject:coercion forKey:javaClassName];
+ self.javaClassToCoercion = nil; // invalidate Java Class cache
+}
+
+- (NSObject *) findCoercerForNSObject:(id)obj {
+ NSMutableDictionary * const nsClassToCoercion = self.nsClassToCoercion;
+ NSObject *coercer = [nsClassToCoercion objectForKey:[obj class]];
+ if (coercer) return coercer;
+
+ NSEnumerator *classes = [nsClassToCoercion keyEnumerator];
+ Class clazzIter;
+ while ((clazzIter = [classes nextObject]) != nil) {
+ if ([obj isKindOfClass:clazzIter]) {
+ return [nsClassToCoercion objectForKey:clazzIter];
+ }
+ }
+
+ return self.parent;
+}
+
+- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
+ return [[self findCoercerForNSObject:obj] coerceNSObject:obj withEnv:env usingCoercer:coercer];
+}
+
+- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env {
+ return [self coerceNSObject:obj withEnv:env usingCoercer:(JNFTypeCoercion *)self];
+}
+
+
+- (NSArray *) javaClassCacheUsingEnv:(JNIEnv *)env {
+ NSArray * const javaClassToCoercion = self.javaClassToCoercion;
+ if (javaClassToCoercion) return javaClassToCoercion;
+
+ NSMutableArray *array = [[NSMutableArray alloc] init];
+
+ NSMutableDictionary * const javaClassNameToCoercion = self.javaClassNameToCoercion;
+ NSEnumerator *classNames = [javaClassNameToCoercion keyEnumerator];
+ NSString *className;
+ while ((className = [classNames nextObject]) != nil) {
+ NSObject *coercion = [javaClassNameToCoercion objectForKey:className];
+ JNFInternalJavaClassToCoersionHolder *holder = [[JNFInternalJavaClassToCoersionHolder alloc] initWithCoercion:coercion className:className withEnv:env];
+ [array addObject:holder];
+ [holder release];
+ }
+
+ self.javaClassToCoercion = array;
+ return [array autorelease];
+}
+
+- (NSObject *) findCoercerForJavaObject:(jobject)obj withEnv:(JNIEnv *)env {
+ if (obj == NULL) return nil;
+
+ NSMutableDictionary * const javaClassNameToCoercion = self.javaClassNameToCoercion;
+ NSString *javaClazzName = JNFObjectClassName(env, obj);
+ NSObject *coercer = [javaClassNameToCoercion objectForKey:javaClazzName];
+ if (coercer) return coercer;
+
+#ifdef DEBUG
+ NSLog(@"attempting to find coercer for: %@", javaClazzName);
+#endif
+
+ NSArray *javaClassCache = [self javaClassCacheUsingEnv:env];
+ NSEnumerator *holderIter = [javaClassCache objectEnumerator];
+ JNFInternalJavaClassToCoersionHolder *holder;
+ while ((holder = [holderIter nextObject]) != nil) {
+ if ([holder isClassFor:obj withEnv:env]) {
+ NSObject *coercion = [holder coercion];
+ [javaClassNameToCoercion setObject:coercion forKey:javaClazzName];
+ return coercion;
+ }
+ }
+
+ return self.parent;
+}
+
+- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
+ NSObject *coercion = [self findCoercerForJavaObject:obj withEnv:env];
+ id nsObj = [coercion coerceJavaObject:obj withEnv:env usingCoercer:coercer];
+ if (nsObj == nil) return [NSNull null];
+ return nsObj;
+}
+
+- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env {
+ return [self coerceJavaObject:obj withEnv:env usingCoercer:(JNFTypeCoercion *)self];
+}
+
+@end
+
+
+@implementation JNFInternalJavaClassToCoersionHolder
+
+- (id) initWithCoercion:(NSObject *)coercionIn className:(NSString *)className withEnv:(JNIEnv *)env {
+ const char *classNameCStr = [className cStringUsingEncoding:NSUTF8StringEncoding];
+ jclass clz = (*env)->FindClass(env, classNameCStr);
+ if (clz == NULL) [JNFException raise:env as:kClassNotFoundException reason:"Unable to create type converter."];
+
+ self = [super initWithJObject:clz withEnv:env];
+ if (!self) return self;
+
+ self.coercion = coercionIn;
+ return self;
+}
+
+- (BOOL)isEqual:(id)object {
+ jclass javaClazz = [self jObject];
+
+ if ([object isKindOfClass:[self class]]) {
+ return [((JNFInternalJavaClassToCoersionHolder *)object) jObject] == javaClazz;
+ }
+
+ return NO;
+}
+
+- (void) dealloc {
+ self.coercion = nil;
+ [super dealloc];
+}
+
+- (NSUInteger)hash {
+ jclass javaClazz = [self jObject];
+ return (NSUInteger)ptr_to_jlong(javaClazz);
+}
+
+- (BOOL) isClassFor:(jobject)obj withEnv:(JNIEnv *)env {
+ if (obj == NULL) return JNI_FALSE;
+#ifdef DEBUG
+ NSLog(@"is (%@(%@)) a kind of (%@)?", JNFToString(env, obj), JNFClassName(env, obj), JNFToString(env, javaClazz));
+#endif
+
+ jclass javaClazz = [self jObject];
+ return (BOOL)(*env)->IsInstanceOf(env, obj, javaClazz);
+}
+
+@end
+
+
+@interface JNFStringCoercion : NSObject { }
+@end
+
+@implementation JNFStringCoercion
+
+- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(__unused JNFTypeCoercion *)coercer {
+ return JNFNSToJavaString(env, (NSString *)obj);
+}
+
+- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(__unused JNFTypeCoercion *)coercer {
+ return JNFJavaToNSString(env, (jstring)obj);
+}
+
+@end
+
+
+@interface JNFNumberCoercion : NSObject { }
+@end
+
+@implementation JNFNumberCoercion
+
+- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(__unused JNFTypeCoercion *)coercer {
+ return JNFNSToJavaNumber(env, (NSNumber *)obj);
+}
+
+- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(__unused JNFTypeCoercion *)coercer {
+ return JNFJavaToNSNumber(env, obj);
+}
+
+@end
+
+
+@interface JNFDateCoercion : NSObject { }
+@end
+
+@implementation JNFDateCoercion
+
+- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(__unused JNFTypeCoercion *)coercer {
+ return JNFNSToJavaCalendar(env, (NSDate *)obj);
+}
+
+- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(__unused JNFTypeCoercion *)coercer {
+ return JNFJavaToNSDate(env, obj);
+}
+
+@end
+
+
+static JNF_CLASS_CACHE(jc_Iterator, "java/util/Iterator");
+static JNF_MEMBER_CACHE(jm_Iterator_hasNext, jc_Iterator, "hasNext", "()Z");
+static JNF_MEMBER_CACHE(jm_Iterator_next, jc_Iterator, "next", "()Ljava/lang/Object;");
+
+@interface JNFMapCoercion : NSObject { }
+@end
+
+@implementation JNFMapCoercion
+
+- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
+ static JNF_CLASS_CACHE(jc_HashMap, "java/util/HashMap");
+ static JNF_CTOR_CACHE(jm_HashMap_ctor, jc_HashMap, "()V");
+ static JNF_MEMBER_CACHE(jm_HashMap_put, jc_HashMap, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+ NSDictionary *nsDict = (NSDictionary *)obj;
+ NSEnumerator *keyEnum = [nsDict keyEnumerator];
+
+ jobject jHashMap = JNFNewObject(env, jm_HashMap_ctor);
+
+ id key;
+ while ((key = [keyEnum nextObject]) != nil) {
+ jobject jkey = [coercer coerceNSObject:key withEnv:env usingCoercer:coercer];
+
+ id value = [nsDict objectForKey:key];
+ jobject java_value = [coercer coerceNSObject:value withEnv:env usingCoercer:coercer];
+
+ JNFCallObjectMethod(env, jHashMap, jm_HashMap_put, jkey, java_value);
+
+ if (jkey != NULL) (*env)->DeleteLocalRef(env, jkey);
+ if (java_value != NULL) (*env)->DeleteLocalRef(env, java_value);
+ }
+
+ return jHashMap;
+}
+
+- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
+ static JNF_CLASS_CACHE(jc_Map, "java/util/Map");
+ static JNF_MEMBER_CACHE(jm_Map_keySet, jc_Map, "keySet", "()Ljava/util/Set;");
+ static JNF_MEMBER_CACHE(jm_Map_get, jc_Map, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+
+ static JNF_CLASS_CACHE(jc_Set, "java/util/Set");
+ static JNF_MEMBER_CACHE(jm_Set_iterator, jc_Set, "iterator", "()Ljava/util/Iterator;");
+
+ NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
+ jobject jKeySet = JNFCallObjectMethod(env, obj, jm_Map_keySet);
+ jobject jKeyIter = JNFCallObjectMethod(env, jKeySet, jm_Set_iterator);
+ if (jKeySet != NULL) (*env)->DeleteLocalRef(env, jKeySet);
+
+ while (JNFCallBooleanMethod(env, jKeyIter, jm_Iterator_hasNext)) {
+ jobject jkey = JNFCallObjectMethod(env, jKeyIter, jm_Iterator_next);
+ id nsKey = [coercer coerceJavaObject:jkey withEnv:env usingCoercer:coercer];
+
+ jobject java_value = JNFCallObjectMethod(env, obj, jm_Map_get, jkey);
+ if (jkey != NULL) (*env)->DeleteLocalRef(env, jkey);
+
+ id nsValue = [coercer coerceJavaObject:java_value withEnv:env usingCoercer:coercer];
+ if (java_value != NULL) (*env)->DeleteLocalRef(env, java_value);
+
+ [dict setObject:nsValue forKey:nsKey];
+ }
+
+ return [dict autorelease];
+}
+
+@end
+
+
+@interface JNFListCoercion : NSObject { }
+@end
+
+@implementation JNFListCoercion
+
+- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
+ static JNF_CLASS_CACHE(jc_List, "java/util/ArrayList");
+ static JNF_CTOR_CACHE(jm_List_ctor, jc_List, "(I)V");
+ static JNF_MEMBER_CACHE(jm_List_add, jc_List, "add", "(Ljava/lang/Object;)Z");
+
+ NSArray *nsArray = (NSArray *)obj;
+ unsigned long count = [nsArray count];
+
+ jobject javaArray = JNFNewObject(env, jm_List_ctor, (jint)count);
+
+ unsigned long i;
+ for (i = 0; i < count; i++) {
+ id iThObj = [nsArray objectAtIndex:i];
+ jobject iThJObj = [coercer coerceNSObject:iThObj withEnv:env usingCoercer:coercer];
+ JNFCallBooleanMethod(env, javaArray, jm_List_add, iThJObj);
+ if (iThJObj != NULL) (*env)->DeleteLocalRef(env, iThJObj);
+ }
+
+ return javaArray;
+}
+
+- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
+ static JNF_CLASS_CACHE(jc_List, "java/util/List");
+ static JNF_MEMBER_CACHE(jm_List_iterator, jc_List, "iterator", "()Ljava/util/Iterator;");
+
+ jobject jIterator = JNFCallObjectMethod(env, obj, jm_List_iterator);
+
+ NSMutableArray *nsArray = [[NSMutableArray alloc] init];
+ while (JNFCallBooleanMethod(env, jIterator, jm_Iterator_hasNext)) {
+ jobject jobj = JNFCallObjectMethod(env, jIterator, jm_Iterator_next);
+ id nsObj = [coercer coerceJavaObject:jobj withEnv:env usingCoercer:coercer];
+ if (jobj != NULL) (*env)->DeleteLocalRef(env, jobj);
+ [nsArray addObject:nsObj];
+ }
+
+ return [nsArray autorelease];
+}
+
+@end
+
+
+@interface JNFSetCoercion : NSObject { }
+@end
+
+@implementation JNFSetCoercion
+
+- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
+ static JNF_CLASS_CACHE(jc_Set, "java/util/HashSet");
+ static JNF_CTOR_CACHE(jm_Set_ctor, jc_Set, "()V");
+ static JNF_MEMBER_CACHE(jm_Set_add, jc_Set, "add", "(Ljava/lang/Object;)Z");
+
+ NSSet *nsSet = (NSSet *)obj;
+ NSEnumerator *enumerator = [nsSet objectEnumerator];
+
+ jobject javaSet = JNFNewObject(env, jm_Set_ctor);
+ id next;
+ while ((next = [enumerator nextObject]) != nil) {
+ jobject jnext = [coercer coerceNSObject:next withEnv:env usingCoercer:coercer];
+ if (jnext != NULL) {
+ JNFCallBooleanMethod(env, javaSet, jm_Set_add, jnext);
+ (*env)->DeleteLocalRef(env, jnext);
+ }
+ }
+
+ return javaSet;
+}
+
+- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
+ static JNF_CLASS_CACHE(jc_Set, "java/util/Set");
+ static JNF_MEMBER_CACHE(jm_Set_iterator, jc_Set, "iterator", "()Ljava/util/Iterator;");
+
+ jobject jIterator = JNFCallObjectMethod(env, obj, jm_Set_iterator);
+
+ NSMutableSet *nsSet = [[NSMutableSet alloc] init];
+ while (JNFCallBooleanMethod(env, jIterator, jm_Iterator_hasNext)) {
+ jobject jobj = JNFCallObjectMethod(env, jIterator, jm_Iterator_next);
+ if (jobj != NULL) {
+ id nsObj = [coercer coerceJavaObject:jobj withEnv:env usingCoercer:coercer];
+ (*env)->DeleteLocalRef(env, jobj);
+ [nsSet addObject:nsObj];
+ }
+ }
+
+ return [nsSet autorelease];
+}
+
+@end
+
+
+@implementation JNFDefaultCoercions
+
++ (void) addStringCoercionTo:(JNFTypeCoercer *)coercer {
+ [coercer addCoercion:[[[JNFStringCoercion alloc] init] autorelease] forNSClass:[NSString class] javaClass:@"java/lang/String"];
+}
+
++ (void) addNumberCoercionTo:(JNFTypeCoercer *)coercer {
+ [coercer addCoercion:[[[JNFNumberCoercion alloc] init] autorelease] forNSClass:[NSNumber class] javaClass:@"java/lang/Number"];
+}
+
++ (void) addDateCoercionTo:(JNFTypeCoercer *)coercer {
+ id dateCoercion = [[[JNFDateCoercion alloc] init] autorelease];
+ [coercer addCoercion:dateCoercion forNSClass:[NSDate class] javaClass:@"java/util/Calendar"];
+ [coercer addCoercion:dateCoercion forNSClass:[NSDate class] javaClass:@"java/util/Date"];
+}
+
++ (void) addListCoercionTo:(JNFTypeCoercer *)coercer {
+ [coercer addCoercion:[[[JNFListCoercion alloc] init] autorelease] forNSClass:[NSArray class] javaClass:@"java/util/List"];
+}
+
++ (void) addMapCoercionTo:(JNFTypeCoercer *)coercer {
+ [coercer addCoercion:[[[JNFMapCoercion alloc] init] autorelease] forNSClass:[NSDictionary class] javaClass:@"java/util/Map"];
+}
+
++ (void) addSetCoercionTo:(JNFTypeCoercer *)coercer {
+ [coercer addCoercion:[[[JNFSetCoercion alloc] init] autorelease] forNSClass:[NSSet class] javaClass:@"java/util/Set"];
+}
+
++ (JNFTypeCoercer *) defaultCoercer {
+ JNFTypeCoercer *coercer = [[[JNFTypeCoercer alloc] initWithParent:nil] autorelease];
+
+ [JNFDefaultCoercions addStringCoercionTo:coercer];
+ [JNFDefaultCoercions addNumberCoercionTo:coercer];
+ [JNFDefaultCoercions addDateCoercionTo:coercer];
+ [JNFDefaultCoercions addListCoercionTo:coercer];
+ [JNFDefaultCoercions addMapCoercionTo:coercer];
+ [JNFDefaultCoercions addSetCoercionTo:coercer];
+
+ return coercer;
+}
+
+@end
diff --git a/src/JavaNativeFoundation/JavaNativeFoundation-Info.plist b/src/JavaNativeFoundation/JavaNativeFoundation-Info.plist
new file mode 100644
index 0000000..3d57226
--- /dev/null
+++ b/src/JavaNativeFoundation/JavaNativeFoundation-Info.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ $(CURRENT_PROJECT_VERSION)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ NSHumanReadableCopyright
+ Copyright © 2008-2020 Apple Inc.\nAll Rights Reserved.
+
+
diff --git a/src/JavaNativeFoundation/JavaNativeFoundation.h b/src/JavaNativeFoundation/JavaNativeFoundation.h
new file mode 100644
index 0000000..e7e66d4
--- /dev/null
+++ b/src/JavaNativeFoundation/JavaNativeFoundation.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * This umbrella header should be included by all JNI/Cocoa source files
+ * for easy building. Use this file instead of importing individual JNF
+ * headers.
+ */
+
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
diff --git a/src/JavaNativeFoundation/Modules/module.modulemap b/src/JavaNativeFoundation/Modules/module.modulemap
new file mode 100644
index 0000000..bde3b31
--- /dev/null
+++ b/src/JavaNativeFoundation/Modules/module.modulemap
@@ -0,0 +1,5 @@
+framework module JavaNativeFoundation [extern_c] {
+ umbrella header "JavaNativeFoundation.h"
+ export *
+ module * { export * }
+}
diff --git a/src/JavaNativeFoundation/debug.h b/src/JavaNativeFoundation/debug.h
new file mode 100644
index 0000000..d967795
--- /dev/null
+++ b/src/JavaNativeFoundation/debug.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * --
+ *
+ * Internal functions to Java Native Foundation.
+ */
+
+#import
+#import
+
+__BEGIN_DECLS
+
+//
+// Debugging support
+//
+
+// prints a stack trace from the Java VM (can be called from gdb)
+void JNFJavaStackTrace(JNIEnv *env);
+
+// dump some info about a generic Java object.
+void JNFDumpJavaObject(JNIEnv *env, jobject obj);
+
+// prints a Java stack trace into a string
+NSString *JNFGetStackTraceAsNSString(JNIEnv *env, jthrowable throwable);
+
+__END_DECLS
diff --git a/src/JavaNativeFoundation/debug.m b/src/JavaNativeFoundation/debug.m
new file mode 100644
index 0000000..ab359fd
--- /dev/null
+++ b/src/JavaNativeFoundation/debug.m
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2008-2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "debug.h"
+
+#import "JNFAssert.h"
+#import "JNFObject.h"
+
+/*
+ * Utility function to print the Java stack backtrace.
+ * In gdb, if you have a value for the JNIEnv on the thread
+ * you want to trace, you can do a gdb "print" of this function
+ * to get the stack trace for the thread.
+ */
+void JNFJavaStackTrace(JNIEnv *env) {
+ jthrowable obj_javaException;
+ if ((obj_javaException = (*env)->ExceptionOccurred(env)) != NULL) (*env)->ExceptionClear(env);
+
+ jclass cls_Thread = (*env)->FindClass(env, "java/lang/Thread");
+ jmethodID mid_currentThread = (*env)->GetStaticMethodID(env, cls_Thread, "currentThread", "()Ljava/lang/Thread;");
+ jobject obj_currentThread = (*env)->CallStaticObjectMethod(env, cls_Thread, mid_currentThread);
+ jclass cls_currentThread = (*env)->GetObjectClass(env, obj_currentThread);
+ jmethodID mid_getName = (*env)->GetMethodID(env, cls_currentThread, "getName", "()Ljava/lang/String;");
+ jobject obj_threadName = (*env)->CallObjectMethod(env, obj_currentThread, mid_getName);
+ (*env)->DeleteLocalRef(env, obj_currentThread);
+
+ const char *threadName = (*env)->GetStringUTFChars(env, obj_threadName, NULL);
+ JNF_WARN("Stack trace from Java thread \"%s\":", threadName);
+ (*env)->ReleaseStringUTFChars(env, obj_threadName, threadName);
+ (*env)->DeleteLocalRef(env, obj_threadName);
+
+ jmethodID mid_dumpStack = (*env)->GetStaticMethodID(env, cls_Thread, "dumpStack", "()V");
+ (*env)->CallStaticVoidMethod(env, cls_Thread, mid_dumpStack);
+ (*env)->DeleteLocalRef(env, cls_Thread);
+
+ if (obj_javaException) (*env)->Throw(env, obj_javaException);
+}
+
+/*
+ * Utility function to dump some info about a generic Java object.
+ * To be called from gdb. Like JNFJavaStackTrace, you need to have
+ * a valid value for the JNIEnv to call this function.
+ */
+void JNFDumpJavaObject(JNIEnv *env, jobject obj) {
+ jthrowable obj_javaException;
+ if ((obj_javaException = (*env)->ExceptionOccurred(env)) != NULL) (*env)->ExceptionClear(env);
+
+ jclass cls_CToolkit = (*env)->FindClass(env, "apple/awt/CToolkit");
+ jmethodID mid_dumpObject = (*env)->GetStaticMethodID(env, cls_CToolkit, "dumpObject", "(Ljava/lang/Object;)V");
+ (*env)->CallStaticVoidMethod(env, cls_CToolkit, mid_dumpObject, obj);
+ (*env)->DeleteLocalRef(env, cls_CToolkit);
+
+ if (obj_javaException) (*env)->Throw(env, obj_javaException);
+}
+
+/*
+ * Utility function to print a Java stack trace into a string
+ */
+NSString *JNFGetStackTraceAsNSString(JNIEnv *env, jthrowable throwable) {
+ // Writer writer = new StringWriter();
+ JNF_CLASS_CACHE(jc_StringWriter, "java/io/StringWriter");
+ JNF_CTOR_CACHE(jct_StringWriter, jc_StringWriter, "()V");
+ jobject writer = JNFNewObject(env, jct_StringWriter);
+
+ // PrintWriter printWriter = new PrintWriter(writer);
+ JNF_CLASS_CACHE(jc_PrintWriter, "java/io/PrintWriter");
+ JNF_CTOR_CACHE(jct_PrintWriter, jc_PrintWriter, "(Ljava/io/Writer;)V");
+ jobject printWriter = JNFNewObject(env, jct_PrintWriter, writer);
+
+ // throwable.printStackTrace(printWriter);
+ JNF_CLASS_CACHE(jc_Throwable, "java/lang/Throwable");
+ JNF_MEMBER_CACHE(jm_printStackTrace, jc_Throwable, "printStackTrace", "(Ljava/io/PrintWriter;)V");
+ JNFCallVoidMethod(env, throwable, jm_printStackTrace, printWriter);
+ (*env)->DeleteLocalRef(env, printWriter);
+
+ // return writer.toString();
+ NSString *stackTraceAsString = JNFObjectToString(env, writer);
+ (*env)->DeleteLocalRef(env, writer);
+ return stackTraceAsString;
+}