优化
This commit is contained in:
@@ -32,4 +32,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
api(fileTree(dir: 'lib', include: ['rhino-1.7.13-2.jar']))
|
||||
|
||||
def coroutines_version = '1.7.3'
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
|
||||
}
|
||||
@@ -51,6 +51,10 @@ abstract class AbstractScriptEngine(val bindings: Bindings? = null) : ScriptEngi
|
||||
return getBindings(100)?.get(key)
|
||||
}
|
||||
|
||||
override suspend fun evalSuspend(script: String, scope: Scriptable): Any? {
|
||||
return this.evalSuspend(StringReader(script), scope)
|
||||
}
|
||||
|
||||
override fun eval(script: String, scope: Scriptable): Any? {
|
||||
return this.eval(StringReader(script), scope)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ abstract class CompiledScript {
|
||||
@Throws(ScriptException::class)
|
||||
abstract fun eval(scope: Scriptable): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
abstract suspend fun evalSuspend(scope: Scriptable): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(bindings: Bindings?): Any? {
|
||||
var ctxt = getEngine().context
|
||||
|
||||
@@ -14,6 +14,12 @@ interface ScriptEngine {
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(reader: Reader, scope: Scriptable): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
suspend fun evalSuspend(reader: Reader, scope: Scriptable): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
suspend fun evalSuspend(script: String, scope: Scriptable): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(script: String, scope: Scriptable): Any?
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.script.rhino
|
||||
|
||||
import kotlinx.coroutines.ThreadContextElement
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class ContextElement : ThreadContextElement<Any?> {
|
||||
|
||||
companion object Key : CoroutineContext.Key<ContextElement>
|
||||
|
||||
override val key: CoroutineContext.Key<ContextElement>
|
||||
get() = Key
|
||||
|
||||
private val contextHelper: Any? = VMBridgeReflect.contextLocal.get()
|
||||
|
||||
override fun updateThreadContext(context: CoroutineContext): Any? {
|
||||
val oldState = VMBridgeReflect.contextLocal.get()
|
||||
VMBridgeReflect.contextLocal.set(contextHelper)
|
||||
return oldState
|
||||
}
|
||||
|
||||
override fun restoreThreadContext(context: CoroutineContext, oldState: Any?) {
|
||||
VMBridgeReflect.contextLocal.set(oldState)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,11 @@ import com.script.CompiledScript
|
||||
import com.script.ScriptContext
|
||||
import com.script.ScriptEngine
|
||||
import com.script.ScriptException
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.mozilla.javascript.*
|
||||
import java.io.IOException
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
|
||||
|
||||
/**
|
||||
* Represents compiled JavaScript code.
|
||||
@@ -91,4 +95,48 @@ internal class RhinoCompiledScript(
|
||||
return result
|
||||
}
|
||||
|
||||
override suspend fun evalSuspend(scope: Scriptable): Any? {
|
||||
val cx = Context.enter()
|
||||
var ret: Any?
|
||||
withContext(ContextElement()) {
|
||||
try {
|
||||
try {
|
||||
ret = cx.executeScriptWithContinuations(script, scope)
|
||||
} catch (e: ContinuationPending) {
|
||||
var pending = e
|
||||
while (true) {
|
||||
try {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val suspendFunction =
|
||||
pending.applicationState as Function1<Continuation<Any?>, Any?>
|
||||
val functionResult = suspendCoroutineUninterceptedOrReturn { cout ->
|
||||
suspendFunction.invoke(cout)
|
||||
}
|
||||
val continuation = pending.continuation
|
||||
ret = cx.resumeContinuation(continuation, scope, functionResult)
|
||||
break
|
||||
} catch (e: ContinuationPending) {
|
||||
pending = e
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (re: RhinoException) {
|
||||
val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
|
||||
val msg: String = if (re is JavaScriptException) {
|
||||
re.value.toString()
|
||||
} else {
|
||||
re.toString()
|
||||
}
|
||||
val se = ScriptException(msg, re.sourceName(), line)
|
||||
se.initCause(re)
|
||||
throw se
|
||||
} catch (var14: IOException) {
|
||||
throw ScriptException(var14)
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
}
|
||||
return engine.unwrapReturnValue(ret)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
package com.script.rhino
|
||||
|
||||
import com.script.*
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.mozilla.javascript.*
|
||||
import org.mozilla.javascript.Function
|
||||
import java.io.IOException
|
||||
@@ -32,6 +33,8 @@ import java.io.Reader
|
||||
import java.io.StringReader
|
||||
import java.lang.reflect.Method
|
||||
import java.security.*
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
|
||||
|
||||
/**
|
||||
* Implementation of `ScriptEngine` using the Mozilla Rhino
|
||||
@@ -80,6 +83,54 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
|
||||
return unwrapReturnValue(ret)
|
||||
}
|
||||
|
||||
@Throws(ContinuationPending::class)
|
||||
override suspend fun evalSuspend(reader: Reader, scope: Scriptable): Any? {
|
||||
val cx = Context.enter()
|
||||
var ret: Any?
|
||||
withContext(ContextElement()) {
|
||||
try {
|
||||
var filename = this@RhinoScriptEngine["javax.script.filename"] as? String
|
||||
filename = filename ?: "<Unknown source>"
|
||||
val script = cx.compileReader(reader, filename, 1, null)
|
||||
try {
|
||||
ret = cx.executeScriptWithContinuations(script, scope)
|
||||
} catch (e: ContinuationPending) {
|
||||
var pending = e
|
||||
while (true) {
|
||||
try {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val suspendFunction =
|
||||
pending.applicationState as Function1<Continuation<Any?>, Any?>
|
||||
val functionResult = suspendCoroutineUninterceptedOrReturn { cout ->
|
||||
suspendFunction.invoke(cout)
|
||||
}
|
||||
val continuation = pending.continuation
|
||||
ret = cx.resumeContinuation(continuation, scope, functionResult)
|
||||
break
|
||||
} catch (e: ContinuationPending) {
|
||||
pending = e
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (re: RhinoException) {
|
||||
val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
|
||||
val msg: String = if (re is JavaScriptException) {
|
||||
re.value.toString()
|
||||
} else {
|
||||
re.toString()
|
||||
}
|
||||
val se = ScriptException(msg, re.sourceName(), line)
|
||||
se.initCause(re)
|
||||
throw se
|
||||
} catch (var14: IOException) {
|
||||
throw ScriptException(var14)
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
}
|
||||
return unwrapReturnValue(ret)
|
||||
}
|
||||
|
||||
override fun createBindings(): Bindings {
|
||||
return SimpleBindings()
|
||||
}
|
||||
@@ -227,7 +278,7 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
|
||||
callable: Callable,
|
||||
cx: Context,
|
||||
scope: Scriptable,
|
||||
thisObj: Scriptable,
|
||||
thisObj: Scriptable?,
|
||||
args: Array<Any>
|
||||
): Any? {
|
||||
var accContext: AccessControlContext? = null
|
||||
@@ -253,7 +304,7 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
|
||||
callable: Callable,
|
||||
cx: Context,
|
||||
scope: Scriptable,
|
||||
thisObj: Scriptable,
|
||||
thisObj: Scriptable?,
|
||||
args: Array<Any>
|
||||
): Any? {
|
||||
return super.doTopCall(callable, cx, scope, thisObj, args)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.script.rhino
|
||||
|
||||
import org.mozilla.javascript.VMBridge
|
||||
|
||||
object VMBridgeReflect {
|
||||
|
||||
val instance: VMBridge by lazy {
|
||||
VMBridge::class.java.getDeclaredField("instance").apply {
|
||||
isAccessible = true
|
||||
}.get(null) as VMBridge
|
||||
}
|
||||
|
||||
val contextLocal: ThreadLocal<Any> by lazy {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
instance::class.java.getDeclaredField("contextLocal").apply {
|
||||
isAccessible = true
|
||||
}.get(null) as ThreadLocal<Any>
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user