6.7.0 - Alpha7 - 修复 threads.disposable() 返回的对象在存取数据时可能被意外装箱的问题 (issue #435)
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
"timers.keepAlive 方法 timeout 参数功能无效的问题",
|
"timers.keepAlive 方法 timeout 参数功能无效的问题",
|
||||||
"floaty.window/rawWindow 方法无法接受字符串参数的问题",
|
"floaty.window/rawWindow 方法无法接受字符串参数的问题",
|
||||||
"util.class[Name]/getClass[Name] 可能返回错误结果的问题",
|
"util.class[Name]/getClass[Name] 可能返回错误结果的问题",
|
||||||
|
"threads.disposable() 返回的对象在存取数据时可能被意外装箱的问题 _[`issue #435`](http://issues.autojs6.com/435)_",
|
||||||
"无法使用 console/toast 等方法显示 BigInt 数据类型的问题",
|
"无法使用 console/toast 等方法显示 BigInt 数据类型的问题",
|
||||||
"部分全局对象可能丢失 JavaScript 原型属性及方法的问题",
|
"部分全局对象可能丢失 JavaScript 原型属性及方法的问题",
|
||||||
"使用 XML 语法将 JavaScript 表达式作为属性值时, this 对象可能出现指向错误的问题",
|
"使用 XML 语法将 JavaScript 表达式作为属性值时, this 对象可能出现指向错误的问题",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class Threads(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
|
|||||||
::interrupt.name,
|
::interrupt.name,
|
||||||
::start.name,
|
::start.name,
|
||||||
::pool.name,
|
::pool.name,
|
||||||
|
::disposable.name,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val globalAssignmentFunctions = listOf(
|
override val globalAssignmentFunctions = listOf(
|
||||||
@@ -79,6 +80,12 @@ class Threads(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
|
|||||||
Synchronizer(func, lock)
|
Synchronizer(func, lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@RhinoRuntimeFunctionInterface
|
||||||
|
fun disposable(scriptRuntime: ScriptRuntime, args: Array<out Any?>): VolatileDisposeNativeObject = ensureArgumentsIsEmpty(args) {
|
||||||
|
VolatileDisposeNativeObject()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user