Skip to content

Kotlin support #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ jobs:
- name: Download Android Emulator
run: $ANDROID_HOME/tools/bin/sdkmanager "system-images;android-29;google_apis;x86_64"
- name: Create Android Emulator
run: $ANDROID_HOME/tools/bin/avdmanager create avd -n ci-test -k "system-images;android-29;google_apis;x86_64" -d "pixel"
run: $ANDROID_HOME/tools/bin/avdmanager create avd -n ci-test -k "system-images;android-29;google_apis;x86_64" -d "pixel" --force
- name: Start Android Emulator
run: $ANDROID_HOME/emulator/emulator-headless -avd ci-test -noaudio > /dev/null &
run: $ANDROID_HOME/emulator/emulator -no-window -avd ci-test -noaudio > /dev/null &
- name: Run connected android tests
run: export TOOLCHAINS=org.swift.50320190830a;
export ANDROID_NDK_HOME=$(pwd)/android-ndk-r17c;
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.3.61'

repositories {
google()
Expand All @@ -9,7 +9,7 @@ buildscript {
}

dependencies {
classpath "com.android.tools.build:gradle:3.6.0"
classpath 'com.android.tools.build:gradle:3.6.0'
classpath "com.readdle.android.swift:gradle:1.3.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
Expand Down
88 changes: 64 additions & 24 deletions compiler/src/main/java/com/readdle/codegen/JavaSwiftProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
Expand Down Expand Up @@ -57,31 +58,31 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();

messager.printMessage(Diagnostic.Kind.WARNING, "JavaSwiftProcessor init started");
note("JavaSwiftProcessor init started");

String packageJson = processingEnv.getOptions().get(PACKAGE_OPTION);

moduleDescriptor = new Gson().fromJson(packageJson, SwiftModuleDescriptor.class);
if (moduleDescriptor == null) {
messager.printMessage(Diagnostic.Kind.ERROR, "No package description with option: com.readdle.codegen.package", null);
error(null, "No package description with option: com.readdle.codegen.package");
return;
}

messager.printMessage(Diagnostic.Kind.WARNING, "Package moduleName: " + moduleDescriptor.moduleName);
note("Package moduleName: " + moduleDescriptor.moduleName);

if (moduleDescriptor.importPackages != null) {
for (String anImport : moduleDescriptor.importPackages) {
messager.printMessage(Diagnostic.Kind.WARNING, "Package import: " + anImport);
note("Package import: " + anImport);
}
}

if (moduleDescriptor.customTypeMappings != null) {
for (String key : moduleDescriptor.customTypeMappings.keySet()) {
messager.printMessage(Diagnostic.Kind.WARNING, "Package custom mapping: " + key + " -> " + moduleDescriptor.customTypeMappings.get(key));
note("Package custom mapping: " + key + " -> " + moduleDescriptor.customTypeMappings.get(key));
}
}

messager.printMessage(Diagnostic.Kind.WARNING, "JavaSwiftProcessor init finished successfully");
note("JavaSwiftProcessor init finished successfully");
}

@Override
Expand Down Expand Up @@ -120,7 +121,7 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment

private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Filer filer = processingEnv.getFiler();
messager.printMessage(Diagnostic.Kind.WARNING, "JavaSwiftProcessor start code generation");
note("JavaSwiftProcessor start code generation");

try {
generateJavaSwift(filer);
Expand All @@ -129,13 +130,13 @@ private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnviron
error(null, "Can't write to file: " + e.getMessage());
}

messager.printMessage(Diagnostic.Kind.WARNING, "SwiftValue to process: "
note("SwiftValue to process: "
+ roundEnv.getElementsAnnotatedWith(SwiftValue.class).size());
messager.printMessage(Diagnostic.Kind.WARNING, "SwiftReference to process: "
note("SwiftReference to process: "
+ roundEnv.getElementsAnnotatedWith(SwiftReference.class).size());
messager.printMessage(Diagnostic.Kind.WARNING, "SwiftDelegate to process: "
note("SwiftDelegate to process: "
+ roundEnv.getElementsAnnotatedWith(SwiftDelegate.class).size());
messager.printMessage(Diagnostic.Kind.WARNING, "SwiftBlock to process: "
note("SwiftBlock to process: "
+ roundEnv.getElementsAnnotatedWith(SwiftBlock.class).size());

Map<String, SwiftValueDescriptor> swiftValues = new HashMap<>();
Expand Down Expand Up @@ -201,7 +202,7 @@ private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnviron
TypeElement typeElement = (TypeElement) annotatedElement;

try {
SwiftDelegateDescriptor delegateDescriptor = new SwiftDelegateDescriptor(typeElement, filer, this);
SwiftDelegateDescriptor delegateDescriptor = new SwiftDelegateDescriptor(typeElement, filer, messager, this);
swiftDelegates.put(delegateDescriptor.simpleTypeName, delegateDescriptor);
}
catch (IllegalArgumentException e) {
Expand Down Expand Up @@ -241,9 +242,9 @@ private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnviron
try {
File file = valueDescriptor.generateCode();
/* Logging */
messager.printMessage(Diagnostic.Kind.WARNING, "Generate SwiftValue " + file.getName() + ":");
note("Generate SwiftValue " + file.getName() + ":");
for (WritableElement function : valueDescriptor.getFunctions()) {
messager.printMessage(Diagnostic.Kind.NOTE, function.toString(valueDescriptor.getJavaFullName()));
note(function.toString(valueDescriptor.getJavaFullName()));
}
/* Logging */
} catch (IOException e) {
Expand All @@ -257,9 +258,9 @@ private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnviron
try {
File file = referenceDescriptor.generateCode();
/* Logging */
messager.printMessage(Diagnostic.Kind.WARNING, "Generate SwiftReference " + file.getName() + ":");
note("Generate SwiftReference " + file.getName() + ":");
for (WritableElement function : referenceDescriptor.functions) {
messager.printMessage(Diagnostic.Kind.NOTE, function.toString(referenceDescriptor.getJavaFullName()));
note(function.toString(referenceDescriptor.getJavaFullName()));
}
/* Logging */
} catch (IOException e) {
Expand All @@ -273,9 +274,9 @@ private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnviron
try {
File file = delegateDescriptor.generateCode();
/* Logging */
messager.printMessage(Diagnostic.Kind.WARNING, "Generate SwiftDelegate" + file.getName() + ":");
note("Generate SwiftDelegate" + file.getName() + ":");
for (WritableElement function : delegateDescriptor.functions) {
messager.printMessage(Diagnostic.Kind.NOTE, function.toString(delegateDescriptor.getJavaFullName()));
note(function.toString(delegateDescriptor.getJavaFullName()));
}
/* Logging */
} catch (IOException e) {
Expand All @@ -289,7 +290,7 @@ private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnviron
try {
File file = blockDescriptor.generateCode();
/* Logging */
messager.printMessage(Diagnostic.Kind.WARNING, "Generate SwiftBlock" + file.getName());
note("Generate SwiftBlock" + file.getName());
/* Logging */
} catch (IOException e) {
e.printStackTrace();
Expand All @@ -298,7 +299,7 @@ private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnviron
}
}

messager.printMessage(Diagnostic.Kind.WARNING, "JavaSwiftProcessor finished successfully!");
note("JavaSwiftProcessor finished successfully!");

return false;
}
Expand All @@ -307,7 +308,7 @@ private void generateJavaSwift(Filer filer) throws IOException {
String swiftFilePath = filer.getResource(StandardLocation.SOURCE_OUTPUT, FOLDER, "SwiftJava.swift").toUri().getPath();
File swiftExtensionFile = new File(swiftFilePath);
swiftExtensionFile.getParentFile().mkdir();
messager.printMessage(Diagnostic.Kind.WARNING, "JavaSwiftProcessor will generate sources at: " + swiftExtensionFile.getParent());
note("JavaSwiftProcessor will generate sources at: " + swiftExtensionFile.getParent());
SwiftWriter swiftWriter = new SwiftWriter(swiftExtensionFile);
swiftWriter.emitImports(new String[0]);
swiftWriter.emitEmptyLine();
Expand All @@ -329,11 +330,25 @@ private void generateJavaSwift(Filer filer) throws IOException {
swiftWriter.close();
}

private void error(Element e, String msg, Object... args) {
void note(String msg) {
messager.printMessage(Diagnostic.Kind.WARNING, msg);
}

void error(Element e, String msg, Object... args) {
messager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args), e);
}

static boolean isNullable(Element element) {
boolean isNullable(Element element) {
note("Check nullability " + element.asType().toString());
if (element.asType().getKind().isPrimitive()) {
return false;
}
if (element.getKind() == ElementKind.METHOD) {
ExecutableElement executableElement = (ExecutableElement) element;
if (executableElement.getReturnType().getKind().isPrimitive()) {
return false;
}
}
List<? extends AnnotationMirror> mirrors = element.getAnnotationMirrors();
for (AnnotationMirror mirror : mirrors) {
Name simpleName = mirror.getAnnotationType().asElement().getSimpleName();
Expand All @@ -347,6 +362,17 @@ static boolean isNullable(Element element) {
return true;
}

boolean isUnsigned(Element element) {
List<? extends AnnotationMirror> mirrors = element.getAnnotationMirrors();
for (AnnotationMirror mirror : mirrors) {
Name simpleName = mirror.getAnnotationType().asElement().getSimpleName();
if (simpleName.contentEquals("Unsigned")) {
return true;
}
}
return false;
}

static String replaceLast(String text, char replace, char replacement) {
int index = text.lastIndexOf(replace);
if (index >= 0) {
Expand All @@ -356,12 +382,26 @@ static String replaceLast(String text, char replace, char replacement) {
}

public SwiftEnvironment.Type parseJavaType(String javaType) {
if (moduleDescriptor.customTypeMappings.containsKey(javaType)) {
if (moduleDescriptor.customTypeMappings != null && moduleDescriptor.customTypeMappings.containsKey(javaType)) {
return new SwiftEnvironment.Type(moduleDescriptor.customTypeMappings.get(javaType), javaType);
}
switch (javaType) {
case "void":
return null;
case "byte":
return new SwiftEnvironment.Type("Int8", javaType);
case "short":
return new SwiftEnvironment.Type("Int16", javaType);
case "int":
return new SwiftEnvironment.Type("Int", javaType);
case "long":
return new SwiftEnvironment.Type("Int64", javaType);
case "float":
return new SwiftEnvironment.Type("Float", javaType);
case "double":
return new SwiftEnvironment.Type("Double", javaType);
case "boolean":
return new SwiftEnvironment.Type("Bool", javaType);
case "java.lang.Integer":
return new SwiftEnvironment.Type("Int", javaType);
case "java.lang.Byte":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,14 @@ class SwiftBlockDescriptor {
// Except init. We generate it's manually
this.funcName = executableElement.getSimpleName().toString();
this.isThrown = executableElement.getThrownTypes() != null && executableElement.getThrownTypes().size() > 0;
this.returnSwiftType = processor.parseJavaType(executableElement.getReturnType().toString());
this.isReturnTypeOptional = JavaSwiftProcessor.isNullable(executableElement);
this.isReturnTypeOptional = processor.isNullable(executableElement);
boolean isReturnTypeUnsigned = processor.isUnsigned(executableElement);
if (isReturnTypeUnsigned) {
this.returnSwiftType = processor.parseJavaType(executableElement.getReturnType().toString()).makeUnsigned();
}
else {
this.returnSwiftType = processor.parseJavaType(executableElement.getReturnType().toString());
}

this.sig = "(";

Expand Down Expand Up @@ -160,7 +166,9 @@ File generateCode() throws IOException {
}

if (params.size() > 0) {

swiftWriter.emitStatement("do {");

for (int i = 0; i < params.size(); i++) {
SwiftParamDescriptor param = params.get(i);
if (param.isOptional) {
Expand All @@ -171,7 +179,12 @@ File generateCode() throws IOException {
swiftWriter.emitStatement(String.format("java_%s = jnull()", param.name));
swiftWriter.emitStatement("}");
} else {
swiftWriter.emitStatement(String.format("java_%s = try $%s.javaObject()", param.name, i + ""));
if (param.isPrimitive()) {
swiftWriter.emitStatement(String.format("java_%s = try $%s.javaPrimitive()", param.name, i + ""));
}
else {
swiftWriter.emitStatement(String.format("java_%s = try $%s.javaObject()", param.name, i + ""));
}
}
}

Expand All @@ -185,11 +198,13 @@ File generateCode() throws IOException {
swiftWriter.emitStatement("fatalError(errorString)");
}
swiftWriter.emitStatement("}");

}

String jniMethodTemplate;
if (returnSwiftType != null) {
jniMethodTemplate = "let optionalResult = JNI.CallObjectMethod(self.jniObject, %s.javaMethod%s";
String methodCallMethod = returnSwiftType.returnTypeFunc(isReturnTypeOptional);
jniMethodTemplate = "let optionalResult = JNI." + methodCallMethod + "(self.jniObject, %s.javaMethod%s";
}
else {
jniMethodTemplate = "JNI.CallVoidMethod(self.jniObject, %s.javaMethod%s";
Expand Down Expand Up @@ -217,19 +232,29 @@ File generateCode() throws IOException {
swiftWriter.emitStatement("}");

if (returnSwiftType != null) {
swiftWriter.emitStatement("guard let result = optionalResult else {");
swiftWriter.emitStatement(isReturnTypeOptional ? "return nil" : "fatalError(\"Don't support nil here!\")");
swiftWriter.emitStatement("}");
swiftWriter.emitStatement("defer {");
swiftWriter.emitStatement("JNI.DeleteLocalRef(result)");
swiftWriter.emitStatement("}");
swiftWriter.emitStatement("do {");
swiftWriter.emitStatement(String.format("return try %s.from(javaObject: result)", returnSwiftType.swiftConstructorType));
swiftWriter.emitStatement("}");
swiftWriter.emitStatement("catch {");
swiftWriter.emitStatement("let errorString = String(reflecting: type(of: error)) + String(describing: error)");
swiftWriter.emitStatement("fatalError(errorString)");
swiftWriter.emitStatement("}");
if (!isReturnTypeOptional && returnSwiftType.isPrimitiveType()) {
swiftWriter.emitStatement("return " + returnSwiftType.swiftType + "(fromJavaPrimitive: optionalResult)");
}
else {
swiftWriter.emitStatement("guard let result = optionalResult else {");
if (isReturnTypeOptional) {
swiftWriter.emitStatement("return nil");
} else {
swiftWriter.emitStatement("fatalError(\"Don't support nil here!\")");
}
swiftWriter.emitStatement("}");

swiftWriter.emitStatement("defer {");
swiftWriter.emitStatement("JNI.DeleteLocalRef(result)");
swiftWriter.emitStatement("}");
swiftWriter.emitStatement("do {");
swiftWriter.emitStatement(String.format("return try %s.from(javaObject: result)", returnSwiftType.swiftConstructorType));
swiftWriter.emitStatement("}");
swiftWriter.emitStatement("catch {");
swiftWriter.emitStatement("let errorString = String(reflecting: type(of: error)) + String(describing: error)");
swiftWriter.emitStatement("fatalError(errorString)");
swiftWriter.emitStatement("}");
}
}

swiftWriter.emitStatement("}");
Expand Down
Loading