|
@@ -17,6 +17,32 @@ val rhinoContext: RhinoContext
|
|
|
val rhinoContextOrNull: RhinoContext?
|
|
val rhinoContextOrNull: RhinoContext?
|
|
|
get() = Context.getCurrentContext() as? RhinoContext
|
|
get() = Context.getCurrentContext() as? RhinoContext
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 进入脚本上下文。所有需要 [RhinoContext] 的入口都必须走这里,不要直接写
|
|
|
|
|
+ * `Context.enter() as RhinoContext`。
|
|
|
|
|
+ *
|
|
|
|
|
+ * 原因:`Context.enter()` 取的是 Rhino 的**全局** [org.mozilla.javascript.ContextFactory],
|
|
|
|
|
+ * 而生产 [RhinoContext] 的自定义工厂只在 [RhinoScriptEngine] 的 object init 中通过
|
|
|
|
|
+ * `ContextFactory.initGlobal` 安装。在安装完成前调用会拿到普通
|
|
|
|
|
+ * `org.mozilla.javascript.Context`:强转会抛 ClassCastException,且这个 Context 不会
|
|
|
|
|
+ * 被 `Context.exit()` 释放,会把当前线程永久留在普通 Context 上——此后该线程上的任何
|
|
|
|
|
+ * 脚本执行都会失败(书源搜索/换源/正文解析等)。
|
|
|
|
|
+ */
|
|
|
|
|
+fun enterRhinoContext(): RhinoContext {
|
|
|
|
|
+ @Suppress("UNUSED_EXPRESSION")
|
|
|
|
|
+ RhinoScriptEngine
|
|
|
|
|
+ val cx = Context.enter()
|
|
|
|
|
+ if (cx is RhinoContext) {
|
|
|
|
|
+ return cx
|
|
|
|
|
+ }
|
|
|
|
|
+ // 无论失败原因是什么都要撤销本次 enter,否则会把非 RhinoContext 留在线程上。
|
|
|
|
|
+ Context.exit()
|
|
|
|
|
+ error(
|
|
|
|
|
+ "无法进入 Rhino 上下文: Context.enter() 返回 ${cx.javaClass.name}," +
|
|
|
|
|
+ "当前线程已被非 RhinoContext 占用或 Rhino 全局工厂尚未安装"
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
|
|
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
|
|
|
@OptIn(ExperimentalContracts::class)
|
|
@OptIn(ExperimentalContracts::class)
|
|
|
inline fun <T> suspendContinuation(crossinline block: suspend CoroutineScope.() -> T): T {
|
|
inline fun <T> suspendContinuation(crossinline block: suspend CoroutineScope.() -> T): T {
|
|
@@ -40,9 +66,7 @@ inline fun <T> suspendContinuation(crossinline block: suspend CoroutineScope.()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
inline fun <T> runScriptWithContext(context: CoroutineContext, block: () -> T): T {
|
|
inline fun <T> runScriptWithContext(context: CoroutineContext, block: () -> T): T {
|
|
|
- @Suppress("UNUSED_EXPRESSION")
|
|
|
|
|
- RhinoScriptEngine
|
|
|
|
|
- val rhinoContext = Context.enter() as RhinoContext
|
|
|
|
|
|
|
+ val rhinoContext = enterRhinoContext()
|
|
|
val previousCoroutineContext = rhinoContext.coroutineContext
|
|
val previousCoroutineContext = rhinoContext.coroutineContext
|
|
|
rhinoContext.coroutineContext = context.minusKey(ContinuationInterceptor)
|
|
rhinoContext.coroutineContext = context.minusKey(ContinuationInterceptor)
|
|
|
try {
|
|
try {
|
|
@@ -54,7 +78,7 @@ inline fun <T> runScriptWithContext(context: CoroutineContext, block: () -> T):
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
suspend inline fun <T> runScriptWithContext(block: () -> T): T {
|
|
suspend inline fun <T> runScriptWithContext(block: () -> T): T {
|
|
|
- val rhinoContext = Context.enter() as RhinoContext
|
|
|
|
|
|
|
+ val rhinoContext = enterRhinoContext()
|
|
|
val previousCoroutineContext = rhinoContext.coroutineContext
|
|
val previousCoroutineContext = rhinoContext.coroutineContext
|
|
|
rhinoContext.coroutineContext = currentCoroutineContext().minusKey(ContinuationInterceptor)
|
|
rhinoContext.coroutineContext = currentCoroutineContext().minusKey(ContinuationInterceptor)
|
|
|
try {
|
|
try {
|