From 3cbf732d235194a4491b7afc55af17cc10653211 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 9 Apr 2026 12:15:19 +0300 Subject: [PATCH] Fixed kotlin native interfaces and removed reflection nonsense --- .../builders/AndroidGradleBuilder.java | 90 +++++++++---------- .../java/com/codename1/builders/Executor.java | 4 +- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java index 3f83d699cf..93de20015a 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java @@ -3605,6 +3605,24 @@ public void usesClassMethod(String cls, String method) { gradleDependency = "classpath 'com.android.tools.build:gradle:8.1.0'\n"; } } + boolean hasKotlinSources = hasSourceFileWithExtension(new File(projectDir, "src/main/java"), ".kt"); + String kotlinVersion = request.getArg("requireKotlinStdlib", "").trim(); + if (hasKotlinSources && kotlinVersion.length() == 0) { + kotlinVersion = useGradle8 ? "1.9.22" : "1.7.22"; + } + String kotlinPluginApply = ""; + String kotlinRuntimeDependency = ""; + if (hasKotlinSources) { + if (!request.getArg("android.topDependency", "").contains("kotlin-gradle-plugin")) { + gradleDependency += "classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:" + kotlinVersion + "'\n"; + } + kotlinPluginApply = "apply plugin: 'kotlin-android'\n"; + String gradleDeps = request.getArg("android.gradleDep", ""); + if (!additionalDependencies.contains("org.jetbrains.kotlin:kotlin-stdlib") + && !gradleDeps.contains("org.jetbrains.kotlin:kotlin-stdlib")) { + kotlinRuntimeDependency = " implementation 'org.jetbrains.kotlin:kotlin-stdlib:" + kotlinVersion + "'\n"; + } + } gradleDependency += request.getArg("android.topDependency", ""); String compileSdkVersion = "'android-21'"; @@ -3743,6 +3761,7 @@ public void usesClassMethod(String cls, String method) { } String gradleProps = "apply plugin: 'com.android.application'\n" + + kotlinPluginApply + request.getArg("android.gradlePlugin", "") + "\n" + "buildscript {\n" @@ -3826,6 +3845,7 @@ public void usesClassMethod(String cls, String method) { + "dependencies {\n" + " "+compile+" fileTree(dir: 'libs', include: ['*.jar'])\n" + request.getArg("android.supportv4Dep",supportV4Default) + "\n" + + kotlinRuntimeDependency + addNewlineIfMissing(additionalDependencies) + addNewlineIfMissing(request.getArg("android.gradleDep", "")) + addNewlineIfMissing(aarDependencies) @@ -3994,27 +4014,7 @@ protected String registerNativeImplementationsAndCreateStubs(ClassLoader parentC String javaImplSourceFile = "package " + currentNative.getPackage().getName() + ";\n\n" + "import com.codename1.ui.PeerComponent;\n\n" + "public class " + currentNative.getSimpleName() + "Stub implements " + currentNative.getSimpleName() + "{\n" - + " private final Object impl = createImpl();\n\n" - + " private static Object createImpl() {\n" - + " try {\n" - + " return Class.forName(\"" + currentNative.getName() + getImplSuffix() + "\").newInstance();\n" - + " } catch (Throwable t) {\n" - + " throw new RuntimeException(\"Failed to instantiate native implementation for " + currentNative.getName() + "\", t);\n" - + " }\n" - + " }\n\n" - + " private Object __cn1Invoke(String methodName, Object[] args) {\n" - + " try {\n" - + " java.lang.reflect.Method[] methods = impl.getClass().getMethods();\n" - + " for (java.lang.reflect.Method method : methods) {\n" - + " if (method.getName().equals(methodName) && method.getParameterTypes().length == args.length) {\n" - + " return method.invoke(impl, args);\n" - + " }\n" - + " }\n" - + " throw new RuntimeException(methodName + \" with \" + args.length + \" args\");\n" - + " } catch (Throwable t) {\n" - + " throw new RuntimeException(\"Failed to invoke native method \" + methodName, t);\n" - + " }\n" - + " }\n\n"; + + " private " + currentNative.getSimpleName() + getImplSuffix() + " impl = new " + currentNative.getSimpleName() + getImplSuffix() + "();\n\n"; for (Method m : currentNative.getMethods()) { String name = m.getName(); @@ -4042,34 +4042,13 @@ protected String registerNativeImplementationsAndCreateStubs(ClassLoader parentC } } javaImplSourceFile += ") {\n"; - String invocationExpression = "__cn1Invoke(\"" + name + "\", new Object[]{" + args + "})"; if (Void.class == returnType || Void.TYPE == returnType) { - javaImplSourceFile += " " + invocationExpression + ";\n }\n\n"; + javaImplSourceFile += " impl." + name + "(" + args + ");\n }\n\n"; } else { if (returnType.getName().equals("com.codename1.ui.PeerComponent")) { - javaImplSourceFile += " return " + generatePeerComponentCreationCode(invocationExpression) + ";\n }\n\n"; - } else if (returnType.isPrimitive()) { - if (returnType == Boolean.TYPE) { - javaImplSourceFile += " return ((Boolean)" + invocationExpression + ").booleanValue();\n }\n\n"; - } else if (returnType == Integer.TYPE) { - javaImplSourceFile += " return ((Integer)" + invocationExpression + ").intValue();\n }\n\n"; - } else if (returnType == Long.TYPE) { - javaImplSourceFile += " return ((Long)" + invocationExpression + ").longValue();\n }\n\n"; - } else if (returnType == Byte.TYPE) { - javaImplSourceFile += " return ((Byte)" + invocationExpression + ").byteValue();\n }\n\n"; - } else if (returnType == Short.TYPE) { - javaImplSourceFile += " return ((Short)" + invocationExpression + ").shortValue();\n }\n\n"; - } else if (returnType == Character.TYPE) { - javaImplSourceFile += " return ((Character)" + invocationExpression + ").charValue();\n }\n\n"; - } else if (returnType == Float.TYPE) { - javaImplSourceFile += " return ((Float)" + invocationExpression + ").floatValue();\n }\n\n"; - } else if (returnType == Double.TYPE) { - javaImplSourceFile += " return ((Double)" + invocationExpression + ").doubleValue();\n }\n\n"; - } else { - javaImplSourceFile += " return (" + returnType.getSimpleName() + ")" + invocationExpression + ";\n }\n\n"; - } + javaImplSourceFile += " return " + generatePeerComponentCreationCode("impl." + name + "(" + args + ")") + ";\n }\n\n"; } else { - javaImplSourceFile += " return (" + returnType.getSimpleName() + ")" + invocationExpression + ";\n }\n\n"; + javaImplSourceFile += " return impl." + name + "(" + args + ");\n }\n\n"; } } } @@ -4412,7 +4391,7 @@ public void unzip(InputStream source, File classesDir, File resDir, File sourceD if (entryName.endsWith(".class")) { destFile = new File(classesDir, entryName); } else { - if (entryName.endsWith(".java") || entryName.endsWith(".m") || entryName.endsWith(".h")) { + if (entryName.endsWith(".java") || entryName.endsWith(".kt") || entryName.endsWith(".swift") || entryName.endsWith(".m") || entryName.endsWith(".h")) { destFile = new File(sourceDir, entryName); } else { if (entryName.endsWith(".jar") || entryName.endsWith(".a") || entryName.endsWith(".dylib") || entryName.endsWith(".andlib") || entryName.endsWith(".aar")) { @@ -4766,6 +4745,25 @@ private Integer parseSdkInt(String value) { } } + private boolean hasSourceFileWithExtension(File dir, String extension) { + if (dir == null || !dir.exists()) { + return false; + } + if (dir.isFile()) { + return dir.getName().endsWith(extension); + } + File[] children = dir.listFiles(); + if (children == null) { + return false; + } + for (File child : children) { + if (hasSourceFileWithExtension(child, extension)) { + return true; + } + } + return false; + } + private void stripKotlin(File dummyClassesDir) { String[] skipDirectories = new String[] { "org" + File.separator + "jetbrains" + File.separator + "annotations", diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java index e3524630c6..4379fe368c 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java @@ -1089,7 +1089,7 @@ private void copyDir(File dir, File classesDir, File resDir, File sourceDir, Fil destFile = new File(classesDir, fileName); } } else { - if (fileName.endsWith(".java") || fileName.endsWith(".m") || fileName.endsWith(".h") || fileName.endsWith(".cs")) { + if (fileName.endsWith(".java") || fileName.endsWith(".kt") || fileName.endsWith(".swift") || fileName.endsWith(".m") || fileName.endsWith(".h") || fileName.endsWith(".cs")) { destFile = new File(sourceDir, fileName); } else { if (fileName.endsWith(".jar") || fileName.endsWith(".a") || fileName.endsWith(".dylib")) { @@ -1319,7 +1319,7 @@ public void unzip(InputStream source, File classesDir, File resDir, File sourceD destFile = new File(classesDir, entryName); } } else { - if (entryName.endsWith(".java") || entryName.endsWith(".m") || entryName.endsWith(".h") || entryName.endsWith(".cs")) { + if (entryName.endsWith(".java") || entryName.endsWith(".kt") || entryName.endsWith(".swift") || entryName.endsWith(".m") || entryName.endsWith(".h") || entryName.endsWith(".cs")) { destFile = new File(sourceDir, entryName); } else { if (entryName.endsWith(".jar") || entryName.endsWith(".a") || entryName.endsWith(".dylib") || entryName.endsWith(".andlib") || entryName.endsWith(".aar") || entryName.endsWith(dll)) {