6.7.0 - Alpha19 - 修复内置模块方法调用可能出现 Rhino 上下文可能不一致的问题

This commit is contained in:
SuperMonster003
2026-01-31 15:09:08 +08:00
parent 8205409c8c
commit 142c465914
19 changed files with 231 additions and 245 deletions

View File

@@ -1,6 +1,7 @@
package org.autojs.autojs.rhino.extension
import org.autojs.autojs.rhino.extension.AnyExtensions.jsBrief
import org.autojs.autojs.util.RhinoUtils.getOrCreateStandardObjects
import org.autojs.autojs.util.RhinoUtils.withRhinoContext
import org.mozilla.javascript.Context
import org.mozilla.javascript.NativeArray
@@ -9,7 +10,7 @@ object ArrayExtensions {
fun Array<*>.toNativeArray(): NativeArray {
return withRhinoContext { cx ->
val standardObjects = cx.initStandardObjects()
val standardObjects = cx.getOrCreateStandardObjects()
cx.newArray(standardObjects, this.toList().map { Context.javaToJS(it, standardObjects) }.toTypedArray()) as NativeArray
}
}

View File

@@ -1,6 +1,7 @@
package org.autojs.autojs.rhino.extension
import org.autojs.autojs.util.RhinoUtils
import org.autojs.autojs.util.RhinoUtils.getOrCreateStandardObjects
import org.mozilla.javascript.Context
import org.mozilla.javascript.NativeArray
@@ -8,7 +9,7 @@ object IterableExtensions {
fun Iterable<*>.toNativeArray(): NativeArray {
return RhinoUtils.withRhinoContext { cx ->
val standardObjects = cx.initStandardObjects()
val standardObjects = cx.getOrCreateStandardObjects()
cx.newArray(standardObjects, this.map { Context.javaToJS(it, standardObjects) }.toTypedArray()) as NativeArray
}
}

View File

@@ -13,5 +13,5 @@ interface IScriptBridges {
fun toString(obj: Any?): String
fun toPrimitive(obj: Any?): Any
fun toPrimitive(obj: Any?): Any?
}

View File

@@ -29,12 +29,12 @@ class ScriptBridges : IScriptBridges {
this.engine = engine
}
fun setJavaPrimitiveWrap(b: Boolean) = useJsContext {
it.wrapFactory.isJavaPrimitiveWrap = b
fun setJavaPrimitiveWrap(b: Boolean) = useJsContext { cx ->
cx.wrapFactory.isJavaPrimitiveWrap = b
}
fun isJavaPrimitiveWrap(): Boolean = useJsContext {
it.wrapFactory.isJavaPrimitiveWrap
fun isJavaPrimitiveWrap(): Boolean = useJsContext { cx ->
cx.wrapFactory.isJavaPrimitiveWrap
}
override fun call(func: BaseFunction, target: Any?, args: Array<*>) = useJsContext { cx ->
@@ -46,12 +46,12 @@ class ScriptBridges : IScriptBridges {
callFunction(engine.runtime, func, niceScope, thisObj, niceArgs)
}
override fun toArray(o: Iterable<*>?): NativeArray = useJsContext { context ->
val scope = context.initStandardObjects()
context.newArray(scope, o?.map { Context.javaToJS(it, scope) }?.toTypedArray()) as NativeArray
override fun toArray(o: Iterable<*>?): NativeArray = useJsContext { cx ->
val scope = engine.runtime.topLevelScope
cx.newArray(scope, o?.map { Context.javaToJS(it, scope) }?.toTypedArray()) as NativeArray
}
override fun asArray(listLike: Any): NativeArray = useJsContext { context ->
override fun asArray(listLike: Any): NativeArray = useJsContext { cx ->
if (listLike is Iterable<*>) {
return@useJsContext toArray(listLike)
}
@@ -61,7 +61,7 @@ class ScriptBridges : IScriptBridges {
listLike::class.java.methods.forEach {
val name = it.name
val method = NativeJavaMethod(it, name)
val bound = BoundFunction(context, arr, method, boundThis, emptyArray())
val bound = BoundFunction(cx, arr, method, boundThis, emptyArray())
arr.put(name, arr, bound)
}
return@useJsContext arr
@@ -69,16 +69,18 @@ class ScriptBridges : IScriptBridges {
return@useJsContext newNativeArray()
}
override fun toString(obj: Any?): String = Context.toString(obj)
override fun toString(obj: Any?): String =
Context.toString(obj)
override fun toPrimitive(obj: Any?): Any = useJsContext { context ->
Context.javaToJS(obj, context.initStandardObjects())
}
override fun toPrimitive(obj: Any?): Any? =
Context.javaToJS(obj, engine.runtime.topLevelScope)
private fun <T> useJsContext(f: (Context) -> T): T {
val currentContext = Context.getCurrentContext()
return try {
f(currentContext ?: engine.setupContext(Context.enter()))
// Enter Context via engine to ensure WrapFactory and AutoJsContext binding are installed.
// zh-CN: 通过 engine 进入 Context, 确保 WrapFactory 与 AutoJsContext 绑定已安装.
f(currentContext ?: engine.enterContext())
} finally {
currentContext ?: Context.exit()
}

View File

@@ -82,7 +82,7 @@ open class AugmentableProxy(private val scriptRuntime: ScriptRuntime) : Augmenta
// ! but defined in a certain object in its prototype chain.
// ! zh-CN: 表示 `key` 未定义在 `augmented` 上, 但定义在其原型链对象上.
!augmented.has(key) && ScriptableObject.hasProperty(augmented, key) -> {
withRhinoContext { cx -> BoundFunction(cx, augmented, value, augmented, arrayOf()) }
withRhinoContext(scriptRuntime) { cx -> BoundFunction(cx, augmented, value, augmented, arrayOf()) }
}
else -> value
}

View File

@@ -309,7 +309,12 @@ class Automator(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
val last = it.last()
when {
isGestureResultCallbackLike(last) -> {
scriptRuntime.automator.gestureAsync(start, duration, toPointsGroup(it.drop(1).dropLast(1)).toTypedArray<IntArray>(), toGestureResultCallback(last))
scriptRuntime.automator.gestureAsync(
start = start,
duration = duration,
points = toPointsGroup(it.drop(1).dropLast(1)).toTypedArray<IntArray>(),
callback = toGestureResultCallback(scriptRuntime, last),
)
}
else -> scriptRuntime.automator.gestureAsync(start, duration, toPointsGroup(it.drop(1)).toTypedArray<IntArray>())
}
@@ -349,7 +354,10 @@ class Automator(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
when {
isGestureResultCallbackLike(last) -> {
it.dropLast(1).forEach { o -> require((o is List<*>)) { "Arguments for automator.gesturesAsync should be of type Array<Any>" } }
scriptRuntime.automator.gesturesAsync(toStrokes(scriptRuntime, it.dropLast(1).toTypedArray<Any?>()), toGestureResultCallback(last))
scriptRuntime.automator.gesturesAsync(
strokes = toStrokes(scriptRuntime, it.dropLast(1).toTypedArray<Any?>()),
callback = toGestureResultCallback(scriptRuntime, last),
)
}
else -> {
it.dropLast(1).forEach { o -> require((o is List<*>)) { "Arguments for automator.gesturesAsync should be of type Array<Any>" } }
@@ -662,16 +670,16 @@ class Automator(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
else -> throw WrappedIllegalArgumentException("Invalid arguments length (${args.size}) for \"args\" of Automator#performAction")
}
private fun toGestureResultCallback(callback: Any?): AccessibilityService.GestureResultCallback? {
private fun toGestureResultCallback(scriptRuntime: ScriptRuntime, callback: Any?): AccessibilityService.GestureResultCallback? {
if (!isGestureResultCallbackLike(callback)) return null
return object : AccessibilityService.GestureResultCallback() {
override fun onCompleted(gestureDescription: GestureDescription) {
when (callback) {
is BaseFunction -> withRhinoContext { cx ->
is BaseFunction -> withRhinoContext(scriptRuntime) { cx ->
callback.call(cx, ImporterTopLevel(cx), callback, arrayOf(true))
}
is NativeObject -> callback.prop("onCompleted")?.let {
if (it is BaseFunction) withRhinoContext { cx ->
if (it is BaseFunction) withRhinoContext(scriptRuntime) { cx ->
it.call(cx, ImporterTopLevel(cx), callback, arrayOf(gestureDescription))
}
}
@@ -680,11 +688,11 @@ class Automator(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
override fun onCancelled(gestureDescription: GestureDescription) {
when (callback) {
is BaseFunction -> withRhinoContext { cx ->
is BaseFunction -> withRhinoContext(scriptRuntime) { cx ->
callback.call(cx, ImporterTopLevel(cx), callback, arrayOf(false))
}
is NativeObject -> callback.prop("onCancelled")?.let {
if (it is BaseFunction) withRhinoContext { cx ->
if (it is BaseFunction) withRhinoContext(scriptRuntime) { cx ->
it.call(cx, ImporterTopLevel(cx), callback, arrayOf(gestureDescription))
}
}

View File

@@ -13,7 +13,7 @@ import org.autojs.autojs.core.inputevent.RootAutomator as CoreRootAutomator
import org.mozilla.javascript.ScriptRuntime as RhinoScriptRuntime
@Suppress("unused")
class RootAutomatorNativeObject(scriptRuntime: ScriptRuntime, waitForReady: Any? = false) : NativeObject() {
class RootAutomatorNativeObject(private val scriptRuntime: ScriptRuntime, waitForReady: Any? = false) : NativeObject() {
private val mRootAutomatorObject: Scriptable = run {
val rootAutomator = when (waitForReady) {
@@ -46,7 +46,7 @@ class RootAutomatorNativeObject(scriptRuntime: ScriptRuntime, waitForReady: Any?
// # 'touchDown', 'touchUp', 'touchMove', 'getDefaultId', 'setDefaultId', 'exit',
// # ]
return when (val o = mRootAutomatorObject.prop(name)) {
is BaseFunction -> withRhinoContext { cx ->
is BaseFunction -> withRhinoContext(scriptRuntime) { cx ->
BoundFunction(cx, mRootAutomatorObject, o, mRootAutomatorObject, arrayOf())
}
else -> super.get(name, start)

View File

@@ -7,15 +7,15 @@ import org.apache.log4j.LogManager
import org.autojs.autojs.annotation.RhinoRuntimeFunctionInterface
import org.autojs.autojs.core.console.ConsoleImpl
import org.autojs.autojs.core.console.ConsoleImpl.Companion.DEFAULT_EXIT_ON_CLOSE_TIMEOUT
import org.autojs.autojs.rhino.extension.AnyExtensions.isJsNullish
import org.autojs.autojs.rhino.extension.AnyExtensions.jsBrief
import org.autojs.autojs.rhino.ArgumentGuards
import org.autojs.autojs.rhino.ArgumentGuards.Companion.component1
import org.autojs.autojs.rhino.ArgumentGuards.Companion.component2
import org.autojs.autojs.rhino.ProxyObject
import org.autojs.autojs.rhino.extension.AnyExtensions.isJsNullish
import org.autojs.autojs.rhino.extension.AnyExtensions.jsBrief
import org.autojs.autojs.rhino.extension.ScriptableExtensions.hasProp
import org.autojs.autojs.rhino.extension.ScriptableExtensions.prop
import org.autojs.autojs.rhino.extension.ScriptableObjectExtensions.inquire
import org.autojs.autojs.rhino.ProxyObject
import org.autojs.autojs.runtime.ScriptRuntime
import org.autojs.autojs.runtime.api.augment.AugmentableProxy
import org.autojs.autojs.runtime.api.augment.s13n.S13n
@@ -50,7 +50,7 @@ import org.mozilla.javascript.ScriptableObject
import org.mozilla.javascript.Undefined
@Suppress("unused", "UNUSED_PARAMETER")
class Console(scriptRuntime: ScriptRuntime) : AugmentableProxy(scriptRuntime) {
class Console(private val scriptRuntime: ScriptRuntime) : AugmentableProxy(scriptRuntime) {
private val mRtConsole = scriptRuntime.console
private val mTopLevelScope = scriptRuntime.topLevelScope
@@ -137,7 +137,7 @@ class Console(scriptRuntime: ScriptRuntime) : AugmentableProxy(scriptRuntime) {
::launch.name to "launchConsole",
)
private fun getStackTrace() = withRhinoContext { cx ->
private fun getStackTrace() = withRhinoContext(scriptRuntime) { cx ->
newNativeObject().also { o ->
val globalErrorObject = mTopLevelScope.prop(NativeError.ERROR_TAG) as ScriptableObject
NativeError.js_captureStackTrace(

View File

@@ -4,6 +4,7 @@ import org.autojs.autojs.annotation.RhinoFunctionBody
import org.autojs.autojs.annotation.RhinoSingletonFunctionInterface
import org.autojs.autojs.runtime.api.augment.Augmentable
import org.autojs.autojs.runtime.api.augment.Invokable
import org.autojs.autojs.util.RhinoUtils.getOrCreateStandardObjects
import org.autojs.autojs.util.RhinoUtils.withRhinoContext
import org.mozilla.javascript.Context
import org.mozilla.javascript.Scriptable
@@ -61,7 +62,7 @@ object Species : Augmentable(), Invokable {
when {
o == null -> "Null"
Undefined.isUndefined(o) -> "Undefined"
else -> when (val obj = withRhinoContext { cx -> Context.javaToJS(o, cx.initStandardObjects()) }) {
else -> when (val obj = withRhinoContext { cx -> Context.javaToJS(o, cx.getOrCreateStandardObjects()) }) {
is Boolean -> "Boolean"
is String -> "String"
is BigInteger -> "BigInt"

View File

@@ -195,7 +195,7 @@ class Http(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
uiMapper = { response ->
// Wrap response into Rhino object on UI thread with Rhino Context.
// zh-CN: 在 UI 线程进入 Rhino Context, 将 Response 包装为 Rhino 对象.
withRhinoContext {
withRhinoContext(scriptRuntime) {
val wrapped = wrapResponse(scriptRuntime, response, prepared.cacheBody, prepared.cacheThreshold)
// Optional: if user provided callback, call it on UI thread.

View File

@@ -9,14 +9,14 @@ import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okio.BufferedSink
import org.autojs.autojs.pio.PFile
import org.autojs.autojs.pio.PFileInterface
import org.autojs.autojs.rhino.extension.AnyExtensions.isJsNullish
import org.autojs.autojs.rhino.extension.AnyExtensions.isJsNumber
import org.autojs.autojs.rhino.extension.AnyExtensions.isJsString
import org.autojs.autojs.rhino.extension.AnyExtensions.jsBrief
import org.autojs.autojs.rhino.extension.AnyExtensions.toRuntimePath
import org.autojs.autojs.rhino.extension.ScriptableExtensions.prop
import org.autojs.autojs.pio.PFile
import org.autojs.autojs.pio.PFileInterface
import org.autojs.autojs.runtime.ScriptRuntime
import org.autojs.autojs.runtime.api.Mime
import org.autojs.autojs.runtime.exception.WrappedIllegalArgumentException
@@ -53,7 +53,7 @@ class RequestBuilderHelper(private val options: NativeObject) {
// require(method is String) { "Property method is required for header options" }
when {
!options.prop(Http.KEY_BODY).isJsNullish() -> {
request.method(method, parseBody())
request.method(method, parseBody(scriptRuntime))
}
!options.prop(Http.KEY_FILES).isJsNullish() -> {
request.method(method, parseMultipart(scriptRuntime))
@@ -64,7 +64,7 @@ class RequestBuilderHelper(private val options: NativeObject) {
}
}
fun parseBody(): RequestBody = when (val body = options.prop(Http.KEY_BODY)) {
fun parseBody(scriptRuntime: ScriptRuntime): RequestBody = when (val body = options.prop(Http.KEY_BODY)) {
is RequestBody -> body
is String -> {
val mediaType = options.prop(Http.KEY_CONTENT_TYPE).takeUnless { it.isJsNullish() }
@@ -77,7 +77,7 @@ class RequestBuilderHelper(private val options: NativeObject) {
}
override fun writeTo(sink: BufferedSink) {
RhinoUtils.withRhinoContext { cx ->
RhinoUtils.withRhinoContext(scriptRuntime) { cx ->
body.call(cx, body, body, arrayOf(sink))
}
}

View File

@@ -2,12 +2,12 @@ package org.autojs.autojs.runtime.api.augment.http
import okhttp3.ResponseBody
import org.autojs.autojs.annotation.RhinoStandardFunctionInterface
import org.autojs.autojs.rhino.extension.AnyExtensions.toRuntimePath
import org.autojs.autojs.pio.PFile
import org.autojs.autojs.rhino.ArgumentGuards
import org.autojs.autojs.rhino.ArgumentGuards.Companion.component1
import org.autojs.autojs.rhino.ArgumentGuards.Companion.component2
import org.autojs.autojs.rhino.extension.AnyExtensions.toRuntimePath
import org.autojs.autojs.rhino.extension.ScriptableExtensions.prop
import org.autojs.autojs.pio.PFile
import org.autojs.autojs.runtime.ScriptRuntime
import org.autojs.autojs.runtime.exception.WrappedIllegalArgumentException
import org.autojs.autojs.util.RhinoUtils
@@ -56,7 +56,7 @@ class ResponseBodyNativeObject(
return super.get(name, start)
}
return when (val o = mResBodyObject.prop(name)) {
is BaseFunction -> withRhinoContext { cx ->
is BaseFunction -> withRhinoContext(scriptRuntime) { cx ->
BoundFunction(cx, mResBodyObject, o, mResBodyObject, arrayOf())
}
else -> super.get(name, start)

View File

@@ -1,6 +1,5 @@
package org.autojs.autojs.runtime.api.augment.jsox
import org.autojs.autojs.annotation.RhinoFunctionBody
import org.autojs.autojs.annotation.RhinoRuntimeFunctionInterface
import org.autojs.autojs.annotation.RhinoRuntimeFunctionWithThisObjInterface
import org.autojs.autojs.rhino.ArgumentGuards
@@ -12,6 +11,7 @@ import org.autojs.autojs.runtime.ScriptRuntime
import org.autojs.autojs.runtime.api.augment.Augmentable
import org.autojs.autojs.runtime.api.augment.util.Util.ensureArrayTypeRhino
import org.autojs.autojs.util.ArrayUtils.unshiftWith
import org.autojs.autojs.util.RhinoUtils.callFunction
import org.autojs.autojs.util.RhinoUtils.coerceArray
import org.autojs.autojs.util.RhinoUtils.coerceFunction
import org.autojs.autojs.util.RhinoUtils.undefined
@@ -158,18 +158,14 @@ class Arrayx(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRunti
@JvmStatic
@RhinoRuntimeFunctionInterface
fun distinctBy(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsLength(args, 2) {
distinctByRhino(it)
}
@JvmStatic
@RhinoFunctionBody
fun distinctByRhino(it: Array<Any?>): NativeArray = withRhinoContext { cx ->
val (arr, selector) = it
coerceArray(arr).distinctBy { ele ->
require(arr is NativeArray)
coerceFunction(selector).call(cx, arr, arr, arrayOf(ele))
}.toNativeArray()
fun distinctBy(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsLength(args, 2) { argList ->
withRhinoContext(scriptRuntime) { cx ->
val (arrRaw, selector) = argList
val arr = coerceArray(arrRaw)
arr.distinctBy { ele ->
callFunction(scriptRuntime, coerceFunction(selector), arr, arr, arrayOf(ele))
}.toNativeArray()
}
}
@JvmStatic
@@ -202,148 +198,116 @@ class Arrayx(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRunti
@JvmStatic
@RhinoRuntimeFunctionInterface
fun sortBy(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsLength(args, 2) {
val (arr, selector) = it
sortByRhino(arr, selector)
}
@JvmStatic
@RhinoFunctionBody
fun sortByRhino(arr: Any?, selector: Any?): NativeArray = withRhinoContext { cx ->
require(arr is NativeArray) { "Argument \"arr\" ${arr.jsBrief()} for Arrayx.sortBy must be a JavaScript Array" }
require(selector is BaseFunction) { "Argument \"selector\" ${selector.jsBrief()} for Arrayx.sortBy must be a JavaScript Function" }
when {
arr.length < 2 -> arr
else -> {
// In-place sorting (zh-CN: 原地排序)
NativeArray.js_sort(cx, arr, arr, arrayOf(toCompareFunctionAsc(selector)))
arr
fun sortBy(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsLength(args, 2) { argList ->
val (arr, selector) = argList
withRhinoContext(scriptRuntime) { cx ->
require(arr is NativeArray) { "Argument \"arr\" ${arr.jsBrief()} for Arrayx.sortBy must be a JavaScript Array" }
require(selector is BaseFunction) { "Argument \"selector\" ${selector.jsBrief()} for Arrayx.sortBy must be a JavaScript Function" }
when {
arr.length < 2 -> arr
else -> {
// In-place sorting (zh-CN: 原地排序)
NativeArray.js_sort(cx, arr, arr, arrayOf(toCompareFunctionAsc(selector)))
arr
}
}
}
}
@JvmStatic
@RhinoRuntimeFunctionInterface
fun sortByDescending(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsLength(args, 2) {
val (arr, selector) = it
sortByDescendingRhino(arr, selector)
}
@JvmStatic
@RhinoFunctionBody
fun sortByDescendingRhino(arr: Any?, selector: Any?): NativeArray = withRhinoContext { cx ->
require(arr is NativeArray) { "Argument \"arr\" ${arr.jsBrief()} for Arrayx.sortByDescending must be a JavaScript Array" }
require(selector is BaseFunction) { "Argument \"selector\" ${selector.jsBrief()} for Arrayx.sortByDescending must be a JavaScript Function" }
when {
arr.length < 2 -> arr
else -> {
// In-place sorting (zh-CN: 原地排序)
NativeArray.js_sort(cx, arr, arr, arrayOf(toCompareFunctionDesc(selector)))
arr
fun sortByDescending(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsLength(args, 2) { argList ->
val (arr, selector) = argList
withRhinoContext(scriptRuntime) { cx ->
require(arr is NativeArray) { "Argument \"arr\" ${arr.jsBrief()} for Arrayx.sortByDescending must be a JavaScript Array" }
require(selector is BaseFunction) { "Argument \"selector\" ${selector.jsBrief()} for Arrayx.sortByDescending must be a JavaScript Function" }
when {
arr.length < 2 -> arr
else -> {
// In-place sorting (zh-CN: 原地排序)
NativeArray.js_sort(cx, arr, arr, arrayOf(toCompareFunctionDesc(selector)))
arr
}
}
}
}
@JvmStatic
@RhinoRuntimeFunctionInterface
fun sortDescending(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsOnlyOne(args) {
sortDescendingRhino(it)
}
@JvmStatic
@RhinoFunctionBody
fun sortDescendingRhino(arr: Any?): NativeArray = withRhinoContext { cx ->
require(arr is NativeArray) { "Argument \"arr\" ${arr.jsBrief()} for Arrayx.sortDescending must be a JavaScript Array" }
NativeArray.js_sort(cx, arr, arr, arrayOf(toCompareFunctionDesc()))
arr
fun sortDescending(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsOnlyOne(args) { o ->
withRhinoContext(scriptRuntime) { cx ->
require(o is NativeArray) { "Argument \"arr\" ${o.jsBrief()} for Arrayx.sortDescending must be a JavaScript Array" }
NativeArray.js_sort(cx, o, o, arrayOf(toCompareFunctionDesc()))
o
}
}
@JvmStatic
@RhinoRuntimeFunctionInterface
fun sorted(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsOnlyOne(args) {
sortedRhino(it)
}
@JvmStatic
@RhinoFunctionBody
fun sortedRhino(it: Any?): NativeArray = withRhinoContext { cx ->
require(it is NativeArray) { "Argument \"arr\" ${it.jsBrief()} for Arrayx.sorted must be a JavaScript Array" }
val copied = it.slice(it.indices).toNativeArray()
NativeArray.js_sort(cx, it, copied, arrayOf(toCompareFunctionAsc()))
copied
fun sorted(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsOnlyOne(args) { o ->
withRhinoContext(scriptRuntime) { cx ->
require(o is NativeArray) { "Argument \"arr\" ${o.jsBrief()} for Arrayx.sorted must be a JavaScript Array" }
val copied = o.slice(o.indices).toNativeArray()
NativeArray.js_sort(cx, o, copied, arrayOf(toCompareFunctionAsc()))
copied
}
}
@JvmStatic
@RhinoRuntimeFunctionInterface
fun sortedDescending(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsOnlyOne(args) {
sortedDescendingRhino(it)
}
@JvmStatic
@RhinoFunctionBody
fun sortedDescendingRhino(it: Any?): NativeArray = withRhinoContext { cx ->
require(it is NativeArray) { "Argument \"arr\" ${it.jsBrief()} for Arrayx.sortedDescending must be a JavaScript Array" }
val copied = it.slice(it.indices).toNativeArray()
NativeArray.js_sort(cx, it, copied, arrayOf(toCompareFunctionDesc()))
copied
fun sortedDescending(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsOnlyOne(args) { o ->
withRhinoContext(scriptRuntime) { cx ->
require(o is NativeArray) { "Argument \"arr\" ${o.jsBrief()} for Arrayx.sortedDescending must be a JavaScript Array" }
val copied = o.slice(o.indices).toNativeArray()
NativeArray.js_sort(cx, o, copied, arrayOf(toCompareFunctionDesc()))
copied
}
}
@JvmStatic
@RhinoRuntimeFunctionInterface
fun sortedBy(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsLength(args, 2) {
val (arr, selector) = it
sortedByRhino(arr, selector)
}
@JvmStatic
@RhinoFunctionBody
fun sortedByRhino(arr: Any?, selector: Any?): NativeArray = withRhinoContext { cx ->
require(arr is NativeArray) { "Argument \"arr\" ${arr.jsBrief()} for Arrayx.sortedBy must be a JavaScript Array" }
require(selector is BaseFunction) { "Argument \"selector\" ${selector.jsBrief()} for Arrayx.sortedBy must be a JavaScript Function" }
val copied = arr.slice(arr.indices).toNativeArray()
when {
arr.length < 2 -> copied
else -> {
NativeArray.js_sort(cx, arr, copied, arrayOf(toCompareFunctionAsc(selector)))
copied
fun sortedBy(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsLength(args, 2) { argList ->
val (arr, selector) = argList
withRhinoContext(scriptRuntime) { cx ->
require(arr is NativeArray) { "Argument \"arr\" ${arr.jsBrief()} for Arrayx.sortedBy must be a JavaScript Array" }
require(selector is BaseFunction) { "Argument \"selector\" ${selector.jsBrief()} for Arrayx.sortedBy must be a JavaScript Function" }
val copied = arr.slice(arr.indices).toNativeArray()
when {
arr.length < 2 -> copied
else -> {
NativeArray.js_sort(cx, arr, copied, arrayOf(toCompareFunctionAsc(selector)))
copied
}
}
}
}
@JvmStatic
@RhinoRuntimeFunctionInterface
fun sortedByDescending(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsLength(args, 2) {
val (arr, selector) = it
sortedByDescendingRhino(arr, selector)
}
@JvmStatic
@RhinoFunctionBody
fun sortedByDescendingRhino(arr: Any?, selector: Any?): NativeArray = withRhinoContext { cx ->
require(arr is NativeArray) { "Argument \"arr\" ${arr.jsBrief()} for Arrayx.sortedByDescending must be a JavaScript Array" }
require(selector is BaseFunction) { "Argument \"selector\" ${selector.jsBrief()} for Arrayx.sortedByDescending must be a JavaScript Function" }
val copied = arr.slice(arr.indices).toNativeArray()
when {
arr.length < 2 -> copied
else -> {
NativeArray.js_sort(cx, arr, copied, arrayOf(toCompareFunctionDesc(selector)))
copied
fun sortedByDescending(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsLength(args, 2) { argList ->
val (arr, selector) = argList
withRhinoContext(scriptRuntime) { cx ->
require(arr is NativeArray) { "Argument \"arr\" ${arr.jsBrief()} for Arrayx.sortedByDescending must be a JavaScript Array" }
require(selector is BaseFunction) { "Argument \"selector\" ${selector.jsBrief()} for Arrayx.sortedByDescending must be a JavaScript Function" }
val copied = arr.slice(arr.indices).toNativeArray()
when {
arr.length < 2 -> copied
else -> {
NativeArray.js_sort(cx, arr, copied, arrayOf(toCompareFunctionDesc(selector)))
copied
}
}
}
}
@JvmStatic
@RhinoRuntimeFunctionInterface
fun shuffle(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsOnlyOne(args) {
shuffleRhino(it)
}
@JvmStatic
@RhinoFunctionBody
fun shuffleRhino(it: Any?): NativeArray = withRhinoContext { cx ->
require(it is NativeArray) { "Argument \"arr\" ${it.jsBrief()} for Arrayx.shuffle must be a JavaScript Array" }
NativeArray.js_sort(cx, it, it, arrayOf(toCompareFunctionRandom()))
it
fun shuffle(scriptRuntime: ScriptRuntime, args: Array<out Any?>): NativeArray = ensureArgumentsOnlyOne(args) { o ->
withRhinoContext(scriptRuntime) { cx ->
require(o is NativeArray) { "Argument \"arr\" ${o.jsBrief()} for Arrayx.shuffle must be a JavaScript Array" }
NativeArray.js_sort(cx, o, o, arrayOf(toCompareFunctionRandom()))
o
}
}
private fun toCompareFunctionAsc(selector: BaseFunction) = object : BaseFunction() {

View File

@@ -56,7 +56,7 @@ class Selector(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRun
// @Hint by SuperMonster003 on Jul 25, 2024.
// ! For scope binding.
// ! zh-CN: 用于绑定作用域.
withRhinoContext { cx ->
withRhinoContext(scriptRuntime) { cx ->
global.defineProp(methodName, newBaseFunction(null, { argList ->
val methodKey = coerceString(argList[0])
newBaseFunction(methodKey, { arguments ->

View File

@@ -232,7 +232,7 @@ class UI(private val scriptRuntime: ScriptRuntime) : AugmentableProxy(scriptRunt
val widgets = scriptRuntime.ui.widgets
if (widgets.contains(viewName)) {
val ctor = widgets.prop(viewName) as BaseFunction
val widget = withRhinoContext { cx ->
val widget = withRhinoContext(scriptRuntime) { cx ->
ctor.construct(cx, scriptRuntime.topLevelScope, arrayOf())
} as ScriptableObject
val f = widget.prop("renderInternal") as BaseFunction
@@ -881,7 +881,7 @@ class UI(private val scriptRuntime: ScriptRuntime) : AugmentableProxy(scriptRunt
}
}
}, NOT_CONSTRUCTABLE)
withRhinoContext { cx ->
withRhinoContext(scriptRuntime) { cx ->
arrayObserveFunc.call(cx, global, globalArray, arrayOf(dataSource, handlerFunc))
}
}
@@ -899,7 +899,7 @@ class UI(private val scriptRuntime: ScriptRuntime) : AugmentableProxy(scriptRunt
isAutoJsUiMode -> {
callFunction(scriptRuntime, action, scriptRuntime.topLevelScope, arrayOf())
}
else -> withRhinoContext { cx ->
else -> withRhinoContext(scriptRuntime) { cx ->
val scope = scriptRuntime.topLevelScope
val exitIfErrorFunc = scope.prop("__exitIfError__") as BaseFunction
exitIfErrorFunc.call(cx, scope, scope, arrayOf(newBaseFunction("action", {

View File

@@ -35,7 +35,7 @@ class Web(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
@JvmStatic
@RhinoFunctionBody
fun newInjectableWebViewRhinoWithRuntime(scriptRuntime: ScriptRuntime, vararg args: Any?): InjectableWebView = withRhinoContext { cx ->
fun newInjectableWebViewRhinoWithRuntime(scriptRuntime: ScriptRuntime, vararg args: Any?): InjectableWebView = withRhinoContext(scriptRuntime) { cx ->
when (args.size) {
2 -> {
val (androidContext, url) = args
@@ -60,7 +60,7 @@ class Web(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
@JvmStatic
@RhinoRuntimeFunctionInterface
fun newInjectableWebClient(scriptRuntime: ScriptRuntime, args: Array<out Any?>): InjectableWebClient = ensureArgumentsIsEmpty(args) {
withRhinoContext { cx -> InjectableWebClient(cx, scriptRuntime.topLevelScope) }
withRhinoContext(scriptRuntime) { cx -> InjectableWebClient(cx, scriptRuntime.topLevelScope) }
}
@JvmStatic

View File

@@ -9,6 +9,7 @@ import android.os.Parcelable
import org.autojs.autojs.AutoJs
import org.autojs.autojs.annotation.RhinoStandardFunctionInterface
import org.autojs.autojs.core.automator.UiObjectCollection
import org.autojs.autojs.engine.RhinoJavaScriptEngine
import org.autojs.autojs.rhino.TopLevelScope
import org.autojs.autojs.rhino.extension.AnyExtensions.isJsNullish
import org.autojs.autojs.rhino.extension.AnyExtensions.jsBrief
@@ -95,13 +96,28 @@ object RhinoUtils {
private val TAG = RhinoUtils::class.java.simpleName
private val STANDARD_OBJECTS_KEY = Any()
@JvmStatic
val UNDEFINED = Undefined.instance as Undefined
@JvmStatic
fun Context.getOrCreateStandardObjects(): ScriptableObject {
// Cache standard objects per Context to avoid repeated initStandardObjects().
// zh-CN: 按 Context 缓存标准对象, 避免重复调用 initStandardObjects().
val cached = this.getThreadLocal(STANDARD_OBJECTS_KEY) as? ScriptableObject
if (cached != null) return cached
return this.initStandardObjects().also { created ->
this.putThreadLocal(STANDARD_OBJECTS_KEY, created)
}
}
@JvmStatic
val standardObjects: ScriptableObject by lazy {
// Create a global singleton standard objects as a low-fidelity fallback.
// zh-CN: 创建全局单例标准对象, 用作低保真兜底.
try {
Context.enter().initStandardObjects()
Context.enter().getOrCreateStandardObjects()
} finally {
Context.exit()
}
@@ -125,33 +141,25 @@ object RhinoUtils {
}
@JvmStatic
fun callGlobalFunction(name: String, paramsToFunction: Array<Any?>): Any? {
return callGlobalFunction(null, name, paramsToFunction)
}
fun callToStringFunction(o: Scriptable): String =
Context.toString(callFunction(o, "toString", arrayOf()))
@JvmStatic
fun callGlobalFunction(scriptRuntime: ScriptRuntime?, name: String, paramsToFunction: Array<Any?>) = withRhinoContext { cx ->
val topLevel = ImporterTopLevel(cx)
callFunction(scriptRuntime, topLevel, name, paramsToFunction)
}
@JvmStatic
fun callToStringFunction(o: Scriptable): String {
return callToStringFunction(null, o)
}
@JvmStatic
fun callToStringFunction(scriptRuntime: ScriptRuntime?, o: Scriptable): String = Context.toString(callFunction(scriptRuntime, o, "toString", arrayOf()))
fun callToStringFunction(scriptRuntime: ScriptRuntime, o: Scriptable): String =
Context.toString(callFunction(scriptRuntime, o, "toString", arrayOf()))
@JvmStatic
@Throws(NoSuchMethodException::class, InvocationTargetException::class, IllegalAccessException::class)
fun callFunction(obj: Scriptable, name: String, paramsToFunction: Array<Any?>): Any? {
return callFunction(null, obj, name, paramsToFunction)
val property = obj.prop(name)
if (property == NOT_FOUND) throw IllegalArgumentException("Property $name not found on $obj")
if (property !is BaseFunction) throw IllegalArgumentException("Property $name on $obj must be a BaseFunction instead of ${property.jsBrief()}")
return callFunction(property, obj, obj, paramsToFunction)
}
@JvmStatic
@Throws(NoSuchMethodException::class, InvocationTargetException::class, IllegalAccessException::class)
fun callFunction(scriptRuntime: ScriptRuntime?, obj: Scriptable, name: String, paramsToFunction: Array<Any?>): Any? {
fun callFunction(scriptRuntime: ScriptRuntime, obj: Scriptable, name: String, paramsToFunction: Array<Any?>): Any? {
val property = obj.prop(name)
if (property == NOT_FOUND) throw IllegalArgumentException("Property $name not found on $obj")
if (property !is BaseFunction) throw IllegalArgumentException("Property $name on $obj must be a BaseFunction instead of ${property.jsBrief()}")
@@ -160,38 +168,40 @@ object RhinoUtils {
@JvmStatic
@Throws(NoSuchMethodException::class, InvocationTargetException::class, IllegalAccessException::class)
fun callFunction(func: BaseFunction, paramsToFunction: Array<Any?>): Any? {
return callFunction(null, func, paramsToFunction)
}
fun callFunction(func: BaseFunction, paramsToFunction: Array<Any?>): Any? =
callFunction(func, null, paramsToFunction)
@JvmStatic
@Throws(NoSuchMethodException::class, InvocationTargetException::class, IllegalAccessException::class)
fun callFunction(scriptRuntime: ScriptRuntime?, func: BaseFunction, paramsToFunction: Array<Any?>): Any? {
return callFunction(scriptRuntime, func, null, paramsToFunction)
}
fun callFunction(scriptRuntime: ScriptRuntime, func: BaseFunction, paramsToFunction: Array<Any?>): Any? =
callFunction(scriptRuntime, func, null, paramsToFunction)
@JvmStatic
@Throws(NoSuchMethodException::class, InvocationTargetException::class, IllegalAccessException::class)
fun callFunction(func: BaseFunction, scope: Scriptable?, args: Array<Any?>): Any? {
return callFunction(null, func, scope, args)
}
fun callFunction(func: BaseFunction, scope: Scriptable?, args: Array<Any?>): Any? =
callFunction(func, scope, scope, args)
@JvmStatic
@Throws(NoSuchMethodException::class, InvocationTargetException::class, IllegalAccessException::class)
fun callFunction(scriptRuntime: ScriptRuntime?, func: BaseFunction, scope: Scriptable?, args: Array<Any?>): Any? {
return callFunction(scriptRuntime, func, scope, scope, args)
}
fun callFunction(scriptRuntime: ScriptRuntime, func: BaseFunction, scope: Scriptable?, args: Array<Any?>): Any? =
callFunction(scriptRuntime, func, scope, scope, args)
@JvmStatic
@Throws(NoSuchMethodException::class, InvocationTargetException::class, IllegalAccessException::class)
fun callFunction(func: BaseFunction, scope: Scriptable?, thisObj: Scriptable?, args: Array<Any?>): Any? {
return callFunction(null, func, scope, thisObj, args)
}
fun callFunction(func: BaseFunction, scope: Scriptable?, thisObj: Scriptable?, args: Array<Any?>): Any? =
callFunction(null, func, scope, thisObj, args)
@JvmStatic
fun callFunction(scriptRuntime: ScriptRuntime?, func: BaseFunction, scope: Scriptable?, thisObj: Scriptable?, args: Array<Any?>): Any? = withRhinoContext { cx ->
fun callFunction(scriptRuntime: ScriptRuntime?, func: BaseFunction, scope: Scriptable?, thisObj: Scriptable?, args: Array<Any?>): Any? = withRhinoContext(scriptRuntime) { cx ->
try {
val niceScope = scope ?: cx.initStandardObjects()
// Prefer an existing scope to avoid repeatedly creating standard objects.
// zh-CN: 优先使用已有的作用域, 避免反复创建标准对象.
val niceScope = when {
scope != null -> scope
func.parentScope != null -> func.parentScope
scriptRuntime != null -> scriptRuntime.topLevelScope
else -> ImporterTopLevel(cx)
}
when {
RhinoScriptRuntime.hasTopCall(cx) -> func.call(cx, niceScope, thisObj, args)
else -> RhinoScriptRuntime.doTopCall(func, cx, niceScope, thisObj, args, false)
@@ -369,28 +379,23 @@ object RhinoUtils {
}
@JvmStatic
fun encodeURI(str: String): String = encodeURI(null, str)
fun encodeURI(scriptRuntime: ScriptRuntime, str: String): String =
Context.toString(callGlobalFunction(scriptRuntime, "encodeURI", arrayOf(str)))
@JvmStatic
fun encodeURI(scriptRuntime: ScriptRuntime?, str: String): String = Context.toString(callGlobalFunction(scriptRuntime, "encodeURI", arrayOf(str)))
fun parseInt(scriptRuntime: ScriptRuntime, str: String?): Double =
Context.toNumber(callGlobalFunction(scriptRuntime, "parseInt", arrayOf(str)))
@JvmStatic
fun parseInt(str: String?): Double = parseInt(null, str)
fun parseInt(scriptRuntime: ScriptRuntime, str: String?, radix: Number): Double =
Context.toNumber(callGlobalFunction(scriptRuntime, "parseInt", arrayOf(str, radix.toDouble())))
@JvmStatic
fun parseInt(scriptRuntime: ScriptRuntime?, str: String?): Double = Context.toNumber(callGlobalFunction(scriptRuntime, "parseInt", arrayOf(str)))
fun parseFloat(scriptRuntime: ScriptRuntime, str: String?): Double =
Context.toNumber(callGlobalFunction(scriptRuntime, "parseFloat", arrayOf(str)))
@JvmStatic
fun parseInt(str: String?, radix: Number): Double = parseInt(null, str, radix)
@JvmStatic
fun parseInt(scriptRuntime: ScriptRuntime?, str: String?, radix: Number): Double = Context.toNumber(callGlobalFunction(scriptRuntime, "parseInt", arrayOf(str, radix.toDouble())))
@JvmStatic
fun parseFloat(str: String?): Double = parseFloat(null, str)
@JvmStatic
fun parseFloat(scriptRuntime: ScriptRuntime?, str: String?): Double = Context.toNumber(callGlobalFunction(scriptRuntime, "parseFloat", arrayOf(str)))
private fun callGlobalFunction(scriptRuntime: ScriptRuntime, name: String, paramsToFunction: Array<Any?>) =
callFunction(scriptRuntime, scriptRuntime.topLevelScope, name, paramsToFunction)
@JvmStatic
fun ensureNativeArrayLength(o: NativeArray, length: Int, desc: String) {
@@ -417,7 +422,8 @@ object RhinoUtils {
@JvmStatic
fun runJavaScript(code: String): Any? = withRhinoContext { cx ->
cx.evaluateString(standardObjects, code, null, 1, null)
val scope = cx.getOrCreateStandardObjects()
cx.evaluateString(scope, code, null, 1, null)
}
@JvmStatic
@@ -466,13 +472,7 @@ object RhinoUtils {
@JvmStatic
@JvmOverloads
fun coerceRunnable(o: Any?, def: Runnable? = null): Runnable {
return coerceRunnable(null, o, def)
}
@JvmStatic
@JvmOverloads
fun coerceRunnable(scriptRuntime: ScriptRuntime?, o: Any?, def: Runnable? = null): Runnable = when {
fun coerceRunnable(scriptRuntime: ScriptRuntime, o: Any?, def: Runnable? = null): Runnable = when {
!o.isJsNullish() -> when (o) {
is BaseFunction -> Runnable { callFunction(scriptRuntime, o, arrayOf()) }
is Runnable -> o
@@ -789,16 +789,32 @@ object RhinoUtils {
RhinoScriptRuntime.evalSpecial(cx, global, global, arrayOf(s), "eval code", 1)
}
fun <R> withRhinoContext(function: (context: Context) -> R): R {
@JvmStatic
@JvmOverloads
fun <R> withRhinoContext(scriptRuntime: ScriptRuntime? = null, function: (context: Context) -> R): R {
var cxRhino: Context? = null
return try {
val cx = Context.getCurrentContext()
?: Context.enter().also {
it.initStandardObjects()
cxRhino = it
}
cx.languageVersion = Context.VERSION_ES6
cx.isInterpretedMode = true
?: when (scriptRuntime) {
null -> {
// This fallback Context.enter() does NOT guarantee WrapFactory from RhinoJavaScriptEngine.
// zh-CN: 这个兜底的 Context.enter() 无法保证使用 RhinoJavaScriptEngine 的 WrapFactory.
Context.enter()
}
else -> {
// Enter Rhino Context via engine's ContextFactory to keep WrapFactory and AutoJsContext bindings effective.
// zh-CN: 通过引擎的 ContextFactory 进入 Rhino Context, 以保持 WrapFactory 和 AutoJsContext 绑定生效.
val engine = scriptRuntime.engines.myEngine()
require(engine is RhinoJavaScriptEngine) {
"Current engine ${engine.javaClass.name} is not a RhinoJavaScriptEngine"
}
engine.enterContext()
}
}.apply {
languageVersion = Context.VERSION_ES6
isInterpretedMode = true
}.also { cxRhino = it }
function.invoke(cx)
} finally {
cxRhino?.let { Context.exit() }
@@ -870,14 +886,6 @@ object RhinoUtils {
@JvmStatic
fun initNativeObjectPrototype(o: NativeObject): NativeObject {
// val cx = ContextWrapper.getCurrentContext()
// try {
// val tmpScope: Scriptable = cx.initStandardObjects()
// NativeObject.init(tmpScope, false)
// o.prototype = tmpScope.get("Object", tmpScope) as Scriptable
// } finally {
// Context.exit()
// }
return o.apply { prototype = objectPrototype }
}