6.7.0 - Alpha7 - 修复 threads.disposable() 返回的对象在存取数据时可能被意外装箱的问题 (issue #435)

This commit is contained in:
SuperMonster003
2025-10-23 23:10:51 +08:00
parent 369ceba230
commit 799baaa1ee
3 changed files with 125 additions and 0 deletions

View File

@@ -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 对象可能出现指向错误的问题",

View File

@@ -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<out Any?>): VolatileDisposeNativeObject = ensureArgumentsIsEmpty(args) {
VolatileDisposeNativeObject()
}
}
}

View File

@@ -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 <T> 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 <T : RuntimeException> instantiateRuntimeException(clazz: Class<T>) = try {
clazz.getDeclaredConstructor().newInstance()
} catch (e: Exception) {
RuntimeException(e)
}
companion object : FlexibleArray() {
@JvmStatic
@RhinoStandardFunctionInterface
fun blockedGet(cx: Context, thisObj: Scriptable, args: Array<Any?>, 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<Any?>, 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<RuntimeException>)
} ?: defaultValue
}
@JvmStatic
@RhinoStandardFunctionInterface
fun setAndNotify(cx: Context, thisObj: Scriptable, args: Array<Any?>, funObj: Function): Undefined = ensureArgumentsOnlyOne(args) { value ->
val self = thisObj as VolatileDisposeNativeObject
self.withLock {
self.value = value
self.ready.signalAll()
}
UNDEFINED
}
}
}