diff --git a/.gitignore b/.gitignore index 11bbeceb..efff076b 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,7 @@ Package.resolved */**/*.o */**/*.swiftdeps */**/*.swiftdeps~ + +*/**/*.index-build +.index-build +.build-vscode diff --git a/Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift b/Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift index f074fee6..7e2e178b 100644 --- a/Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift +++ b/Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift @@ -21,6 +21,21 @@ enum SwiftWrappedError: Error { @JavaImplementation("com.example.swift.HelloSwift") extension HelloSwift: HelloSwiftNativeMethods { + + @JavaMethod + func compute(_ a: JavaInteger?, _ b: JavaInteger?) -> JavaInteger? { + guard let a else { fatalError("parameter 'a' must not be null!") } + guard let b else { fatalError("parameter 'b' must not be null!") } + + print("[swift] a = \(a.intValue())") + print("[swift] b = \(b.intValue())") + print("[swift] a + b = \(a.intValue() + b.intValue())") + let result = JavaInteger(a.intValue() + b.intValue()) + let x = JavaObjectHolder(object: result.javaThis, environment: try! JavaVirtualMachine.shared().environment()) + print("[swift] Integer(a + b) = \(result)") + return result + } + @JavaMethod func sayHello(_ i: Int32, _ j: Int32) -> Int32 { print("Hello from Swift!") diff --git a/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/HelloSwift.java b/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/HelloSwift.java index d7ea7f22..ea29587b 100644 --- a/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/HelloSwift.java +++ b/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/HelloSwift.java @@ -30,6 +30,9 @@ public HelloSwift() { } native int sayHello(int x, int y); + + native Integer compute(Integer x, Integer y); + native String throwMessageFromSwift(String message) throws Exception; // To be called back by the native code diff --git a/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/JavaKitSampleMain.java b/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/JavaKitSampleMain.java index 9036b7a5..3e85dc6a 100644 --- a/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/JavaKitSampleMain.java +++ b/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/JavaKitSampleMain.java @@ -20,7 +20,16 @@ */ public class JavaKitSampleMain { public static void main(String[] args) { - int result = new HelloSubclass("Swift").sayHello(17, 25); - System.out.println("sayHello(17, 25) = " + result); + var subclass = new HelloSubclass("Swift"); + + int intResult = subclass.sayHello(17, 25); + System.out.println("sayHello(17, 25) = " + intResult); + + Integer integerResult = subclass.compute(16, 25); + System.out.println("compute(17, 25) = " + integerResult); + + if (integerResult == null) { + throw AssertionError("integerResult was null!") + } } } diff --git a/Samples/SwiftKitSampleApp/com_example_swift_HelloJava2Swift.h b/Samples/SwiftKitSampleApp/com_example_swift_HelloJava2Swift.h new file mode 100644 index 00000000..19a6a52d --- /dev/null +++ b/Samples/SwiftKitSampleApp/com_example_swift_HelloJava2Swift.h @@ -0,0 +1,37 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_example_swift_HelloJava2Swift */ + +#ifndef _Included_com_example_swift_HelloJava2Swift +#define _Included_com_example_swift_HelloJava2Swift +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_example_swift_HelloJava2Swift + * Method: jniWriteString + * Signature: (Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_com_example_swift_HelloJava2Swift_jniWriteString + (JNIEnv *, jclass, jstring); + +/* + * Class: com_example_swift_HelloJava2Swift + * Method: jniGetInt + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_com_example_swift_HelloJava2Swift_jniGetInt + (JNIEnv *, jclass); + +/* + * Class: com_example_swift_HelloJava2Swift + * Method: add + * Signature: (Ljava/lang/Integer;Ljava/lang/Integer;)J + */ +JNIEXPORT jlong JNICALL Java_com_example_swift_HelloJava2Swift_add + (JNIEnv *, jclass, jobject, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/Sources/Java2SwiftLib/JavaClassTranslator.swift b/Sources/Java2SwiftLib/JavaClassTranslator.swift index bfd1657f..65d1951d 100644 --- a/Sources/Java2SwiftLib/JavaClassTranslator.swift +++ b/Sources/Java2SwiftLib/JavaClassTranslator.swift @@ -546,7 +546,8 @@ extension JavaClassTranslator { let resultType = try translator.getSwiftTypeNameAsString( javaMethod.getGenericReturnType()!, preferValueTypes: true, - outerOptional: .implicitlyUnwrappedOptional + // outerOptional: .implicitlyUnwrappedOptional // FIXME: why? it's allowed to return null objects hmmm + outerOptional: .optional // FIXME: why? it's allowed to return null objects hmmm ) // FIXME: cleanup the checking here @@ -555,6 +556,7 @@ extension JavaClassTranslator { } else { resultTypeStr = "" } + assert(!resultTypeStr.contains("!"), "contained !: '\(resultTypeStr)'") let throwsStr = javaMethod.throwsCheckedException ? "throws" : "" let swiftMethodName = javaMethod.getName().escapedSwiftName diff --git a/Sources/JavaKit/BridgedValues/JavaValue+Integer.swift b/Sources/JavaKit/BridgedValues/JavaValue+Integer.swift new file mode 100644 index 00000000..e2cf3633 --- /dev/null +++ b/Sources/JavaKit/BridgedValues/JavaValue+Integer.swift @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import JavaTypes + +// TODO: extension JavaValue: CustomStringConvertible where JNIType == jobject? { +extension JavaInteger: CustomStringConvertible { + public var description: String { + "JavaKit.\(Self.self)(\(toString()))" + } +} + +extension JavaInteger: JavaValue { + public typealias JNIType = jobject? + + public static var jvalueKeyPath: WritableKeyPath { \.l } + + public static var javaType: JavaType { + .class(package: "java.lang", name: "Integer") + } + + // FIXME: cannot implement in extension, need to fix source generator +// public required init(fromJNI value: JNIType, in environment: JNIEnvironment) { +// fatalError() +// } + + + public func getJNIValue(in environment: JNIEnvironment) -> JNIType { + fatalError() + } + + public static func jniMethodCall(in environment: JNIEnvironment) -> JNIMethodCall { + environment.interface.CallObjectMethodA + } + + public static func jniFieldGet(in environment: JNIEnvironment) -> JNIFieldGet { + environment.interface.GetObjectField + } + + public static func jniFieldSet(in environment: JNIEnvironment) -> JNIFieldSet { + environment.interface.SetObjectField + } + + public static func jniStaticMethodCall(in environment: JNIEnvironment) -> JNIStaticMethodCall { + environment.interface.CallStaticObjectMethodA + } + + public static func jniStaticFieldGet(in environment: JNIEnvironment) -> JNIStaticFieldGet { + environment.interface.GetStaticObjectField + } + + public static func jniStaticFieldSet(in environment: JNIEnvironment) -> JNIStaticFieldSet { + environment.interface.SetStaticObjectField + } + + public static func jniNewArray(in environment: JNIEnvironment) -> JNINewArray { + return { environment, size in + // FIXME: Introduce a JavaString class that we can use for this. + let clazz = environment.interface.FindClass(environment, "java/lang/Integer") + return environment.interface.NewObjectArray(environment, size, clazz, nil) + } + } + + public static func jniGetArrayRegion(in environment: JNIEnvironment) -> JNIGetArrayRegion { + return { environment, array, start, length, outPointer in + let buffer = UnsafeMutableBufferPointer(start: outPointer, count: Int(length)) + for i in 0.. JNISetArrayRegion { + return { environment, array, start, length, outPointer in + let buffer = UnsafeBufferPointer(start: outPointer, count: Int(length)) + for i in start..