6.6.3 - Alpha3 - 修复 JavaAdapter 栈溢出问题 (issue #376)

This commit is contained in:
SuperMonster003
2025-05-09 18:23:05 +08:00
parent 9f82114757
commit c2b98af9d5
4 changed files with 200 additions and 12 deletions

View File

@@ -16,6 +16,7 @@
"APK 文件类型信息对话框可能无法获取应用名称及 SDK 信息的问题", "APK 文件类型信息对话框可能无法获取应用名称及 SDK 信息的问题",
"Android 15 部分页面状态栏背景颜色可能无法动态跟随主题色的问题", "Android 15 部分页面状态栏背景颜色可能无法动态跟随主题色的问题",
"dialogs 模块无法正常使用 customView 属性的问题 _[`issue #364`](http://issues.autojs6.com/364)_", "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)_", "console.setContentTextColor 方法导致日志字体颜色丢失默认值的问题 _[`issue #346`](http://issues.autojs6.com/346)_",
"README.md 中部分语言日期格式不正确的问题" "README.md 中部分语言日期格式不正确的问题"
], ],

View File

@@ -1,14 +1,200 @@
package com.stardust.autojs.rhino; 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.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. * Created by Stardust on 2017/4/5.
*/ */
public class AndroidClassLoader extends org.autojs.autojs.rhino.AndroidClassLoader {
public AndroidClassLoader(@NotNull ClassLoader parent, @NotNull File cacheDir) { public class AndroidClassLoader extends ClassLoader implements GeneratedClassLoader {
super(parent, cacheDir);
private static final String LOG_TAG = "AndroidClassLoader";
private final ClassLoader parent;
private final List<DexClassLoader> 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<java.util.jar.JarEntry> 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);
}
} }
} }

View File

@@ -29,7 +29,9 @@ import com.legacy.android.dx.command.dexer.Main as LegacyMain
* @param parent the parent * @param parent the parent
* @param cacheDir the cache directory * @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<String, DexClassLoader>()
init { init {
if (cacheDir.exists()) { 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 { 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) @Throws(ClassNotFoundException::class)
public override fun loadClass(name: String, resolve: Boolean): Class<*> { public override fun loadClass(name: String, resolve: Boolean): Class<*> {
findLoadedClass(name)?.let { return it } findLoadedClass(name)?.let { return it }
for (dex in dexClassLoaders.values) try { for (dex in mDexClassLoaders.values) try {
dex.loadClass(name)?.let { return it } dex.loadClass(name)?.let { return it }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@@ -216,7 +218,6 @@ open class AndroidClassLoader(private val parent: ClassLoader, private val cache
companion object { companion object {
private val TAG = AndroidClassLoader::class.java.simpleName private val TAG = AndroidClassLoader::class.java.simpleName
private val dexClassLoaders = HashMap<String, DexClassLoader>()
} }

View File

@@ -1,5 +1,5 @@
#Fri May 09 16:55:43 CST 2025 #Fri May 09 18:20:25 CST 2025
BUILD_TIME=1746780943861 BUILD_TIME=1746786025154
COMPILE_SDK_VERSION=35 COMPILE_SDK_VERSION=35
JAVA_VERSION=23 JAVA_VERSION=23
JAVA_VERSION_MIN_RADICAL=0 JAVA_VERSION_MIN_RADICAL=0
@@ -17,6 +17,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3 RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
TARGET_SDK_VERSION=35 TARGET_SDK_VERSION=35
TARGET_SDK_VERSION_INRT=29 TARGET_SDK_VERSION_INRT=29
VERSION_BUILD=3211 VERSION_BUILD=3212
VERSION_NAME=6.6.3 Alpha2 VERSION_NAME=6.6.3 Alpha3
VSCODE_EXT_REQUIRED_VERSION=1.0.8 VSCODE_EXT_REQUIRED_VERSION=1.0.8