From 799baaa1ee84e9770c069e9106f748aacab6dffc Mon Sep 17 00:00:00 2001 From: SuperMonster003 Date: Thu, 23 Oct 2025 23:10:51 +0800 Subject: [PATCH] =?UTF-8?q?6.7.0=20-=20Alpha7=20-=20=E4=BF=AE=E5=A4=8D=20t?= =?UTF-8?q?hreads.disposable()=20=E8=BF=94=E5=9B=9E=E7=9A=84=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E5=9C=A8=E5=AD=98=E5=8F=96=E6=95=B0=E6=8D=AE=E6=97=B6?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E8=A2=AB=E6=84=8F=E5=A4=96=E8=A3=85=E7=AE=B1?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98=20(issue=20#435)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changelog/lang_zh-Hans.json | 1 + .../runtime/api/augment/threads/Threads.kt | 7 ++ .../threads/VolatileDisposeNativeObject.kt | 117 ++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 app/src/main/java/org/autojs/autojs/runtime/api/augment/threads/VolatileDisposeNativeObject.kt diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index 6037ebf2..cea4e9e4 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -16,6 +16,7 @@ "timers.keepAlive 方法 timeout 参数功能无效的问题", "floaty.window/rawWindow 方法无法接受字符串参数的问题", "util.class[Name]/getClass[Name] 可能返回错误结果的问题", + "threads.disposable() 返回的对象在存取数据时可能被意外装箱的问题 _[`issue #435`](http://issues.autojs6.com/435)_", "无法使用 console/toast 等方法显示 BigInt 数据类型的问题", "部分全局对象可能丢失 JavaScript 原型属性及方法的问题", "使用 XML 语法将 JavaScript 表达式作为属性值时, this 对象可能出现指向错误的问题", diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/threads/Threads.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/threads/Threads.kt index 5652545f..a76b8ea4 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/threads/Threads.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/threads/Threads.kt @@ -28,6 +28,7 @@ class Threads(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { ::interrupt.name, ::start.name, ::pool.name, + ::disposable.name, ) override val globalAssignmentFunctions = listOf( @@ -79,6 +80,12 @@ class Threads(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) { Synchronizer(func, lock) } + @JvmStatic + @RhinoRuntimeFunctionInterface + fun disposable(scriptRuntime: ScriptRuntime, args: Array): VolatileDisposeNativeObject = ensureArgumentsIsEmpty(args) { + VolatileDisposeNativeObject() + } + } } diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/threads/VolatileDisposeNativeObject.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/threads/VolatileDisposeNativeObject.kt new file mode 100644 index 00000000..c44ef1d0 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/threads/VolatileDisposeNativeObject.kt @@ -0,0 +1,117 @@ +package org.autojs.autojs.runtime.api.augment.threads + +import org.autojs.autojs.annotation.RhinoStandardFunctionInterface +import org.autojs.autojs.extension.FlexibleArray +import org.autojs.autojs.extension.FlexibleArray.Companion.component1 +import org.autojs.autojs.extension.FlexibleArray.Companion.component2 +import org.autojs.autojs.extension.FlexibleArray.Companion.component3 +import org.autojs.autojs.util.RhinoUtils +import org.autojs.autojs.util.RhinoUtils.UNDEFINED +import org.autojs.autojs.util.RhinoUtils.coerceLongNumber +import org.mozilla.javascript.Context +import org.mozilla.javascript.Function +import org.mozilla.javascript.NativeObject +import org.mozilla.javascript.Scriptable +import org.mozilla.javascript.Undefined +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.Volatile + +@Suppress("unused") +class VolatileDisposeNativeObject : NativeObject() { + + @Volatile + private var value: Any? = null + + private val lock = ReentrantLock() + private val ready = lock.newCondition() + + private val mFunctionNames = arrayOf( + ::blockedGet.name, + ::blockedGetOrThrow.name, + ::setAndNotify.name, + ) + + init { + RhinoUtils.initNativeObjectPrototype(this) + defineFunctionProperties(mFunctionNames, javaClass, PERMANENT) + } + + private inline fun withLock(block: () -> T): T { + lock.lock() + return try { + block() + } finally { + lock.unlock() + } + } + + private fun awaitValue(timeoutMillis: Long, onInterrupted: (() -> Unit)? = null): Any? = withLock { + when { + timeoutMillis <= 0L -> { + try { + ready.await() + } catch (e: InterruptedException) { + onInterrupted?.invoke() ?: throw RuntimeException(e) + } + } + else -> { + var nanos = TimeUnit.MILLISECONDS.toNanos(timeoutMillis) + while (nanos > 0L) { + try { + ready.awaitNanos(nanos).also { nanos = it } + } catch (e: InterruptedException) { + onInterrupted?.invoke() ?: throw RuntimeException(e) + } + } + } + } + value + } + + private fun instantiateRuntimeException(clazz: Class) = try { + clazz.getDeclaredConstructor().newInstance() + } catch (e: Exception) { + RuntimeException(e) + } + + companion object : FlexibleArray() { + + @JvmStatic + @RhinoStandardFunctionInterface + fun blockedGet(cx: Context, thisObj: Scriptable, args: Array, funObj: Function): Any? = ensureArgumentsAtMost(args, 1) { argList -> + val (timeout) = argList + val self = thisObj as VolatileDisposeNativeObject + val timeoutMillis = coerceLongNumber(timeout, 0L) + self.awaitValue(timeoutMillis) + } + + @JvmStatic + @RhinoStandardFunctionInterface + fun blockedGetOrThrow(cx: Context, thisObj: Scriptable, args: Array, funObj: Function): Any? = ensureArgumentsLengthInRange(args, 1..3) { argList -> + val (exception, timeout, defaultValue) = argList + require(exception is Class<*>) { + "Argument \"exception\" must be a RuntimeException Class for ${VolatileDisposeNativeObject::class.java.simpleName}::blockedGetOrThrow" + } + val self = thisObj as VolatileDisposeNativeObject + val timeoutMillis = coerceLongNumber(timeout, 0L) + self.awaitValue(timeoutMillis) { + @Suppress("UNCHECKED_CAST") + throw self.instantiateRuntimeException(exception as Class) + } ?: defaultValue + } + + @JvmStatic + @RhinoStandardFunctionInterface + fun setAndNotify(cx: Context, thisObj: Scriptable, args: Array, funObj: Function): Undefined = ensureArgumentsOnlyOne(args) { value -> + val self = thisObj as VolatileDisposeNativeObject + self.withLock { + self.value = value + self.ready.signalAll() + } + UNDEFINED + } + + } + +} \ No newline at end of file