From c2b98af9d595febaf204dbfadc4b6ba4f2edcb37 Mon Sep 17 00:00:00 2001 From: SuperMonster003 Date: Fri, 9 May 2025 18:23:05 +0800 Subject: [PATCH] =?UTF-8?q?6.6.3=20-=20Alpha3=20-=20=E4=BF=AE=E5=A4=8D=20J?= =?UTF-8?q?avaAdapter=20=E6=A0=88=E6=BA=A2=E5=87=BA=E9=97=AE=E9=A2=98=20(i?= =?UTF-8?q?ssue=20#376)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changelog/lang_zh-Hans.json | 1 + .../autojs/rhino/AndroidClassLoader.java | 194 +++++++++++++++++- .../autojs/autojs/rhino/AndroidClassLoader.kt | 9 +- version.properties | 8 +- 4 files changed, 200 insertions(+), 12 deletions(-) diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index 30509099..13a6bf16 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -16,6 +16,7 @@ "APK 文件类型信息对话框可能无法获取应用名称及 SDK 信息的问题", "Android 15 部分页面状态栏背景颜色可能无法动态跟随主题色的问题", "dialogs 模块无法正常使用 customView 属性的问题 _[`issue #364`](http://issues.autojs6.com/364)_", + "使用 JavaAdapter 时导致 ClassLoader 调用栈溢出的问题 _[`issue #376`](http://issues.autojs6.com/376)_", "console.setContentTextColor 方法导致日志字体颜色丢失默认值的问题 _[`issue #346`](http://issues.autojs6.com/346)_", "README.md 中部分语言日期格式不正确的问题" ], diff --git a/app/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java b/app/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java index 2e6d852e..9b1ca605 100644 --- a/app/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java +++ b/app/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java @@ -1,14 +1,200 @@ package com.stardust.autojs.rhino; -import org.jetbrains.annotations.NotNull; +import android.util.Log; +import com.android.dx.command.dexer.Main; +import com.stardust.pio.PFiles; +import com.stardust.util.MD5; + +import org.mozilla.javascript.GeneratedClassLoader; + +import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; + +import dalvik.system.DexClassLoader; /** * Created by Stardust on 2017/4/5. */ -public class AndroidClassLoader extends org.autojs.autojs.rhino.AndroidClassLoader { - public AndroidClassLoader(@NotNull ClassLoader parent, @NotNull File cacheDir) { - super(parent, cacheDir); + +public class AndroidClassLoader extends ClassLoader implements GeneratedClassLoader { + + + private static final String LOG_TAG = "AndroidClassLoader"; + private final ClassLoader parent; + private final List mDexClassLoaders = new ArrayList<>(); + private final File mCacheDir; + + /** + * Create a new instance with the given parent classloader and cache dierctory + * + * @param parent the parent + * @param dir the cache directory + */ + public AndroidClassLoader(ClassLoader parent, File dir) { + this.parent = parent; + mCacheDir = dir; + if (dir.exists()) { + PFiles.deleteFilesOfDir(dir); + } else { + dir.mkdirs(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Class defineClass(String name, byte[] data) { + Log.d(LOG_TAG, "defineClass: name = " + name + " data.length = " + data.length); + File classFile = null; + try { + classFile = generateTempFile(name, false); + java.util.jar.JarOutputStream jos = new java.util.jar.JarOutputStream(new java.io.FileOutputStream(classFile)); + java.util.jar.JarEntry entry = new java.util.jar.JarEntry(name.replace('.', '/') + ".class"); + jos.putNextEntry(entry); + jos.write(data); + jos.closeEntry(); + jos.close(); + return dexJar(classFile, null).loadClass(name); + } catch (IOException | ClassNotFoundException e) { + throw new FatalLoadingException(e); + } finally { + if (classFile != null) { + classFile.delete(); + } + } + } + + private File generateTempFile(String name, boolean create) throws IOException { + File file = new File(mCacheDir, name.hashCode() + System.currentTimeMillis() + ".jar"); + if (create) { + if (!file.exists()) { + file.createNewFile(); + } + } else { + file.delete(); + } + return file; + } + + public void loadJar(File jar) throws IOException { + Log.d(LOG_TAG, "loadJar: jar = " + jar); + if (!jar.exists() || !jar.canRead()) { + throw new FileNotFoundException("File does not exist or readable: " + jar.getPath()); + } + File dexFile = new File(mCacheDir, generateDexFileName(jar)); + if (dexFile.exists()) { + loadDex(dexFile); + return; + } + try { + final File classFile = generateTempFile(jar.getPath(), false); + java.util.jar.JarOutputStream jos = new java.util.jar.JarOutputStream(new java.io.FileOutputStream(classFile)); + java.util.jar.JarFile jarFile = new java.util.jar.JarFile(jar); + java.util.Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + java.util.jar.JarEntry entry = entries.nextElement(); + if (!entry.isDirectory()) { + jos.putNextEntry(new java.util.jar.JarEntry(entry.getName())); + java.io.InputStream is = jarFile.getInputStream(entry); + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = is.read(buffer)) != -1) { + jos.write(buffer, 0, bytesRead); + } + is.close(); + jos.closeEntry(); + } + } + jos.close(); + jarFile.close(); + dexJar(classFile, dexFile); + classFile.delete(); + } catch (Exception e) { + throw new IOException(e); + } + } + + private String generateDexFileName(File jar) { + String message = jar.getPath() + "_" + jar.lastModified(); + return MD5.md5(message); + } + + public DexClassLoader loadDex(File file) throws FileNotFoundException { + Log.d(LOG_TAG, "loadDex: file = " + file); + if (!file.exists()) { + throw new FileNotFoundException(file.getPath()); + } + DexClassLoader loader = new DexClassLoader(file.getPath(), mCacheDir.getPath(), null, parent); + mDexClassLoaders.add(loader); + return loader; + } + + private DexClassLoader dexJar(File classFile, File dexFile) throws IOException { + final Main.Arguments arguments = new Main.Arguments(); + arguments.fileNames = new String[]{classFile.getPath()}; + boolean isTmpDex = dexFile == null; + if (isTmpDex) { + dexFile = generateTempFile("dex-" + classFile.getPath(), true); + } + arguments.outName = dexFile.getPath(); + arguments.jarOutput = true; + Main.run(arguments); + DexClassLoader loader = loadDex(dexFile); + if (isTmpDex) { + dexFile.delete(); + } + return loader; + } + + /** + * Does nothing + * + * @param aClass ignored + */ + @Override + public void linkClass(Class aClass) { + //doesn't make sense on android + } + + /** + * Try to load a class. This will search all defined classes, all loaded jars and the parent class loader. + * + * @param name the name of the class to load + * @param resolve ignored + * @return the class + * @throws ClassNotFoundException if the class could not be found in any of the locations + */ + @Override + public Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + Class loadedClass = findLoadedClass(name); + if (loadedClass == null) { + for (DexClassLoader dex : mDexClassLoaders) { + loadedClass = dex.loadClass(name); + if (loadedClass != null) { + break; + } + } + if (loadedClass == null) { + loadedClass = parent.loadClass(name); + } + } + return loadedClass; + } + + /** + * Might be thrown in any Rhino method that loads bytecode if the loading failed + */ + public static class FatalLoadingException extends RuntimeException { + FatalLoadingException(Throwable t) { + super("Failed to define class", t); + } } } diff --git a/app/src/main/java/org/autojs/autojs/rhino/AndroidClassLoader.kt b/app/src/main/java/org/autojs/autojs/rhino/AndroidClassLoader.kt index 96c2768a..b9e2c95d 100644 --- a/app/src/main/java/org/autojs/autojs/rhino/AndroidClassLoader.kt +++ b/app/src/main/java/org/autojs/autojs/rhino/AndroidClassLoader.kt @@ -29,7 +29,9 @@ import com.legacy.android.dx.command.dexer.Main as LegacyMain * @param parent the parent * @param cacheDir the cache directory */ -open class AndroidClassLoader(private val parent: ClassLoader, private val cacheDir: File) : ClassLoader(), GeneratedClassLoader { +class AndroidClassLoader(private val parent: ClassLoader, private val cacheDir: File) : ClassLoader(), GeneratedClassLoader { + + private val mDexClassLoaders = HashMap() init { if (cacheDir.exists()) { @@ -78,7 +80,7 @@ open class AndroidClassLoader(private val parent: ClassLoader, private val cache } return DexClassLoader(safeDexFile.path, cacheDir.path, null, parent).also { - dexClassLoaders[safeDexFile.path] = it + mDexClassLoaders[safeDexFile.path] = it } } @@ -125,7 +127,7 @@ open class AndroidClassLoader(private val parent: ClassLoader, private val cache @Throws(ClassNotFoundException::class) public override fun loadClass(name: String, resolve: Boolean): Class<*> { findLoadedClass(name)?.let { return it } - for (dex in dexClassLoaders.values) try { + for (dex in mDexClassLoaders.values) try { dex.loadClass(name)?.let { return it } } catch (e: Exception) { e.printStackTrace() @@ -216,7 +218,6 @@ open class AndroidClassLoader(private val parent: ClassLoader, private val cache companion object { private val TAG = AndroidClassLoader::class.java.simpleName - private val dexClassLoaders = HashMap() } diff --git a/version.properties b/version.properties index fb6954b4..87abc8ef 100644 --- a/version.properties +++ b/version.properties @@ -1,5 +1,5 @@ -#Fri May 09 16:55:43 CST 2025 -BUILD_TIME=1746780943861 +#Fri May 09 18:20:25 CST 2025 +BUILD_TIME=1746786025154 COMPILE_SDK_VERSION=35 JAVA_VERSION=23 JAVA_VERSION_MIN_RADICAL=0 @@ -17,6 +17,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13 RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3 TARGET_SDK_VERSION=35 TARGET_SDK_VERSION_INRT=29 -VERSION_BUILD=3211 -VERSION_NAME=6.6.3 Alpha2 +VERSION_BUILD=3212 +VERSION_NAME=6.6.3 Alpha3 VSCODE_EXT_REQUIRED_VERSION=1.0.8